@mixd-id/web-scaffold 0.1.230406043 → 0.1.230406045

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mixd-id/web-scaffold",
3
3
  "private": false,
4
- "version": "0.1.230406043",
4
+ "version": "0.1.230406045",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -1,116 +1,137 @@
1
1
  <template>
2
- <div :class="$style.comp" v-if="true || configLoaded">
2
+ <div :class="$style.comp" v-if="configLoaded">
3
3
 
4
4
  <slot v-if="$slots.head" name="head"></slot>
5
5
  <div v-else class="flex flex-row items-center gap-4">
6
- <div class="flex flex-row gap-1">
7
- <button type="button" class="flex flex-row gap-1 items-center">
8
- <h3>{{ title }}</h3>
9
- <svg width="16" height="16" class="fill-text-300 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"/></svg>
10
- </button>
11
- <button type="button" @click="$refs.setting.open()">
12
- <svg width="16" height="16" class="fill-text-300 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M452.515 237l31.843-18.382c9.426-5.441 13.996-16.542 11.177-27.054-11.404-42.531-33.842-80.547-64.058-110.797-7.68-7.688-19.575-9.246-28.985-3.811l-31.785 18.358a196.276 196.276 0 0 0-32.899-19.02V39.541a24.016 24.016 0 0 0-17.842-23.206c-41.761-11.107-86.117-11.121-127.93-.001-10.519 2.798-17.844 12.321-17.844 23.206v36.753a196.276 196.276 0 0 0-32.899 19.02l-31.785-18.358c-9.41-5.435-21.305-3.877-28.985 3.811-30.216 30.25-52.654 68.265-64.058 110.797-2.819 10.512 1.751 21.613 11.177 27.054L59.485 237a197.715 197.715 0 0 0 0 37.999l-31.843 18.382c-9.426 5.441-13.996 16.542-11.177 27.054 11.404 42.531 33.842 80.547 64.058 110.797 7.68 7.688 19.575 9.246 28.985 3.811l31.785-18.358a196.202 196.202 0 0 0 32.899 19.019v36.753a24.016 24.016 0 0 0 17.842 23.206c41.761 11.107 86.117 11.122 127.93.001 10.519-2.798 17.844-12.321 17.844-23.206v-36.753a196.34 196.34 0 0 0 32.899-19.019l31.785 18.358c9.41 5.435 21.305 3.877 28.985-3.811 30.216-30.25 52.654-68.266 64.058-110.797 2.819-10.512-1.751-21.613-11.177-27.054L452.515 275c1.22-12.65 1.22-25.35 0-38zm-52.679 63.019l43.819 25.289a200.138 200.138 0 0 1-33.849 58.528l-43.829-25.309c-31.984 27.397-36.659 30.077-76.168 44.029v50.599a200.917 200.917 0 0 1-67.618 0v-50.599c-39.504-13.95-44.196-16.642-76.168-44.029l-43.829 25.309a200.15 200.15 0 0 1-33.849-58.528l43.819-25.289c-7.63-41.299-7.634-46.719 0-88.038l-43.819-25.289c7.85-21.229 19.31-41.049 33.849-58.529l43.829 25.309c31.984-27.397 36.66-30.078 76.168-44.029V58.845a200.917 200.917 0 0 1 67.618 0v50.599c39.504 13.95 44.196 16.642 76.168 44.029l43.829-25.309a200.143 200.143 0 0 1 33.849 58.529l-43.819 25.289c7.631 41.3 7.634 46.718 0 88.037zM256 160c-52.935 0-96 43.065-96 96s43.065 96 96 96 96-43.065 96-96-43.065-96-96-96zm0 144c-26.468 0-48-21.532-48-48 0-26.467 21.532-48 48-48s48 21.533 48 48c0 26.468-21.532 48-48 48z"/></svg>
13
- </button>
14
-
15
- <Modal ref="setting" width="680" height="560">
16
- <template #head>
17
- <div class="relative">
18
- <div class="absolute top-0 right-0 p-2">
19
- <button type="button" class="p-2" @click="$refs.setting.close()">
20
- <svg width="24" height="24" viewBox="0 0 24 24" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg">
21
- <path d="M6.53034 5.46965C6.23745 5.17676 5.76257 5.17676 5.46968 5.46965C5.17679 5.76255 5.17679 6.23742 5.46968 6.53031L10.9393 12L5.46967 17.4697C5.17678 17.7626 5.17678 18.2374 5.46967 18.5303C5.76256 18.8232 6.23744 18.8232 6.53033 18.5303L12 13.0606L17.4697 18.5303C17.7626 18.8232 18.2375 18.8232 18.5303 18.5303C18.8232 18.2374 18.8232 17.7626 18.5303 17.4697L13.0607 12L18.5303 6.53032C18.8232 6.23743 18.8232 5.76256 18.5303 5.46966C18.2374 5.17677 17.7626 5.17677 17.4697 5.46966L12 10.9393L6.53034 5.46965Z"/>
22
- </svg>
23
- </button>
24
- </div>
25
- </div>
26
- </template>
27
- <ListViewSettings class="flex-1"
28
- :config="config"
29
- @change="load" />
30
- </Modal>
6
+ <div class="flex-1 flex flex-row gap-4">
7
+ <button type="button" ref="presetSelectorBtn"
8
+ class="flex flex-row gap-1 items-center"
9
+ @click="$refs.presetSelector.open($refs.presetSelectorBtn)">
10
+ <h3 class="flex-1 overflow-hidden whitespace-nowrap text-ellipsis">{{ preset.name }}</h3>
11
+ <svg width="16" height="16" class="fill-text-300 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"/></svg>
12
+ </button>
13
+ <button type="button" @click="openPreset()">
14
+ <svg width="19" height="19" class="fill-text-300 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M452.515 237l31.843-18.382c9.426-5.441 13.996-16.542 11.177-27.054-11.404-42.531-33.842-80.547-64.058-110.797-7.68-7.688-19.575-9.246-28.985-3.811l-31.785 18.358a196.276 196.276 0 0 0-32.899-19.02V39.541a24.016 24.016 0 0 0-17.842-23.206c-41.761-11.107-86.117-11.121-127.93-.001-10.519 2.798-17.844 12.321-17.844 23.206v36.753a196.276 196.276 0 0 0-32.899 19.02l-31.785-18.358c-9.41-5.435-21.305-3.877-28.985 3.811-30.216 30.25-52.654 68.265-64.058 110.797-2.819 10.512 1.751 21.613 11.177 27.054L59.485 237a197.715 197.715 0 0 0 0 37.999l-31.843 18.382c-9.426 5.441-13.996 16.542-11.177 27.054 11.404 42.531 33.842 80.547 64.058 110.797 7.68 7.688 19.575 9.246 28.985 3.811l31.785-18.358a196.202 196.202 0 0 0 32.899 19.019v36.753a24.016 24.016 0 0 0 17.842 23.206c41.761 11.107 86.117 11.122 127.93.001 10.519-2.798 17.844-12.321 17.844-23.206v-36.753a196.34 196.34 0 0 0 32.899-19.019l31.785 18.358c9.41 5.435 21.305 3.877 28.985-3.811 30.216-30.25 52.654-68.266 64.058-110.797 2.819-10.512-1.751-21.613-11.177-27.054L452.515 275c1.22-12.65 1.22-25.35 0-38zm-52.679 63.019l43.819 25.289a200.138 200.138 0 0 1-33.849 58.528l-43.829-25.309c-31.984 27.397-36.659 30.077-76.168 44.029v50.599a200.917 200.917 0 0 1-67.618 0v-50.599c-39.504-13.95-44.196-16.642-76.168-44.029l-43.829 25.309a200.15 200.15 0 0 1-33.849-58.528l43.819-25.289c-7.63-41.299-7.634-46.719 0-88.038l-43.819-25.289c7.85-21.229 19.31-41.049 33.849-58.529l43.829 25.309c31.984-27.397 36.66-30.078 76.168-44.029V58.845a200.917 200.917 0 0 1 67.618 0v50.599c39.504 13.95 44.196 16.642 76.168 44.029l43.829-25.309a200.143 200.143 0 0 1 33.849 58.529l-43.819 25.289c7.631 41.3 7.634 46.718 0 88.037zM256 160c-52.935 0-96 43.065-96 96s43.065 96 96 96 96-43.065 96-96-43.065-96-96-96zm0 144c-26.468 0-48-21.532-48-48 0-26.467 21.532-48 48-48s48 21.533 48 48c0 26.468-21.532 48-48 48z"/></svg>
15
+ </button>
16
+
17
+ <ContextMenu ref="presetSelector">
18
+ <div class="flex-1 flex flex-col w-[270px] cursor-pointer">
19
+ <button type="button" v-for="(preset, idx) in config.presets"
20
+ class="p-3 text-left hover:bg-primary hover:text-white"
21
+ @click="config.presetIdx = idx">
22
+ {{ preset.name }}
23
+ </button>
24
+ </div>
25
+ </ContextMenu>
26
+
27
+ <Modal ref="setting" width="680" height="560">
28
+ <template #head>
29
+ <div class="relative">
30
+ <div class="absolute top-0 right-0 p-2">
31
+ <button type="button" class="p-2" @click="$refs.setting.close()">
32
+ <svg width="24" height="24" viewBox="0 0 24 24" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg">
33
+ <path d="M6.53034 5.46965C6.23745 5.17676 5.76257 5.17676 5.46968 5.46965C5.17679 5.76255 5.17679 6.23742 5.46968 6.53031L10.9393 12L5.46967 17.4697C5.17678 17.7626 5.17678 18.2374 5.46967 18.5303C5.76256 18.8232 6.23744 18.8232 6.53033 18.5303L12 13.0606L17.4697 18.5303C17.7626 18.8232 18.2375 18.8232 18.5303 18.5303C18.8232 18.2374 18.8232 17.7626 18.5303 17.4697L13.0607 12L18.5303 6.53032C18.8232 6.23743 18.8232 5.76256 18.5303 5.46966C18.2374 5.17677 17.7626 5.17677 17.4697 5.46966L12 10.9393L6.53034 5.46965Z"/>
34
+ </svg>
35
+ </button>
36
+ </div>
37
+ </div>
38
+ </template>
39
+ <template #foot>
40
+ <div class="p-6 py-4 border-t-[1px] border-text-50">
41
+ <Button class="w-[90px]" @click="applyPreset">Apply</Button>
42
+ </div>
43
+ </template>
44
+ <ListViewSettings ref="listviewSettings" class="flex-1"
45
+ :config="copiedConfig" />
46
+ </Modal>
31
47
  </div>
32
48
 
33
- <div class="flex-1">
34
- <slot name="headerOpt"></slot>
35
- </div>
49
+ <slot name="headerOpt"></slot>
36
50
 
37
51
  <Textbox :placeholder="$t('Search...')" :clearable="true" @clear="clearSearch" v-model="preset.search"
38
- @keyup.enter="load" :class="$style.searchBox">
39
- <template #start>
40
- <div class="pl-2">
41
- <svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M508.5 481.6l-129-129c-2.3-2.3-5.3-3.5-8.5-3.5h-10.3C395 312 416 262.5 416 208 416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c54.5 0 104-21 141.1-55.2V371c0 3.2 1.3 6.2 3.5 8.5l129 129c4.7 4.7 12.3 4.7 17 0l9.9-9.9c4.7-4.7 4.7-12.3 0-17zM208 384c-97.3 0-176-78.7-176-176S110.7 32 208 32s176 78.7 176 176-78.7 176-176 176z"/></svg>
42
- </div>
43
- </template>
52
+ @keyup.enter="load" :class="$style.searchBox">
53
+ <template #start>
54
+ <div class="pl-2">
55
+ <svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M508.5 481.6l-129-129c-2.3-2.3-5.3-3.5-8.5-3.5h-10.3C395 312 416 262.5 416 208 416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c54.5 0 104-21 141.1-55.2V371c0 3.2 1.3 6.2 3.5 8.5l129 129c4.7 4.7 12.3 4.7 17 0l9.9-9.9c4.7-4.7 4.7-12.3 0-17zM208 384c-97.3 0-176-78.7-176-176S110.7 32 208 32s176 78.7 176 176-78.7 176-176 176z"/></svg>
56
+ </div>
57
+ </template>
44
58
  </Textbox>
45
59
  </div>
46
60
 
47
- <VirtualTable ref="table1" :columns="presetColumns" :items="items" class="flex-1"
48
- @scroll-end="loadNext">
49
- <template v-for="column in presetColumns" #[colOf(column.key)]="{}">
50
- <div :class="getHeader(column)" @click="openColumnOptions(column.key, $event.target.closest('.' + $style.header))">
51
- <div>
52
- {{ column.label ?? column.key }}
61
+ <div class="flex-1 flex" v-if="mediaPrefix">
62
+ <VirtualTable v-if="mediaPrefix !== 'sm'" ref="table1" :columns="presetColumns" :items="items" class="flex-1"
63
+ @scroll-end="loadNext">
64
+ <template v-for="column in presetColumns" #[colOf(column.key)]="{}">
65
+ <div :class="getHeader(column)" @click="openColumnOptions(column.key, $event.target.closest('.' + $style.header))">
66
+ <div>
67
+ {{ column.label ?? column.key }}
68
+ </div>
69
+ <div class="absolute top-0 right-0 p-2 bg-base-500" v-if="presetSortedColumns[column.key] === 'asc'">
70
+ <svg width="19" height="19" viewBox="0 0 24 24" class="fill-text-400" xmlns="http://www.w3.org/2000/svg">
71
+ <path d="M6.75 5C6.75 4.58579 6.41421 4.25 6 4.25C5.58579 4.25 5.25 4.58579 5.25 5V17.6893L3.53033 15.9697C3.23744 15.6768 2.76256 15.6768 2.46967 15.9697C2.17678 16.2626 2.17678 16.7374 2.46967 17.0303L4.76256 19.3232C5.44598 20.0066 6.55402 20.0066 7.23744 19.3232L9.53033 17.0303C9.82322 16.7374 9.82322 16.2626 9.53033 15.9697C9.23744 15.6768 8.76256 15.6768 8.46967 15.9697L6.75 17.6893V5Z"/>
72
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 17C12.25 17.4142 12.5858 17.75 13 17.75H21C21.4142 17.75 21.75 17.4142 21.75 17C21.75 16.5858 21.4142 16.25 21 16.25H13C12.5858 16.25 12.25 16.5858 12.25 17Z"/>
73
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 12C12.25 11.5858 12.5858 11.25 13 11.25H18C18.4142 11.25 18.75 11.5858 18.75 12C18.75 12.4142 18.4142 12.75 18 12.75H13C12.5858 12.75 12.25 12.4142 12.25 12Z"/>
74
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 7C12.25 6.58579 12.5858 6.25 13 6.25H15C15.4142 6.25 15.75 6.58579 15.75 7C15.75 7.41421 15.4142 7.75 15 7.75H13C12.5858 7.75 12.25 7.41421 12.25 7Z"/>
75
+ </svg>
76
+ </div>
77
+ <div class="absolute top-0 right-0 p-2 bg-base-500" v-else-if="presetSortedColumns[column.key] === 'desc'">
78
+ <svg width="19" height="19" viewBox="0 0 24 24" class="fill-text-400" xmlns="http://www.w3.org/2000/svg">
79
+ <path d="M6.75 5C6.75 4.58579 6.41421 4.25 6 4.25C5.58579 4.25 5.25 4.58579 5.25 5V17.6893L3.53033 15.9697C3.23744 15.6768 2.76256 15.6768 2.46967 15.9697C2.17678 16.2626 2.17678 16.7374 2.46967 17.0303L4.76256 19.3232C5.44598 20.0066 6.55402 20.0066 7.23744 19.3232L9.53033 17.0303C9.82322 16.7374 9.82322 16.2626 9.53033 15.9697C9.23744 15.6768 8.76256 15.6768 8.46967 15.9697L6.75 17.6893V5Z"/>
80
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 7C12.25 6.58579 12.5858 6.25 13 6.25H21C21.4142 6.25 21.75 6.58579 21.75 7C21.75 7.41421 21.4142 7.75 21 7.75H13C12.5858 7.75 12.25 7.41421 12.25 7Z"/>
81
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 12C12.25 12.4142 12.5858 12.75 13 12.75H18C18.4142 12.75 18.75 12.4142 18.75 12C18.75 11.5858 18.4142 11.25 18 11.25H13C12.5858 11.25 12.25 11.5858 12.25 12Z"/>
82
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 17C12.25 17.4142 12.5858 17.75 13 17.75H15C15.4142 17.75 15.75 17.4142 15.75 17C15.75 16.5858 15.4142 16.25 15 16.25H13C12.5858 16.25 12.25 16.5858 12.25 17Z"/>
83
+ </svg>
84
+ </div>
53
85
  </div>
54
- <div class="absolute top-0 right-0 p-2 bg-base-500" v-if="false">
55
- <svg width="19" height="19" viewBox="0 0 24 24" class="fill-text-400" xmlns="http://www.w3.org/2000/svg">
56
- <path d="M6.75 5C6.75 4.58579 6.41421 4.25 6 4.25C5.58579 4.25 5.25 4.58579 5.25 5V17.6893L3.53033 15.9697C3.23744 15.6768 2.76256 15.6768 2.46967 15.9697C2.17678 16.2626 2.17678 16.7374 2.46967 17.0303L4.76256 19.3232C5.44598 20.0066 6.55402 20.0066 7.23744 19.3232L9.53033 17.0303C9.82322 16.7374 9.82322 16.2626 9.53033 15.9697C9.23744 15.6768 8.76256 15.6768 8.46967 15.9697L6.75 17.6893V5Z"/>
57
- <path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 17C12.25 17.4142 12.5858 17.75 13 17.75H21C21.4142 17.75 21.75 17.4142 21.75 17C21.75 16.5858 21.4142 16.25 21 16.25H13C12.5858 16.25 12.25 16.5858 12.25 17Z"/>
58
- <path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 12C12.25 11.5858 12.5858 11.25 13 11.25H18C18.4142 11.25 18.75 11.5858 18.75 12C18.75 12.4142 18.4142 12.75 18 12.75H13C12.5858 12.75 12.25 12.4142 12.25 12Z"/>
59
- <path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 7C12.25 6.58579 12.5858 6.25 13 6.25H15C15.4142 6.25 15.75 6.58579 15.75 7C15.75 7.41421 15.4142 7.75 15 7.75H13C12.5858 7.75 12.25 7.41421 12.25 7Z"/>
60
- </svg>
86
+ </template>
87
+ <template v-for="(_, slot) in headerSlots" #[slot]="{ item, index }">
88
+ <div :class="getHeader(slot.replace('col-', ''))">
89
+ <slot :name="slot" :item="item" :index="index"></slot>
61
90
  </div>
62
- <div class="absolute top-0 right-0 p-2 bg-base-500" v-else-if="false">
63
- <svg width="19" height="19" viewBox="0 0 24 24" class="fill-text-400" xmlns="http://www.w3.org/2000/svg">
64
- <path d="M6.75 5C6.75 4.58579 6.41421 4.25 6 4.25C5.58579 4.25 5.25 4.58579 5.25 5V17.6893L3.53033 15.9697C3.23744 15.6768 2.76256 15.6768 2.46967 15.9697C2.17678 16.2626 2.17678 16.7374 2.46967 17.0303L4.76256 19.3232C5.44598 20.0066 6.55402 20.0066 7.23744 19.3232L9.53033 17.0303C9.82322 16.7374 9.82322 16.2626 9.53033 15.9697C9.23744 15.6768 8.76256 15.6768 8.46967 15.9697L6.75 17.6893V5Z"/>
65
- <path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 7C12.25 6.58579 12.5858 6.25 13 6.25H21C21.4142 6.25 21.75 6.58579 21.75 7C21.75 7.41421 21.4142 7.75 21 7.75H13C12.5858 7.75 12.25 7.41421 12.25 7Z"/>
66
- <path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 12C12.25 12.4142 12.5858 12.75 13 12.75H18C18.4142 12.75 18.75 12.4142 18.75 12C18.75 11.5858 18.4142 11.25 18 11.25H13C12.5858 11.25 12.25 11.5858 12.25 12Z"/>
67
- <path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 17C12.25 17.4142 12.5858 17.75 13 17.75H15C15.4142 17.75 15.75 17.4142 15.75 17C15.75 16.5858 15.4142 16.25 15 16.25H13C12.5858 16.25 12.25 16.5858 12.25 17Z"/>
68
- </svg>
91
+ </template>
92
+ <template v-for="(_, slot) in contentSlots" #[slot]="{ item, index }">
93
+ <slot :name="slot" :item="item" :index="index"></slot>
94
+ </template>
95
+ </VirtualTable>
96
+ <VirtualScroll v-else :items="items" class="flex-1" @scroll-end="loadNext">
97
+ <template #item="{ item }">
98
+ <slot name="mobileItem" :item="item"></slot>
99
+ </template>
100
+ </VirtualScroll>
101
+ </div>
102
+
103
+ <ContextMenu ref="columnMenu" :dismiss="false">
104
+ <div class="flex-1 flex flex-col w-[270px] p-3">
105
+ <div class="flex flex-col">
106
+ <div class="flex flex-row gap-2 items-center">
107
+ <div class="p-2 text-text-300 flex-1">Sort By</div>
108
+ <div class="text-primary cursor-pointer text-sm" @click="openPreset('sort');$refs.columnMenu.close()">Sort Options</div>
69
109
  </div>
110
+ <div class="p-2 cursor-pointer" :class="$style.hoverable" @click="setSortCurrent(1)">Sort Ascending</div>
111
+ <div class="p-2 cursor-pointer" :class="$style.hoverable" @click="setSortCurrent(2)">Sort Descending</div>
70
112
  </div>
71
- </template>
72
- <template v-for="(_, slot) in headerSlots" #[slot]="{ item, index }">
73
- <div :class="getHeader(slot.replace('col-', ''))">
74
- <slot :name="slot" :item="item" :index="index"></slot>
113
+ <div class="h-[1px] bg-text-50 my-2"></div>
114
+ <div class="flex flex-col">
115
+ <div class="p-2 cursor-pointer" :class="$style.hoverable" @click="hide">Hide</div>
116
+ <div class="p-2 cursor-pointer" :class="$style.hoverable" @click="openPreset();$refs.columnMenu.close()">Column Options</div>
75
117
  </div>
76
- </template>
77
- <template v-for="(_, slot) in contentSlots" #[slot]="{ item, index }">
78
- <slot :name="slot" :item="item" :index="index"></slot>
79
- </template>
80
- </VirtualTable>
81
-
82
- <ContextMenu ref="columnMenu" :dismiss="false">
83
- <div class="flex-1 flex flex-col w-[270px] p-3">
84
- <div class="flex flex-col">
85
- <div class="flex flex-row gap-2 items-center">
86
- <div class="p-2 text-text-300 flex-1">Sort By</div>
87
- <div class="text-primary cursor-pointer text-sm" @click="openPreset('sort');$refs.columnMenu.close()">Sort Options</div>
88
- </div>
89
- <div class="p-2 cursor-pointer" :class="$style.hoverable" @click="setSortCurrent(1)">Sort Ascending</div>
90
- <div class="p-2 cursor-pointer" :class="$style.hoverable" @click="setSortCurrent(2)">Sort Descending</div>
91
- </div>
92
- <div class="h-[1px] bg-text-50 my-2"></div>
93
- <div class="flex flex-col">
94
- <div class="p-2 cursor-pointer" :class="$style.hoverable" @click="hide">Hide</div>
95
- <div class="p-2 cursor-pointer" :class="$style.hoverable" @click="openPreset();$refs.columnMenu.close()">Column Options</div>
96
- </div>
97
- <div class="h-[1px] bg-text-50 my-2"></div>
98
- <div class="flex flex-col">
99
- <div class="flex flex-row gap-2 items-center">
100
- <div class="p-2 text-text-300 flex-1">Filters</div>
101
- <div class="text-primary cursor-pointer text-sm" @click="openPreset('filter');$refs.columnMenu.close()">Filter Options</div>
102
- </div>
103
- <div v-if="presetCurrentFilters.length > 0">
104
- <ListPage1Filter v-if="preset.filters" v-for="filter in presetCurrentFilters"
105
- :filter="filter" :column="config.columns[filter.key]"
106
- @remove="removeFilter(filter)" @change="load" />
107
- </div>
108
- <div v-else>
109
- <div class="p-2 cursor-pointer" :class="$style.hoverable" @click.stop="addCurrentFilter">Add Filter</div>
110
- </div>
111
- </div>
112
- </div>
113
- </ContextMenu>
118
+ <div class="h-[1px] bg-text-50 my-2"></div>
119
+ <div class="flex flex-col">
120
+ <div class="flex flex-row gap-2 items-center">
121
+ <div class="p-2 text-text-300 flex-1">Filters</div>
122
+ <div class="text-primary cursor-pointer text-sm" @click="openPreset('filter');$refs.columnMenu.close()">Filter Options</div>
123
+ </div>
124
+ <div v-if="presetCurrentFilters.length > 0">
125
+ <ListPage1Filter v-if="preset.filters" v-for="filter in presetCurrentFilters"
126
+ :filter="filter" :column="config.columns[filter.key]"
127
+ @remove="removeFilter(filter)" @change="load" />
128
+ </div>
129
+ <div v-else>
130
+ <div class="p-2 cursor-pointer" :class="$style.hoverable" @click.stop="addCurrentFilter">Add Filter</div>
131
+ </div>
132
+ </div>
133
+ </div>
134
+ </ContextMenu>
114
135
 
115
136
  </div>
116
137
  </template>
@@ -120,7 +141,6 @@
120
141
  import throttle from "lodash/throttle";
121
142
  import VirtualTable from "./VirtualTable.vue";
122
143
 
123
-
124
144
  export default{
125
145
  components: {VirtualTable},
126
146
 
@@ -153,7 +173,7 @@ export default{
153
173
 
154
174
  addFilter(key){
155
175
 
156
- const column = this.config.columns[key]
176
+ const column = this.presetColumns.find((_) => _.key === key)
157
177
 
158
178
  if(!this.preset.filters){
159
179
  this.preset.filters = []
@@ -179,6 +199,32 @@ export default{
179
199
  this.presetFilterSelector = null
180
200
  },
181
201
 
202
+ calcMediaPrefix: throttle(function(){
203
+ if(!this.$el) return
204
+
205
+ const w = this.$el.clientWidth
206
+ if(!w) return
207
+
208
+ let prefix
209
+ if(w < 768){
210
+ prefix = 'sm'
211
+ }
212
+ else if(w < 1024){
213
+ prefix = 'md'
214
+ }
215
+ else if(w < 1280){
216
+ prefix = 'lg'
217
+ }
218
+ else if(w < 1536){
219
+ prefix = 'xl'
220
+ }
221
+ else{
222
+ prefix = '2xl'
223
+ }
224
+
225
+ this.mediaPrefix = prefix
226
+ }, 1000, { leading:true }),
227
+
182
228
  clearSearch(){
183
229
  this.preset.search = ''
184
230
  this.load()
@@ -214,8 +260,7 @@ export default{
214
260
  },
215
261
 
216
262
  loadNext(){
217
-
218
- this.$refs.table1.setState(2)
263
+ this.$refs.table1 ? this.$refs.table1.setState(2) : undefined
219
264
  this.socketEmit2(this.src, {
220
265
  preset: this.preset,
221
266
  afterItem: this.items[this.items.length - 1]
@@ -223,12 +268,13 @@ export default{
223
268
  .then((res) => {
224
269
  this.items.push(...res.items)
225
270
  this.hasNext = res.hasNext
226
- console.log('loadNext', this.$data)
227
271
  })
228
272
  .catch((err) => {
229
273
  this.toast(err)
230
274
  })
231
- .finally(() => this.$refs.table1.setState(1))
275
+ .finally(() => {
276
+ this.$refs.table1 ? this.$refs.table1.setState(1) : undefined
277
+ })
232
278
  },
233
279
 
234
280
  async loadConfig(){
@@ -241,12 +287,28 @@ export default{
241
287
  .then((config) => {
242
288
  Object.assign(this.config, config ?? {})
243
289
  this.configLoaded = true
290
+ this.$nextTick(() => this.calcMediaPrefix())
244
291
  })
245
292
  }
246
293
  },
247
294
 
295
+ loadSummary(){
296
+
297
+ if(!this.presetSummary) return
298
+
299
+ this.socketEmit2(`${this.computedDataSource}.load-summary`, {
300
+ preset: this.preset
301
+ })
302
+ .then((res) => {
303
+ const data = res && res.data ? res.data : res
304
+ this.summary = data
305
+ })
306
+ .catch((err) => {
307
+ this.toast(err)
308
+ })
309
+ },
310
+
248
311
  onHooks(model, event, items){
249
- console.log('onHooks', model, event, items)
250
312
  if(model === this.subscriptionObj.model){
251
313
  switch(event){
252
314
 
@@ -276,9 +338,23 @@ export default{
276
338
  },
277
339
 
278
340
  openPreset(tab){
341
+ if(!this.config.presetTab) this.config.presetTab = 'column'
342
+
343
+ this.copiedConfig = {
344
+ ...JSON.parse(JSON.stringify(this.config)),
345
+ presetTab: tab ?? this.config.presetTab
346
+ }
279
347
  this.$refs.setting.open()
280
348
  },
281
349
 
350
+ applyPreset(){
351
+ if(JSON.stringify(this.copiedConfig) !== JSON.stringify(this.config)){
352
+ Object.assign(this.config, this.copiedConfig)
353
+ this.load()
354
+ }
355
+ this.$refs.setting.close()
356
+ },
357
+
282
358
  removeFilter(filter){
283
359
  this.preset.filters.splice(this.preset.filters.indexOf(filter), 1)
284
360
  this.load()
@@ -288,7 +364,7 @@ export default{
288
364
  this.preset.sorts = [
289
365
  {
290
366
  key: this.selectedColumn,
291
- label: this.config.columns[this.selectedColumn].label,
367
+ label: this.presetColumns.find((_) => _.key === this.selectedColumn).label,
292
368
  type: sortType === 2 ? 'desc' : 'asc'
293
369
  }
294
370
  ]
@@ -321,7 +397,6 @@ export default{
321
397
 
322
398
  },
323
399
 
324
-
325
400
  inject: [ 'socket', 'socketEmit2', 'toast', 'alert' ],
326
401
 
327
402
  computed: {
@@ -371,17 +446,38 @@ export default{
371
446
  },
372
447
 
373
448
  presetColumns(){
374
- if(!this.preset.columns) return
375
-
376
- for(let i = 0 ; i < (this.preset.columns).length ; i++){
377
- const presetColumn = this.preset.columns[i]
378
- const column = this.config.columns[presetColumn.key] ?? {}
379
- for(let key in column){
380
- if(!presetColumn[key]){
381
- this.preset.columns[i][key] = column[key]
382
- }
449
+ if(!this.preset || !this.preset.columns) return []
450
+
451
+ const presetColumns = {}
452
+ this.preset.columns.forEach((column, _index) => {
453
+ presetColumns[column.key] = {
454
+ ...column,
455
+ _index
383
456
  }
384
- }
457
+ })
458
+
459
+ this.preset.columns = this.config.columns.map((column, index) => {
460
+ const _sort = {
461
+ index: presetColumns[column.key] ? presetColumns[column.key]._index : index,
462
+ isUser: !!presetColumns[column.key]
463
+ }
464
+
465
+ return {
466
+ ...column,
467
+ ...(presetColumns[column.key] ?? {}),
468
+ _sort
469
+ }
470
+ })
471
+ .sort((a, b) => {
472
+ if(a._sort.index < b._sort.index) return -1
473
+ if(a._sort.index > b._sort.index) return 1
474
+ return a._sort.isUser ? 1 : -1
475
+ })
476
+ .map((column) => {
477
+ delete column._sort
478
+ delete column._index
479
+ return column
480
+ })
385
481
 
386
482
  return this.preset.columns
387
483
  },
@@ -391,6 +487,12 @@ export default{
391
487
  return this.preset.filters.filter((_) => _.key === this.selectedColumn)
392
488
  },
393
489
 
490
+ presetSortedColumns(){
491
+ const c = {};
492
+ (this.preset.sorts ?? []).forEach((_) => c[_.key] = _.type);
493
+ return c
494
+ },
495
+
394
496
  },
395
497
 
396
498
  data(){
@@ -399,12 +501,16 @@ export default{
399
501
  hasNext: false,
400
502
  count: null,
401
503
  configLoaded: false,
402
- selectedColumn: null
504
+ selectedColumn: null,
505
+ copiedConfig: null,
506
+ mediaPrefix: 'md'
403
507
  }
404
508
  },
405
509
 
406
510
 
407
511
  mounted() {
512
+ window.addEventListener('resize', () => this.calcMediaPrefix())
513
+ this.calcMediaPrefix()
408
514
  this.socket.onAny(this.onHooks)
409
515
 
410
516
  window.setTimeout(() => {
@@ -448,6 +554,10 @@ export default{
448
554
  @apply text-ellipsis whitespace-nowrap overflow-x-hidden;
449
555
  }
450
556
 
557
+ .headerSelected{
558
+ @apply border-primary;
559
+ }
560
+
451
561
  .searchBox{
452
562
  @apply w-[300px] max-w-[30%];
453
563
  }
@@ -2,21 +2,21 @@
2
2
  <div :class="styleComp">
3
3
  <div class="border-r-[1px] border-text-50 bg-base-400">
4
4
 
5
- <div class="p-6">
6
- <h3>Presets</h3>
7
- <br />
5
+ <div class="p-6 flex flex-row">
6
+ <h3 class="flex-1">Presets</h3>
7
+ <button type="button" @click="addPreset">
8
+ <svg width="21" height="21" class="fill-text-300 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M376 232H216V72c0-4.42-3.58-8-8-8h-32c-4.42 0-8 3.58-8 8v160H8c-4.42 0-8 3.58-8 8v32c0 4.42 3.58 8 8 8h160v160c0 4.42 3.58 8 8 8h32c4.42 0 8-3.58 8-8V280h160c4.42 0 8-3.58 8-8v-32c0-4.42-3.58-8-8-8z"/></svg>
9
+ </button>
8
10
  </div>
9
11
 
10
12
  <div class="flex-1 overflow-y-auto">
11
13
  <div v-for="(preset, idx) in config.presets"
12
- class="px-6 py-3 flex flex-row gap-2 items-center hover:bg-text-50 cursor-pointer">
13
- <div class="px-2">
14
- <Checkbox :checked="idx === config.presetIdx" @change="selectPreset(preset)" />
15
- </div>
16
- <div @click="presetOpenIdx = idx" class="flex-1">
14
+ class="p-2 flex flex-row gap-2 items-center hover:bg-primary-50 cursor-pointer"
15
+ :class="config.presetIdx === idx ? 'bg-primary-100' : ''">
16
+ <div @click="config.presetIdx = idx" class="flex-1 p-1 pl-4 overflow-x-hidden whitespace-nowrap text-ellipsis">
17
17
  <label>{{ preset.name }}</label>
18
18
  </div>
19
- <button class="px-2" v-if="config.presets.length > 1" @click="removePreset(idx)">
19
+ <button type="button" class="px-2" v-if="config.presets.length > 1" @click="removePreset(idx)">
20
20
  <svg width="16" height="16" viewBox="0 0 24 24" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg">
21
21
  <path fill-rule="evenodd" clip-rule="evenodd" d="M10 10.25C10.4142 10.25 10.75 10.5858 10.75 11V16C10.75 16.4142 10.4142 16.75 10 16.75C9.58579 16.75 9.25 16.4142 9.25 16V11C9.25 10.5858 9.58579 10.25 10 10.25Z"/>
22
22
  <path fill-rule="evenodd" clip-rule="evenodd" d="M14 10.25C14.4142 10.25 14.75 10.5858 14.75 11V16C14.75 16.4142 14.4142 16.75 14 16.75C13.5858 16.75 13.25 16.4142 13.25 16V11C13.25 10.5858 13.5858 10.25 14 10.25Z"/>
@@ -24,37 +24,34 @@
24
24
  </svg>
25
25
  </button>
26
26
  </div>
27
- <div class="p-5 text-center">
28
- <button @click="addPreset">
29
- <svg width="21" height="21" viewBox="0 0 24 24" class="fill-text-300 hover:fill-primary" xmlns="http://www.w3.org/2000/svg">
30
- <path d="M12.75 5C12.75 4.58579 12.4142 4.25 12 4.25C11.5858 4.25 11.25 4.58579 11.25 5V11.25H5C4.58579 11.25 4.25 11.5858 4.25 12C4.25 12.4142 4.58579 12.75 5 12.75H11.25V19C11.25 19.4142 11.5858 19.75 12 19.75C12.4142 19.75 12.75 19.4142 12.75 19V12.75H19C19.4142 12.75 19.75 12.4142 19.75 12C19.75 11.5858 19.4142 11.25 19 11.25H12.75V5Z"/>
31
- </svg>
32
- </button>
33
- </div>
34
27
  </div>
35
28
 
36
29
  </div>
37
30
 
38
31
  <div>
39
32
  <div class="p-6 pb-0">
40
- <div class="flex flex-row gap-4 mb-3">
41
- <button @click="presetOpenIdx = -1">
33
+ <div class="flex flex-row items-center gap-4 mb-3">
34
+ <button type="button" class="hidden" @click="presetOpenIdx = -1">
42
35
  <svg width="19" height="19" viewBox="0 0 24 24" class="fill-text-300 hover:fill-text-400" xmlns="http://www.w3.org/2000/svg">
43
36
  <path d="M10.5303 17.9697C10.8232 18.2626 10.8232 18.7374 10.5303 19.0303C10.2374 19.3232 9.76253 19.3232 9.46964 19.0303L3.67675 13.2374C2.99333 12.554 2.99333 11.446 3.67675 10.7626L9.46964 4.96967C9.76253 4.67678 10.2374 4.67678 10.5303 4.96967C10.8232 5.26256 10.8232 5.73744 10.5303 6.03033L5.31063 11.25H20C20.4142 11.25 20.75 11.5858 20.75 12C20.75 12.4142 20.4142 12.75 20 12.75H5.31063L10.5303 17.9697Z"/>
44
37
  </svg>
45
38
  </button>
46
- <input class="flex-1 text-xl bg-transparent outline-none" :value="preset.name" @blur="preset.name = $event.target.value"
47
- @keyup.enter="preset.name = $event.target.value" />
48
- </div>
39
+ <div class="flex-1 flex flex-row items-center gap-2">
40
+ <svg @click="$refs.title.select()" width="16" height="16" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/></svg>
41
+ <input ref="title" class="flex-1 text-xl bg-transparent outline-none" :value="preset.name" maxlength="30"
42
+ @blur="preset.name = $event.target.value"
43
+ @keyup.enter="preset.name = $event.target.value" />
44
+ </div>
45
+ </div>
49
46
 
50
47
  <div class="flex justify-center">
51
- <Tabs v-model="presetTab" :items="tabItems" @change="tabChanged"/>
48
+ <Tabs v-model="config.presetTab" :items="tabItems" @change="tabChanged"/>
52
49
  </div>
53
50
  </div>
54
51
 
55
52
  <div class="flex-1 overflow-y-auto px-6 mb-6">
56
53
 
57
- <div class="p-2 mt-6 flex flex-col" v-if="presetTab === 'column'">
54
+ <div class="p-2 mt-6 flex flex-col" v-if="!config.presetTab || config.presetTab === 'column'">
58
55
  <ListItem :items="presetColumns" @reorder="reorderColumns">
59
56
  <template v-slot="{ item }">
60
57
  <div class="flex flex-row items-center gap-2" :key="item">
@@ -72,7 +69,7 @@
72
69
  </ListItem>
73
70
  </div>
74
71
 
75
- <div class="flex flex-col" v-else-if="presetTab === 'filter'">
72
+ <div class="flex flex-col" v-else-if="config.presetTab === 'filter'">
76
73
  <div v-if="filterableColumns.length > 0">
77
74
  <ListPage1Filter v-if="preset.filters" v-for="filter in preset.filters"
78
75
  :filter="filter" :column="config.columns[filter.key]"
@@ -91,7 +88,7 @@
91
88
  </div>
92
89
  </div>
93
90
 
94
- <div class="flex flex-col" v-else-if="presetTab === 'sort'">
91
+ <div class="flex flex-col" v-else-if="config.presetTab === 'sort'">
95
92
  <div v-if="sortableColumns.length > 0" class="my-8">
96
93
  <div v-for="sort in preset.sorts" class="py-4">
97
94
  <div class="flex flex-row items-center gap-2">
@@ -107,7 +104,7 @@
107
104
  <option value="desc">Desc</option>
108
105
  </Dropdown>
109
106
  </div>
110
- <button @click="removeSort(sort)">
107
+ <button type="button" @click="removeSort(sort)">
111
108
  <svg width="21" height="21" class="fill-text-100 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
112
109
  <path class="secondary" fill-rule="evenodd" d="M15.78 14.36a1 1 0 0 1-1.42 1.42l-2.82-2.83-2.83 2.83a1 1 0 1 1-1.42-1.42l2.83-2.82L7.3 8.7a1 1 0 0 1 1.42-1.42l2.83 2.83 2.82-2.83a1 1 0 0 1 1.42 1.42l-2.83 2.83 2.83 2.82z"/>
113
110
  </svg>
@@ -129,7 +126,7 @@
129
126
  </div>
130
127
  </div>
131
128
 
132
- <div class="flex flex-col gap-3 py-8" v-else-if="presetTab === 'summary'">
129
+ <div class="flex flex-col gap-3 py-8" v-else-if="config.presetTab === 'summarys'">
133
130
 
134
131
  <div v-if="config.summaryOpenIdx === -1">
135
132
  <div v-for="(summary, idx) in preset.summaries">
@@ -140,7 +137,7 @@
140
137
  <div class="flex-1" @click="config.summaryOpenIdx = idx">
141
138
  <label>{{ summary.title }}</label>
142
139
  </div>
143
- <button class="px-2" v-if="preset.summaries.length > 1" @click="removeSummary(summary)">
140
+ <button type="button" class="px-2" v-if="preset.summaries.length > 1" @click="removeSummary(summary)">
144
141
  <svg width="16" height="16" viewBox="0 0 24 24" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg">
145
142
  <path fill-rule="evenodd" clip-rule="evenodd" d="M10 10.25C10.4142 10.25 10.75 10.5858 10.75 11V16C10.75 16.4142 10.4142 16.75 10 16.75C9.58579 16.75 9.25 16.4142 9.25 16V11C9.25 10.5858 9.58579 10.25 10 10.25Z"/>
146
143
  <path fill-rule="evenodd" clip-rule="evenodd" d="M14 10.25C14.4142 10.25 14.75 10.5858 14.75 11V16C14.75 16.4142 14.4142 16.75 14 16.75C13.5858 16.75 13.25 16.4142 13.25 16V11C13.25 10.5858 13.5858 10.25 14 10.25Z"/>
@@ -150,7 +147,7 @@
150
147
  </div>
151
148
  </div>
152
149
  <div class="p-2 text-center">
153
- <button @click="addSummary">
150
+ <button type="button" @click="addSummary">
154
151
  <svg width="21" height="21" viewBox="0 0 24 24" class="fill-text-300 hover:fill-primary" xmlns="http://www.w3.org/2000/svg">
155
152
  <path d="M12.75 5C12.75 4.58579 12.4142 4.25 12 4.25C11.5858 4.25 11.25 4.58579 11.25 5V11.25H5C4.58579 11.25 4.25 11.5858 4.25 12C4.25 12.4142 4.58579 12.75 5 12.75H11.25V19C11.25 19.4142 11.5858 19.75 12 19.75C12.4142 19.75 12.75 19.4142 12.75 19V12.75H19C19.4142 12.75 19.75 12.4142 19.75 12C19.75 11.5858 19.4142 11.25 19 11.25H12.75V5Z"/>
156
153
  </svg>
@@ -162,7 +159,7 @@
162
159
 
163
160
  <div class="flex flex-row items-center p-3 py-1">
164
161
  <label class="text-text-400 flex-1">Enabled</label>
165
- <Checkbox v-model="openedPresetSummary.enabled" @change="loadSummary"/>
162
+ <Checkbox v-model="openedPresetSummary.enabled"/>
166
163
  </div>
167
164
 
168
165
  <div class="h-[1px] bg-text-50 my-1"></div>
@@ -176,7 +173,7 @@
176
173
 
177
174
  <div class="flex flex-row items-center p-3 py-1">
178
175
  <label class="text-text-400 flex-1">Hide Details</label>
179
- <Checkbox v-model="openedPresetSummary.hideDetails" @change="loadSummary"/>
176
+ <Checkbox v-model="openedPresetSummary.hideDetails"/>
180
177
  </div>
181
178
 
182
179
  <div class="h-[1px] bg-text-50 my-1"></div>
@@ -184,7 +181,7 @@
184
181
  <div class="p-3">
185
182
  <label class="text-text-400 flex-1">Type</label>
186
183
  <div class="mt-2">
187
- <Dropdown v-model="openedPresetSummary.mode" class="flex-1" @change="loadSummary">
184
+ <Dropdown v-model="openedPresetSummary.mode" class="flex-1">
188
185
  <option value="table">{{ $t('Table') }}</option>
189
186
  <option value="bar">{{ $t('Bar Chart') }}</option>
190
187
  <option value="line">{{ $t('Line Chart') }}</option>
@@ -192,7 +189,7 @@
192
189
  </Dropdown>
193
190
  </div>
194
191
  <div v-if="[ 'bar', 'line' ].includes(openedPresetSummary.mode)" class="mt-1">
195
- <Checkbox v-model="openedPresetSummary.hideLegends" @change="loadSummary">Hide Legend</Checkbox>
192
+ <Checkbox v-model="openedPresetSummary.hideLegends">Hide Legend</Checkbox>
196
193
  </div>
197
194
  </div>
198
195
 
@@ -201,11 +198,11 @@
201
198
  <div class="p-3">
202
199
  <label class="text-text-400 flex-1">{{ $t('Row') }}</label>
203
200
  <div class="flex flex-row mt-2 gap-2">
204
- <Dropdown v-model="openedPresetSummary.table.rows[0].key" class="flex-1" @change="loadSummary">
201
+ <Dropdown v-model="openedPresetSummary.table.rows[0].key" class="flex-1">
205
202
  <option value="" disabled selected>{{ $t('Add Column') }}</option>
206
203
  <option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
207
204
  </Dropdown>
208
- <Dropdown v-model="openedPresetSummary.table.rows[0].format" class="w-[100px]" @change="loadSummary">
205
+ <Dropdown v-model="openedPresetSummary.table.rows[0].format" class="w-[100px]">
209
206
  <option value="">{{ $t('Default') }}</option>
210
207
  <option value="date">{{ $t('Date') }}</option>
211
208
  <option value="month">{{ $t('Month') }}</option>
@@ -221,7 +218,7 @@
221
218
  <div class="p-3">
222
219
  <label class="text-text-400 flex-1">{{$t('Column') }}</label>
223
220
  <div>
224
- <Dropdown v-model="openedPresetSummary.table.columns[0].key" class="flex-1" @change="loadSummary">
221
+ <Dropdown v-model="openedPresetSummary.table.columns[0].key" class="flex-1">
225
222
  <option value="" disabled selected>{{ $t('Add Column') }}</option>
226
223
  <option value="(none)">{{ $t('(None)') }}</option>
227
224
  <option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
@@ -235,7 +232,7 @@
235
232
  <div class="p-3">
236
233
  <label class="text-text-400 flex-1">Values</label>
237
234
  <div class="flex flex-row mt-2 gap-2">
238
- <Dropdown v-model="openedPresetSummary.table.values[0].aggregrate" class="flex-1" @change="loadSummary">
235
+ <Dropdown v-model="openedPresetSummary.table.values[0].aggregrate" class="flex-1">
239
236
  <option value="" disabled selected>{{ $t('Select') }}</option>
240
237
  <option value="count">{{ $t('Count') }}</option>
241
238
  <option value="max">{{ $t('Max') }}</option>
@@ -252,11 +249,11 @@
252
249
  <div class="p-3">
253
250
  <label class="text-text-400 flex-1">{{ $t('X-axis') }}</label>
254
251
  <div class="flex flex-row mt-2 gap-2">
255
- <Dropdown v-model="openedPresetSummary.bar.rows[0].key" class="flex-1" @change="loadSummary">
252
+ <Dropdown v-model="openedPresetSummary.bar.rows[0].key" class="flex-1">
256
253
  <option value="" disabled selected>{{ $t('Add Column') }}</option>
257
254
  <option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
258
255
  </Dropdown>
259
- <Dropdown v-model="openedPresetSummary.bar.rows[0].format" class="w-[100px]" @change="loadSummary">
256
+ <Dropdown v-model="openedPresetSummary.bar.rows[0].format" class="w-[100px]">
260
257
  <option value="">{{ $t('Default') }}</option>
261
258
  <option value="date">{{ $t('Date') }}</option>
262
259
  <option value="month">{{ $t('Month') }}</option>
@@ -269,12 +266,12 @@
269
266
  <div class="p-3">
270
267
  <label class="text-text-400 flex-1">{{ $t('Column') }}</label>
271
268
  <div class="flex flex-row mt-2 gap-2">
272
- <Dropdown v-model="openedPresetSummary.bar.columns[0].key" class="flex-1" @change="loadSummary">
269
+ <Dropdown v-model="openedPresetSummary.bar.columns[0].key" class="flex-1">
273
270
  <option value="" disabled selected>{{ $t('Add Column') }}</option>
274
271
  <option value="(none)">{{ $t('None') }}</option>
275
272
  <option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
276
273
  </Dropdown>
277
- <Dropdown v-model="openedPresetSummary.bar.columns[0].format" class="w-[100px]" @change="loadSummary">
274
+ <Dropdown v-model="openedPresetSummary.bar.columns[0].format" class="w-[100px]">
278
275
  <option value="">{{ $t('Default') }}</option>
279
276
  <option value="date">{{ $t('Date') }}</option>
280
277
  </Dropdown>
@@ -287,7 +284,7 @@
287
284
  <div class="p-3">
288
285
  <label class="text-text-400 flex-1">Values</label>
289
286
  <div class="flex flex-row mt-2 gap-2">
290
- <Dropdown v-model="openedPresetSummary.bar.values[0].aggregrate" class="flex-1" @change="loadSummary">
287
+ <Dropdown v-model="openedPresetSummary.bar.values[0].aggregrate" class="flex-1">
291
288
  <option value="" disabled selected>{{ $t('Select') }}</option>
292
289
  <option value="count">{{ $t('Count') }}</option>
293
290
  <option value="max">{{ $t('Max') }}</option>
@@ -304,11 +301,11 @@
304
301
  <div class="p-3">
305
302
  <label class="text-text-400 flex-1">{{ $t('X-axis') }}</label>
306
303
  <div class="flex flex-row mt-2 gap-2">
307
- <Dropdown v-model="openedPresetSummary.line.rows[0].key" class="flex-1" @change="loadSummary">
304
+ <Dropdown v-model="openedPresetSummary.line.rows[0].key" class="flex-1">
308
305
  <option value="" disabled selected>{{ $t('Add Column') }}</option>
309
306
  <option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
310
307
  </Dropdown>
311
- <Dropdown v-model="openedPresetSummary.line.rows[0].format" class="w-[100px]" @change="loadSummary">
308
+ <Dropdown v-model="openedPresetSummary.line.rows[0].format" class="w-[100px]">
312
309
  <option value="">{{ $t('Default') }}</option>
313
310
  <option value="date">{{ $t('Date') }}</option>
314
311
  <option value="month">{{ $t('Month') }}</option>
@@ -321,12 +318,12 @@
321
318
  <div class="p-3">
322
319
  <label class="text-text-400 flex-1">{{$t('Column') }}</label>
323
320
  <div class="flex flex-row mt-2 gap-2">
324
- <Dropdown v-model="openedPresetSummary.line.columns[0].key" class="flex-1" @change="loadSummary">
321
+ <Dropdown v-model="openedPresetSummary.line.columns[0].key" class="flex-1">
325
322
  <option value="" disabled selected>{{ $t('Add Column') }}</option>
326
323
  <option value="(none)">{{ $t('None') }}</option>
327
324
  <option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
328
325
  </Dropdown>
329
- <Dropdown v-model="openedPresetSummary.line.columns[0].format" class="w-[100px]" @change="loadSummary">
326
+ <Dropdown v-model="openedPresetSummary.line.columns[0].format" class="w-[100px]">
330
327
  <option value="">{{ $t('Default') }}</option>
331
328
  <option value="date">{{ $t('Date') }}</option>
332
329
  </Dropdown>
@@ -336,7 +333,7 @@
336
333
  <div class="p-3">
337
334
  <label class="text-text-400 flex-1">Values</label>
338
335
  <div class="flex flex-row mt-2 gap-2">
339
- <Dropdown v-model="openedPresetSummary.line.values[0].aggregrate" class="flex-1" @change="loadSummary">
336
+ <Dropdown v-model="openedPresetSummary.line.values[0].aggregrate" class="flex-1">
340
337
  <option value="" disabled selected>{{ $t('Select') }}</option>
341
338
  <option value="count">{{ $t('Count') }}</option>
342
339
  <option value="max">{{ $t('Max') }}</option>
@@ -353,7 +350,7 @@
353
350
  <div class="p-3 flex flex-row gap-3">
354
351
  <div class="flex-1">
355
352
  <label class="text-text-400 flex-1">Map Type</label>
356
- <Dropdown class="mt-2" v-model="openedPresetSummary.map.mapType" @change="loadSummary">
353
+ <Dropdown class="mt-2" v-model="openedPresetSummary.map.mapType">
357
354
  <option value="" disabled selected>(Default)</option>
358
355
  <option value="heatmap">Heatmap</option>
359
356
  </Dropdown>
@@ -361,7 +358,7 @@
361
358
  <div>
362
359
  <label class="text-text-400 flex-1">Radius</label>
363
360
  <div class="flex flex-row mt-2 gap-2">
364
- <Textbox v-model="openedPresetSummary.map.mapRadius" class="w-[60px]" @blur="loadSummary" @keyup.enter="loadSummary" />
361
+ <Textbox v-model="openedPresetSummary.map.mapRadius" class="w-[60px]" />
365
362
  </div>
366
363
  </div>
367
364
  </div>
@@ -369,7 +366,7 @@
369
366
  <div class="p-3">
370
367
  <label class="text-text-400 flex-1">{{$t('Property') }}</label>
371
368
  <div>
372
- <Dropdown v-model="openedPresetSummary.map.key" class="flex-1" @change="loadSummary">
369
+ <Dropdown v-model="openedPresetSummary.map.key" class="flex-1">
373
370
  <option value="" disabled selected>{{ $t('Select Column') }}</option>
374
371
  <option v-for="column in coordinateColumns" :value="column.key">{{ column.label ?? column.key }}</option>
375
372
  </Dropdown>
@@ -384,7 +381,6 @@
384
381
 
385
382
  </div>
386
383
  </div>
387
-
388
384
  </div>
389
385
  </template>
390
386
 
@@ -400,9 +396,22 @@ export default{
400
396
 
401
397
  methods: {
402
398
 
399
+ addPreset(){
400
+ this.config.presets.push({
401
+ name: "Untitled",
402
+ columns: []
403
+ })
404
+ },
405
+
406
+ removePreset(index){
407
+ if(this.config.presets.length <= 1) return
408
+ this.config.presets.splice(index, 1)
409
+ this.config.presetIdx = this.config.presets[index] ? index : this.config.presets.length - 1
410
+ },
411
+
403
412
  addFilter(key){
404
413
 
405
- const column = this.config.columns[key]
414
+ const column = this.presetColumns.find((_) => _.key === key)
406
415
 
407
416
  if(!this.preset.filters){
408
417
  this.preset.filters = []
@@ -471,6 +480,22 @@ export default{
471
480
  }
472
481
  },
473
482
 
483
+ addSummary(){
484
+
485
+ if(!this.preset.summaries)
486
+ this.preset.summaries = []
487
+
488
+ this.preset.summaries.push({
489
+ title: "New Summary",
490
+ table: { rows: [{}], columns:[{}], values:[{}] },
491
+ bar: { rows: [{}], columns:[{}], values:[{}] },
492
+ line: { rows: [{}], columns:[{}], values:[{}] },
493
+ map: {}
494
+ })
495
+
496
+ this.configs.summaryOpenIdx = this.preset.summaries.length - 1
497
+ },
498
+
474
499
  },
475
500
 
476
501
 
@@ -500,19 +525,40 @@ export default{
500
525
  },
501
526
 
502
527
  presetColumns(){
503
- if(!this.preset.columns) return
504
-
505
- for(let i = 0 ; i < (this.preset.columns).length ; i++){
506
- const presetColumn = this.preset.columns[i]
507
- const column = this.config.columns[presetColumn.key] ?? {}
508
- for(let key in column){
509
- if(!presetColumn[key]){
510
- this.preset.columns[i][key] = column[key]
511
- }
512
- }
513
- }
514
-
515
- return this.preset.columns
528
+ if(!this.preset.columns) return []
529
+
530
+ const presetColumns = {}
531
+ this.preset.columns.forEach((column, _index) => {
532
+ presetColumns[column.key] = {
533
+ ...column,
534
+ _index
535
+ }
536
+ })
537
+
538
+ this.preset.columns = this.config.columns.map((column, index) => {
539
+ const _sort = {
540
+ index: presetColumns[column.key] ? presetColumns[column.key]._index : index,
541
+ isUser: !!presetColumns[column.key]
542
+ }
543
+
544
+ return {
545
+ ...column,
546
+ ...(presetColumns[column.key] ?? {}),
547
+ _sort
548
+ }
549
+ })
550
+ .sort((a, b) => {
551
+ if(a._sort.index < b._sort.index) return -1
552
+ if(a._sort.index > b._sort.index) return 1
553
+ return a._sort.isUser ? 1 : -1
554
+ })
555
+ .map((column) => {
556
+ delete column._sort
557
+ delete column._index
558
+ return column
559
+ })
560
+
561
+ return this.preset.columns
516
562
  },
517
563
 
518
564
  presetAppearances(){
@@ -544,7 +590,6 @@ export default{
544
590
  { text:'Sorts', value:'sort' },
545
591
  { text:'Summary', value:'summary' },
546
592
  ],
547
- presetTab: 'column',
548
593
  presetFilterSelector: null,
549
594
  presetSortSelector: null,
550
595
  viewMode: null,
@@ -558,6 +603,14 @@ export default{
558
603
 
559
604
  watch: {
560
605
 
606
+ config: {
607
+ immediate: true,
608
+ handler(to){
609
+ if(!this.config.presetTab){
610
+ this.config.presetTab = 'column'
611
+ }
612
+ }
613
+ }
561
614
 
562
615
  }
563
616
 
@@ -589,7 +642,7 @@ export default{
589
642
  @apply flex flex-row gap-4;
590
643
  }
591
644
  .comp-md>*:first-child{
592
- @apply w-[200px];
645
+ @apply w-[200px] flex flex-col;
593
646
  }
594
647
  .comp-md>*:last-child{
595
648
  @apply flex-1 flex flex-col;
@@ -3,7 +3,7 @@
3
3
  <Teleport to=".bW9k">
4
4
  <Transition :name="transition" @after-leave="onAfterLeave" @after-enter="$emit('show')">
5
5
  <div v-if="computedState" :class="computedClass" ref="modal" :style="computedStyle">
6
- <form @submit.prevent="$emit('submit')">
6
+ <form v-if="useForm" @submit.prevent="$emit('submit')">
7
7
  <div class="modal-head">
8
8
  <slot name="head"></slot>
9
9
  </div>
@@ -15,6 +15,18 @@
15
15
  </div>
16
16
  <div v-if="isDisabled" :class="$style.overlay"></div>
17
17
  </form>
18
+ <div v-else class="flex-1 min-h-0 flex flex-col relative">
19
+ <div class="modal-head">
20
+ <slot name="head"></slot>
21
+ </div>
22
+ <div :class="modalBodyClass">
23
+ <slot></slot>
24
+ </div>
25
+ <div class="modal-foot">
26
+ <slot name="foot"></slot>
27
+ </div>
28
+ <div v-if="isDisabled" :class="$style.overlay"></div>
29
+ </div>
18
30
  </div>
19
31
  </Transition>
20
32
  </Teleport>
@@ -54,7 +66,11 @@ export default{
54
66
  position: String,
55
67
  state: [ Number, Boolean, String ],
56
68
  transition: { type: String, default: 'slideup' },
57
- width: String
69
+ width: String,
70
+ useForm: {
71
+ type: Boolean,
72
+ default: false
73
+ }
58
74
  },
59
75
 
60
76
  computed:{
@@ -250,4 +266,4 @@ html[data-theme='dark'] .overlay{
250
266
  transform: translate3d(0, 10px, 0);
251
267
  }
252
268
 
253
- </style>
269
+ </style>
@@ -2,7 +2,7 @@
2
2
  <div :class="$style.comp">
3
3
  <div ref="scroller" :class="$style.scroller" :style="scrollerStyle">
4
4
  <div v-if="visibleItems && visibleItems.length > 0" :class="$style.spacer" ref="spacer" :style="spacerStyle">
5
- <div v-for="(item, index) in visibleItems" :key="item" :data-id="item.id"
5
+ <div v-for="(item, index) in visibleItems" :key="item" :data-id="item.id" class="bg-base-500"
6
6
  @click="select(item, index)"
7
7
  :class="[ $style.item, selectedIndex === ((item && item.id) ? item.id : visibleStartIndex + index) ? $style.trSelected : '', item._highlight ? $style.highlight : '' ].join(' ')">
8
8
  <slot name="item" :item="item" :index="index"></slot>
@@ -212,6 +212,7 @@ export default{
212
212
  .spacer{
213
213
  will-change: auto;
214
214
  position: relative;
215
+ @apply divide-y divide-text-50;
215
216
  }
216
217
 
217
218
  .item:hover{
@@ -171,8 +171,7 @@ export default{
171
171
  },
172
172
 
173
173
  visibleColumns(){
174
- const columns = (this.columns ?? []).filter(_ => _.visible)
175
- return columns
174
+ return (this.columns ?? []).filter(_ => _.visible)
176
175
  }
177
176
 
178
177
  },