FloatingBox.vue 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. <template>
  2. <div
  3. ref="box"
  4. :class="{
  5. 'floating-box': true,
  6. column
  7. }"
  8. :id="id"
  9. v-if="persist || shown"
  10. :style="{
  11. width: width + 'px',
  12. height: height + 'px',
  13. top: top + 'px',
  14. left: left + 'px'
  15. }"
  16. @mousedown.left="onResizeBox"
  17. >
  18. <div class="box-header item-draggable" @mousedown.left="onDragBox">
  19. <span class="drag material-icons">drag_indicator</span>
  20. <span v-if="title" class="box-title">{{ title }}</span>
  21. <span
  22. v-if="!persist"
  23. class="delete material-icons"
  24. @click="toggleBox()"
  25. >highlight_off</span
  26. >
  27. </div>
  28. <div class="box-body">
  29. <slot name="body"></slot>
  30. </div>
  31. </div>
  32. </template>
  33. <script>
  34. export default {
  35. props: {
  36. id: { type: String, default: null },
  37. column: { type: Boolean, default: true },
  38. title: { type: String, default: null },
  39. persist: { type: Boolean, default: false }
  40. },
  41. data() {
  42. return {
  43. width: 200,
  44. height: 200,
  45. top: 0,
  46. left: 0,
  47. shown: false,
  48. pos1: 0,
  49. pos2: 0,
  50. pos3: 0,
  51. pos4: 0
  52. };
  53. },
  54. mounted() {
  55. if (this.id !== null && localStorage[`box:${this.id}`]) {
  56. const json = JSON.parse(localStorage.getItem(`box:${this.id}`));
  57. this.height = json.height;
  58. this.width = json.width;
  59. this.top = json.top;
  60. this.left = json.left;
  61. this.shown = json.shown;
  62. }
  63. },
  64. methods: {
  65. onDragBox(e) {
  66. const e1 = e || window.event;
  67. e1.preventDefault();
  68. this.pos3 = e1.clientX;
  69. this.pos4 = e1.clientY;
  70. document.onmousemove = e => {
  71. const e2 = e || window.event;
  72. e2.preventDefault();
  73. // calculate the new cursor position:
  74. this.pos1 = this.pos3 - e.clientX;
  75. this.pos2 = this.pos4 - e.clientY;
  76. this.pos3 = e.clientX;
  77. this.pos4 = e.clientY;
  78. // set the element's new position:
  79. this.top -= this.pos2;
  80. this.left -= this.pos1;
  81. };
  82. document.onmouseup = () => {
  83. document.onmouseup = null;
  84. document.onmousemove = null;
  85. this.saveBox();
  86. };
  87. },
  88. onResizeBox(e) {
  89. if (e.target !== this.$refs.box) return;
  90. document.onmouseup = () => {
  91. document.onmouseup = null;
  92. const { height, width } = e.target.style;
  93. this.height = Number(
  94. height
  95. .split("")
  96. .splice(0, height.length - 2)
  97. .join("")
  98. );
  99. this.width = Number(
  100. width
  101. .split("")
  102. .splice(0, width.length - 2)
  103. .join("")
  104. );
  105. this.saveBox();
  106. };
  107. },
  108. toggleBox() {
  109. this.shown = !this.shown;
  110. this.saveBox();
  111. },
  112. resetBox() {
  113. this.top = 0;
  114. this.left = 0;
  115. this.width = 200;
  116. this.height = 200;
  117. this.saveBox();
  118. },
  119. saveBox() {
  120. if (this.id === null) return;
  121. localStorage.setItem(
  122. `box:${this.id}`,
  123. JSON.stringify({
  124. height: this.height,
  125. width: this.width,
  126. top: this.top,
  127. left: this.left,
  128. shown: this.shown
  129. })
  130. );
  131. }
  132. }
  133. };
  134. </script>
  135. <style lang="less">
  136. .night-mode .floating-box {
  137. background-color: var(--dark-grey-2) !important;
  138. border: 0 !important;
  139. .box-body b {
  140. color: var(--light-grey-2) !important;
  141. }
  142. }
  143. .floating-box {
  144. display: flex;
  145. flex-direction: column;
  146. background-color: var(--white);
  147. color: var(--black);
  148. position: fixed;
  149. z-index: 10000000;
  150. resize: both;
  151. overflow: auto;
  152. border: 1px solid var(--light-grey-2);
  153. border-radius: @border-radius;
  154. min-height: 50px !important;
  155. min-width: 50px !important;
  156. padding: 0;
  157. .box-header {
  158. display: flex;
  159. height: 30px;
  160. width: 100%;
  161. background-color: var(--primary-color);
  162. color: var(--white);
  163. z-index: 100000001;
  164. .box-title {
  165. font-size: 16px;
  166. font-weight: 600;
  167. line-height: 30px;
  168. margin-right: 5px;
  169. }
  170. .material-icons {
  171. font-size: 20px;
  172. line-height: 30px;
  173. &:hover,
  174. &:focus {
  175. filter: brightness(90%);
  176. }
  177. &.drag {
  178. margin: 0 5px;
  179. }
  180. &.delete {
  181. margin: 0 5px 0 auto;
  182. cursor: pointer;
  183. }
  184. }
  185. }
  186. .box-body {
  187. display: flex;
  188. flex-wrap: wrap;
  189. padding: 10px;
  190. height: calc(100% - 30px); /* 30px is the height of the box-header */
  191. overflow: auto;
  192. span {
  193. padding: 3px 6px;
  194. }
  195. }
  196. &.column .box-body {
  197. flex-flow: column;
  198. span {
  199. flex: 1;
  200. display: block;
  201. }
  202. }
  203. }
  204. </style>