@mixd-id/web-scaffold 0.1.230406284 → 0.1.230406285

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.230406284",
4
+ "version": "0.1.230406285",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div :class="$style.comp">
3
3
  <input :id="id" type="checkbox" :checked="checked" @change="onChange" :disabled="isDisabled"/>
4
- <label :for="id" :class="labelClass">
4
+ <label :for="id" :class="labelClass" class="whitespace-nowrap text-ellipsis overflow-hidden">
5
5
  <div :class="$style.indicator" v-if="Boolean(showIcon)">
6
6
  <Transition name="checkbox">
7
7
  <div v-if="checked">
@@ -127,7 +127,7 @@ export default {
127
127
  <style module>
128
128
 
129
129
  .comp{
130
- @apply flex items-center;
130
+ @apply flex flex-row;
131
131
  }
132
132
 
133
133
  .comp label{
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="flex flex-col">
3
3
 
4
- <div v-if="readyState === 1"
4
+ <div v-if="readyState >= 1"
5
5
  class="flex-1 flex flex-row">
6
6
 
7
7
  <div v-if="computedPresetMode === 'sidebar' && sidebar.open"
@@ -10,6 +10,7 @@
10
10
 
11
11
  <PresetBar :config="config"
12
12
  class="flex-1"
13
+ :enumCache="enumCache"
13
14
  @apply="load">
14
15
 
15
16
  <template #toolbar>
@@ -25,142 +26,210 @@
25
26
  @mousedown="(e) => $util.dragResize(e, resize1)"></div>
26
27
  </div>
27
28
 
28
- <div class="flex-1 flex flex-col" :class="containerClass">
29
-
30
- <slot name="head"
31
- :preset="preset"
32
- :presetSelector="$refs.presetSelector"
33
- :compPrefix="compPrefix">
34
- <div class="flex flex-col md:flex-row gap-3 md:items-start overflow-hidden leading-tight p-3 md:p-0 md:pl-3 panel-400 md:panel-none border-b-[1px] border-text-50 md:border-none"
35
- :class="headerClass">
36
-
37
- <div class="flex md:flex-1 flex-row items-start gap-3">
38
-
39
- <div class="flex flex-row gap-2 md:gap-3 items-center" ref="title">
40
- <div class="flex flex-col whitespace-nowrap text-ellipsis overflow-hidden">
41
- <div class="cursor-pointer group" @click="$refs.contextMenu.open($refs.title)">
42
- <small class="text-text-400 text-xs">{{ title ?? 'Untitled' }}</small>
43
- <div class="flex flex-row items-baseline gap-1">
44
- <h5 class="whitespace-nowrap relative top-[-2px] text-ellipsis overflow-hidden group-hover:text-primary">
45
- {{ preset.name ?? 'Preset Name' }}
46
- </h5>
47
- <svg width="13" height="13" class="fill-text group-hover:fill-primary relative top-[-1px]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M310.6 246.6l-127.1 128C176.4 380.9 168.2 384 160 384s-16.38-3.125-22.63-9.375l-127.1-128C.2244 237.5-2.516 223.7 2.438 211.8S19.07 192 32 192h255.1c12.94 0 24.62 7.781 29.58 19.75S319.8 237.5 310.6 246.6z"/></svg>
29
+ <div class="flex-1 flex flex-col">
30
+
31
+ <div class="flex-1 flex flex-col" :class="containerClass">
32
+ <slot name="head"
33
+ :preset="preset"
34
+ :presetSelector="$refs.presetSelector"
35
+ :compPrefix="compPrefix">
36
+ <div class="flex flex-col md:flex-row gap-3 md:items-start overflow-hidden leading-tight p-3 md:p-0 md:pl-3 panel-400 md:panel-none border-b-[1px] border-text-50 md:border-none"
37
+ :class="headerClass">
38
+
39
+ <div class="flex md:flex-1 flex-row items-start gap-3">
40
+
41
+ <div class="flex flex-row gap-2 md:gap-3 items-center" ref="title">
42
+ <div class="flex flex-col whitespace-nowrap text-ellipsis overflow-hidden">
43
+ <div class="cursor-pointer group" @click="$refs.contextMenu.open($refs.title)">
44
+ <small class="text-text-400 text-xs">{{ title ?? 'Untitled' }}</small>
45
+ <div class="flex flex-row items-baseline gap-1">
46
+ <h5 class="whitespace-nowrap relative top-[-2px] text-ellipsis overflow-hidden group-hover:text-primary">
47
+ {{ preset.name ?? 'Preset Name' }}
48
+ </h5>
49
+ <svg width="13" height="13" class="fill-text group-hover:fill-primary relative top-[-1px]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M310.6 246.6l-127.1 128C176.4 380.9 168.2 384 160 384s-16.38-3.125-22.63-9.375l-127.1-128C.2244 237.5-2.516 223.7 2.438 211.8S19.07 192 32 192h255.1c12.94 0 24.62 7.781 29.58 19.75S319.8 237.5 310.6 246.6z"/></svg>
50
+ </div>
48
51
  </div>
49
- </div>
50
- <ContextMenu ref="contextMenu" class="panel-400">
51
- <div class="flex flex-col min-w-[300px] divide-y divide-text-50">
52
-
53
- <div class="p-3 flex flex-col gap-1 bg-base-300">
54
- <button v-for="(preset, idx) in config.presets"
55
- class="p-2 px-5 text-left flex flex-row items-center rounded-md"
56
- :class="config.presetIdx === idx ? 'bg-primary-200 text-white' : 'hover:bg-primary-100'"
57
- type="button"
58
- @click="selectPreset(idx)">
59
- <label class="flex-1 pr-12">
60
- {{ preset.name }}
61
- </label>
62
- <div class="p-1">
63
- <div v-if="idx < 10"
64
- class="border-[1px] border-text-300 px-2 text-mono text-xs text-text-400 rounded-lg">
65
- Alt {{ idx + 1 }}
52
+ <ContextMenu ref="contextMenu" class="panel-400">
53
+ <div class="flex flex-col min-w-[300px] divide-y divide-text-50">
54
+
55
+ <div class="p-3 flex flex-col gap-1 bg-base-300">
56
+ <button v-for="(preset, idx) in config.presets"
57
+ class="p-2 px-5 text-left flex flex-row items-center rounded-md"
58
+ :class="config.presetIdx === idx ? 'bg-primary-200 text-white' : 'hover:bg-primary-100'"
59
+ type="button"
60
+ @click="selectPreset(idx)">
61
+ <label class="flex-1 pr-12">
62
+ {{ preset.name }}
63
+ </label>
64
+ <div class="p-1">
65
+ <div v-if="idx < 10"
66
+ class="border-[1px] border-text-300 px-2 text-mono text-xs text-text-400 rounded-lg">
67
+ Alt {{ idx + 1 }}
68
+ </div>
66
69
  </div>
67
- </div>
68
- </button>
69
- </div>
70
+ </button>
71
+ </div>
72
+
73
+ <div class="px-3">
74
+ <button type="button" class="text-primary p-5 text-left w-full" @click="openPreset">Edit</button>
75
+ </div>
70
76
 
71
- <div class="px-3">
72
- <button type="button" class="text-primary p-5 text-left w-full" @click="openPreset">Edit</button>
73
77
  </div>
78
+ </ContextMenu>
79
+ </div>
80
+ </div>
81
+
82
+ <div class="flex-1"></div>
83
+
84
+ <slot name="toolbar"></slot>
85
+ </div>
74
86
 
87
+ <div class="flex flex-row gap-2 gap-1" v-if="Boolean(toolbar)">
88
+ <Textbox class="flex-1 md:w-[240px]" placeholder="Search..." clearable="1"
89
+ v-model="preset.search"
90
+ @clear="delete preset.search; load()"
91
+ @keyup.enter="load">
92
+ <template #start>
93
+ <div class="p-2 pr-0">
94
+ <svg width="16" height="16" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M504.1 471l-134-134C399.1 301.5 415.1 256.8 415.1 208c0-114.9-93.13-208-208-208S-.0002 93.13-.0002 208S93.12 416 207.1 416c48.79 0 93.55-16.91 129-45.04l134 134C475.7 509.7 481.9 512 488 512s12.28-2.344 16.97-7.031C514.3 495.6 514.3 480.4 504.1 471zM48 208c0-88.22 71.78-160 160-160s160 71.78 160 160s-71.78 160-160 160S48 296.2 48 208z"/></svg>
75
95
  </div>
76
- </ContextMenu>
96
+ </template>
97
+ </Textbox>
98
+
99
+ <div v-if="$slots.gridItem"
100
+ class="hidden md:grid grid-cols-2 gap-[1px] border-[1px] border-text-50 rounded-lg">
101
+ <Radio v-model="preset.view"
102
+ name="view"
103
+ value="table"
104
+ :custom="true"
105
+ class="p-2 px-3 rounded-lg rounded-r-none"
106
+ :class="preset.view === 'table' ? 'bg-primary-500' : 'bg-base-500'">
107
+ <svg width="14" height="14" :class="preset.view === 'table' ? 'fill-white' : 'fill-text'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M448 32H64.05C28.7 32 .0492 60.65 .0492 96v320c0 35.35 28.65 64 64 64h383.1c35.35 0 64-28.65 64-64V96C512 60.65 483.4 32 448 32zM224 416H64v-96h160V416zM224 256H64V160h160V256zM448 416h-160v-96h160V416zM448 256h-160V160h160V256z"/></svg>
108
+ </Radio>
109
+ <Radio v-model="preset.view"
110
+ name="view"
111
+ value="grid"
112
+ :custom="true"
113
+ class="p-2 px-3 bg-base-500 rounded-lg rounded-l-none"
114
+ :class="preset.view === 'grid' ? 'bg-primary-500' : 'bg-base-500'">
115
+ <svg width="14" height="14" :class="preset.view === 'grid' ? 'fill-white' : 'fill-text'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M144 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C176 302.3 161.7 288 144 288zM368 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C400 302.3 385.7 288 368 288zM592 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C624 302.3 609.7 288 592 288zM144 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C176 78.33 161.7 64 144 64zM368 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C400 78.33 385.7 64 368 64zM592 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C624 78.33 609.7 64 592 64z"/></svg>
116
+ </Radio>
77
117
  </div>
78
118
  </div>
79
119
 
80
- <div class="flex-1"></div>
81
-
82
- <slot name="toolbar"></slot>
83
120
  </div>
121
+ </slot>
122
+
123
+ <VirtualTable v-if="presetView === 'table'"
124
+ ref="table"
125
+ :columns="columns"
126
+ class="flex-1 rounded-lg panel-400"
127
+ :items="data.items"
128
+ :enumCache="enumCache"
129
+ @scroll-end="loadNext"
130
+ @item-click="onTableItemClick">
131
+
132
+ <template v-for="(_, slot) in headerSlots" #[slot]="{ item, index }">
133
+ <div :class="getHeader(slot.replace('col-', ''))">
134
+ <slot :name="slot" :item="item" :index="index"></slot>
135
+ </div>
136
+ </template>
84
137
 
85
- <div class="flex flex-row gap-2 gap-1" v-if="Boolean(toolbar)">
86
- <Textbox class="flex-1 md:w-[240px]" placeholder="Search..." clearable="1"
87
- v-model="preset.search"
88
- @clear="delete preset.search; load()"
89
- @keyup.enter="load">
90
- <template #start>
91
- <div class="p-2 pr-0">
92
- <svg width="16" height="16" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M504.1 471l-134-134C399.1 301.5 415.1 256.8 415.1 208c0-114.9-93.13-208-208-208S-.0002 93.13-.0002 208S93.12 416 207.1 416c48.79 0 93.55-16.91 129-45.04l134 134C475.7 509.7 481.9 512 488 512s12.28-2.344 16.97-7.031C514.3 495.6 514.3 480.4 504.1 471zM48 208c0-88.22 71.78-160 160-160s160 71.78 160 160s-71.78 160-160 160S48 296.2 48 208z"/></svg>
138
+ <template v-for="(_, slot) in contentSlots" #[slot]="{ item, index }">
139
+ <slot :name="slot" :item="item" :index="index"></slot>
140
+ </template>
141
+
142
+ </VirtualTable>
143
+
144
+ <VirtualGrid v-else-if="presetView === 'grid'"
145
+ ref="grid"
146
+ :items="dataItems"
147
+ :column="computedGridColumn"
148
+ :class="gridClass"
149
+ @scroll-end="loadNext"
150
+ :container-class="`${gridContainerClass}`"
151
+ :config="config">
152
+ <template #item="{ item }">
153
+ <slot name="gridItem" :item="item" :enumCache="enumCache">
154
+ <div class="flex flex-row panel-400 rounded-lg overflow-hidden md:rounded-lg overflow-hidden p-3 gap-3">
155
+ <div>
156
+ <Image :src="item.imageUrl" class="bg-text-50 w-[64px] h-[64px] rounded-lg" />
93
157
  </div>
94
- </template>
95
- </Textbox>
96
-
97
- <div v-if="$slots.gridItem"
98
- class="hidden md:grid grid-cols-2 gap-[1px] border-[1px] border-text-50 rounded-lg">
99
- <Radio v-model="preset.view"
100
- name="view"
101
- value="table"
102
- :custom="true"
103
- class="p-2 px-3 rounded-lg rounded-r-none"
104
- :class="preset.view === 'table' ? 'bg-primary-500' : 'bg-base-500'">
105
- <svg width="14" height="14" :class="preset.view === 'table' ? 'fill-white' : 'fill-text'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M448 32H64.05C28.7 32 .0492 60.65 .0492 96v320c0 35.35 28.65 64 64 64h383.1c35.35 0 64-28.65 64-64V96C512 60.65 483.4 32 448 32zM224 416H64v-96h160V416zM224 256H64V160h160V256zM448 416h-160v-96h160V416zM448 256h-160V160h160V256z"/></svg>
106
- </Radio>
107
- <Radio v-model="preset.view"
108
- name="view"
109
- value="grid"
110
- :custom="true"
111
- class="p-2 px-3 bg-base-500 rounded-lg rounded-l-none"
112
- :class="preset.view === 'grid' ? 'bg-primary-500' : 'bg-base-500'">
113
- <svg width="14" height="14" :class="preset.view === 'grid' ? 'fill-white' : 'fill-text'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M144 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C176 302.3 161.7 288 144 288zM368 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C400 302.3 385.7 288 368 288zM592 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C624 302.3 609.7 288 592 288zM144 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C176 78.33 161.7 64 144 64zM368 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C400 78.33 385.7 64 368 64zM592 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C624 78.33 609.7 64 592 64z"/></svg>
114
- </Radio>
115
- </div>
116
- </div>
158
+ <div class="flex-1 flex flex-col leading-tight">
159
+ <small>{{ item.code }}</small>
160
+ <strong>{{ item.name }}</strong>
161
+ </div>
162
+ </div>
163
+ </slot>
164
+ </template>
165
+ </VirtualGrid>
166
+
167
+ <PresetSelector ref="presetSelector" :config="config" @select="load" />
168
+ </div>
169
+
170
+ <div v-if="extBar.open && pivotEnabled"
171
+ :style="extStyle" class="border-t-[1px] border-text-50 flex flex-col relative md:p-5">
172
+
173
+ <div :class="$style.resize2"
174
+ @mousedown="(e) => $util.dragResize(e, resize2)">
175
+ <button type="button" @click.prevent="extBar.open = false"
176
+ :class="$style.extClose">
177
+ <svg width="14"
178
+ height="14"
179
+ class="fill-text-300 cursor-pointer"
180
+ xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M432.6 209.3l-191.1 183.1C235.1 397.8 229.1 400 224 400s-11.97-2.219-16.59-6.688L15.41 209.3C5.814 200.2 5.502 184.1 14.69 175.4c9.125-9.625 24.38-9.938 33.91-.7187L224 342.8l175.4-168c9.5-9.219 24.78-8.906 33.91 .7187C442.5 184.1 442.2 200.2 432.6 209.3z"/></svg>
181
+ </button>
182
+ </div>
117
183
 
184
+ <div v-if="readyState === 3" class="flex-1 flex items-center justify-center">
185
+ <svg class="animate-spin" width="36" height="36" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
118
186
  </div>
119
- </slot>
120
187
 
121
- <VirtualTable v-if="presetView === 'table'"
122
- ref="table"
123
- :columns="columns"
124
- class="flex-1 rounded-lg panel-400"
125
- :items="data.items"
126
- @scroll-end="loadNext">
188
+ <VirtualTable v-else-if="presetView === 'table'"
189
+ ref="table"
190
+ :columns="preset.columns"
191
+ class="flex-1 rounded-lg panel-400"
192
+ :items="data.extItems"
193
+ :enumCache="enumCache"
194
+ @scroll-end="loadExtNext">
195
+
196
+ <template v-for="(_, slot) in headerSlots" #[slot]="{ item, index }">
197
+ <div :class="getHeader(slot.replace('col-', ''))">
198
+ <slot :name="slot" :item="item" :index="index"></slot>
199
+ </div>
200
+ </template>
127
201
 
128
- <template v-for="(_, slot) in headerSlots" #[slot]="{ item, index }">
129
- <div :class="getHeader(slot.replace('col-', ''))">
202
+ <template v-for="(_, slot) in contentSlots" #[slot]="{ item, index }">
130
203
  <slot :name="slot" :item="item" :index="index"></slot>
131
- </div>
132
- </template>
133
-
134
- <template v-for="(_, slot) in contentSlots" #[slot]="{ item, index }">
135
- <slot :name="slot" :item="item" :index="index"></slot>
136
- </template>
137
-
138
- </VirtualTable>
139
-
140
- <VirtualGrid v-else-if="presetView === 'grid'"
141
- ref="grid"
142
- :items="dataItems"
143
- :column="computedGridColumn"
144
- :class="gridClass"
145
- @scroll-end="loadNext"
146
- :container-class="`${gridContainerClass}`"
147
- :config="config">
148
- <template #item="{ item }">
149
- <slot name="gridItem" :item="item">
150
- <div class="flex flex-row panel-400 rounded-lg overflow-hidden md:rounded-lg overflow-hidden p-3 gap-3">
151
- <div>
152
- <Image :src="item.imageUrl" class="bg-text-50 w-[64px] h-[64px] rounded-lg" />
153
- </div>
154
- <div class="flex-1 flex flex-col leading-tight">
155
- <small>{{ item.code }}</small>
156
- <strong>{{ item.name }}</strong>
204
+ </template>
205
+
206
+ </VirtualTable>
207
+
208
+ <VirtualGrid v-else-if="presetView === 'grid'"
209
+ ref="grid"
210
+ :items="data.extItems"
211
+ :column="computedGridColumn"
212
+ :class="gridClass"
213
+ @scroll-end="loadExtNext"
214
+ :container-class="`${gridContainerClass}`"
215
+ :config="config">
216
+ <template #item="{ item }">
217
+ <slot name="gridItem" :item="item">
218
+ <div class="flex flex-row panel-400 rounded-lg overflow-hidden md:rounded-lg overflow-hidden p-3 gap-3">
219
+ <div>
220
+ <Image :src="item.imageUrl" class="bg-text-50 w-[64px] h-[64px] rounded-lg" />
221
+ </div>
222
+ <div class="flex-1 flex flex-col leading-tight">
223
+ <small>{{ item.code }}</small>
224
+ <strong>{{ item.name }}</strong>
225
+ </div>
157
226
  </div>
158
- </div>
159
- </slot>
160
- </template>
161
- </VirtualGrid>
227
+ </slot>
228
+ </template>
229
+ </VirtualGrid>
230
+
231
+ </div>
162
232
 
163
- <PresetSelector ref="presetSelector" :config="config" @select="load" />
164
233
  </div>
165
234
 
166
235
  </div>
@@ -180,6 +249,10 @@ import PresetBar from "../widgets/PresetBar.vue";
180
249
 
181
250
  export default{
182
251
 
252
+ emits: [ 'after-load', 'open-preset', 'signal', 'pivot-item-click' ],
253
+
254
+ inject: [ 'socket', 'toast' ],
255
+
183
256
  props: {
184
257
  config: {
185
258
  type: Object,
@@ -231,9 +304,103 @@ export default{
231
304
  .join(' ')
232
305
  },
233
306
 
307
+ generatePivotColumns(items){
308
+ if(!this.preset.pivot || !this.preset.pivot.enabled) return
309
+ if(!Array.isArray(items) && items.length < 1) return
310
+
311
+ const tableColumns = (this.preset.columns ?? this.config.columns)
312
+ .reduce((cur, obj) => {
313
+ cur[obj.key] = obj
314
+ return cur
315
+ }, {})
316
+
317
+ const currentPivotColumns = [ ...(this.preset.pivot.columns ?? []) ]
318
+ .reduce((cur, obj) => {
319
+ cur[obj.key] = obj
320
+ return cur
321
+ }, {})
322
+
323
+ const pivotColumns = []
324
+
325
+ for(let row of this.preset.pivot.rows){
326
+
327
+ let format
328
+ switch(row.aggregrate){
329
+ case 'year':
330
+ format = 'YYYY'
331
+ break
332
+
333
+ case 'month':
334
+ format = 'MMM YYYY'
335
+ break
336
+
337
+ case 'date':
338
+ format = 'D MMM YYYY'
339
+ break
340
+ }
341
+
342
+ if(currentPivotColumns[row.key]){
343
+ Object.assign(currentPivotColumns[row.key], {
344
+ format,
345
+ visible: true
346
+ })
347
+
348
+ pivotColumns.push(currentPivotColumns[row.key])
349
+ }
350
+ else{
351
+ pivotColumns.push({
352
+ ...tableColumns[row.key],
353
+ format,
354
+ visible: true
355
+ })
356
+ }
357
+ }
358
+
359
+ for(let key in items[0]){
360
+ if(key.startsWith('_')){
361
+ let [ k1, ...k2 ] = key.substring(1).split('-')
362
+ k2 = k2.join('-')
363
+
364
+ let label = k2.replace(/_/gi, ' ')
365
+ const column = tableColumns[k1]
366
+ if(column){
367
+ switch(column.type){
368
+ case 'enum':
369
+ label = (column.typeParams ?? []).find(_ => `${_.value}` === `${k2}`)?.text ?? label
370
+ break
371
+ }
372
+ }
373
+
374
+ if(currentPivotColumns[key]){
375
+ Object.assign(currentPivotColumns[key], {
376
+ visible: true
377
+ })
378
+
379
+ pivotColumns.push(currentPivotColumns[key])
380
+ }
381
+ else{
382
+ pivotColumns.push({
383
+ key,
384
+ label,
385
+ label2: (((tableColumns[k1] ?? {}).label ?? '') + ': ' + label).trim(),
386
+ type: 'number',
387
+ })
388
+ }
389
+ }
390
+ }
391
+
392
+ (this.preset.pivot.columns ?? []).filter(_ => !pivotColumns.find(i => i.key === _.key))
393
+ .forEach(_ => {
394
+ _.visible = false
395
+ pivotColumns.push(_)
396
+ })
397
+
398
+ this.preset.pivot.columns = pivotColumns
399
+
400
+ },
401
+
234
402
  load(){
235
403
  if(this.src){
236
-
237
404
  if(!this.preset.columns){
238
405
  this.preset.columns = JSON.parse(JSON.stringify(this.config.columns))
239
406
  }
@@ -244,54 +411,15 @@ export default{
244
411
  })
245
412
  .then(data => {
246
413
 
247
- // Handle non-aggregrated pivot values
248
- if((this.preset.pivot ?? {}).enabled && data.items.length > 0){
249
-
250
- const tableColumns = (this.preset.columns ?? this.config.columns).reduce((cur, obj) => {
251
- cur[obj.key] = obj
252
- return cur
253
- }, {})
254
-
255
- const columns = []
256
- for(let key in data.items[0]){
257
- if(key.startsWith('_')){
258
- let [ k1, ...k2 ] = key.substring(1).split('-')
259
- k2 = k2.join('-')
260
-
261
- let label = k2.replace(/_/gi, ' ')
262
- const column = tableColumns[k1]
263
- if(column){
264
- switch(column.type){
265
- case 'enum':
266
- label = (column.typeParams ?? []).find(_ => `${_.value}` === `${k2}`)?.text ?? label
267
- break
268
- }
269
- }
270
-
271
- if(!tableColumns[key]){
272
- columns.push({
273
- key,
274
- label,
275
- label2: (((tableColumns[k1] ?? {}).label ?? '') + ': ' + label).trim(),
276
- type: 'number',
277
- })
278
- }
279
- }
280
- }
281
-
282
- if(!Array.isArray(this.preset.columns)){
283
- this.preset.columns = JSON.parse(JSON.stringify(this.config.columns))
284
- }
285
- this.preset.columns.push(...columns)
286
- }
287
-
288
- this.loadEnums()
289
-
414
+ this.generatePivotColumns(data.items)
415
+ this.loadEnums(data.items)
290
416
  Object.assign(this.data, data ?? {})
291
-
292
417
  this.$emit('after-load')
418
+
419
+ })
420
+ .catch(err => {
421
+ this.toast(err)
293
422
  })
294
- .catch(err => this.toast(err))
295
423
  }
296
424
  return new Promise(resolve => resolve())
297
425
  },
@@ -312,6 +440,7 @@ export default{
312
440
  //console.log('next id', data.items[0].id)
313
441
 
314
442
  this.data.items.push(...data.items)
443
+ this.loadEnums(data.items)
315
444
  })
316
445
  .catch(err => this.toast(err))
317
446
  }
@@ -347,37 +476,94 @@ export default{
347
476
  }
348
477
  },
349
478
 
350
- loadEnums(){
479
+ loadEnums(items){
351
480
 
352
481
  for(let i in this.preset.columns){
353
482
  const column = this.preset.columns[i]
354
483
 
355
484
  if(column.enumSrc) {
356
- const [ src, key, attr ] = column.enumSrc.split(':')
357
- this.socket.send(src, {
358
- columns: [
359
- { key:attr, visible:true }
360
- ],
361
- filters: [
362
- {
363
- key:key, value: [ { operator:'in', value:[ 1 ] } ]
364
- }
365
- ]
366
- })
367
- .then(res => {
368
- this.preset.columns[i].typeParams = (res.items ?? []).map(_ => {
369
- return {
370
- text: _[attr] ?? _[key],
371
- value: _[key]
485
+
486
+ if(!this.enumCache[column.key])
487
+ this.enumCache[column.key] = {}
488
+
489
+ for(let item of items){
490
+ if(item[column.key] && !this.enumCache[column.key][item[column.key]]){
491
+ this.enumCache[column.key][item[column.key]] = { value:item[column.key] }
492
+ }
493
+ }
494
+
495
+ const reqItems = Object.values(this.enumCache[column.key]).filter(_ => !_.text).map(_ => _.value)
496
+
497
+ if(reqItems.length > 0){
498
+
499
+ const [ src, key, attr ] = column.enumSrc.split(':')
500
+ this.socket.send(src, {
501
+ columns: [
502
+ { key:attr, visible:true }
503
+ ],
504
+ itemsPerPage: 1000,
505
+ filters: [
506
+ {
507
+ key:key, value: [ { operator:'in', value:reqItems } ]
372
508
  }
373
- })
509
+ ]
374
510
  })
375
- .catch(err => this.log('Load enums error', err))
511
+ .then(res => {
512
+ (res.items ?? []).forEach(_ => {
513
+ this.enumCache[column.key][_[key]] = {
514
+ text: _[attr] ?? _[key],
515
+ value: _[key],
516
+ }
517
+ })
518
+
519
+ for(let key in this.enumCache[column.key]){
520
+ if(!this.enumCache[column.key][key].text)
521
+ this.enumCache[column.key][key].text = key
522
+ }
523
+ })
524
+ .catch(err => this.log('Load enums error', err))
525
+ }
376
526
  }
377
527
  }
378
528
 
379
529
  },
380
530
 
531
+ loadExt(){
532
+ if(!this.preset.filters || !this.extBar.open) return
533
+
534
+ this.readyState = 3
535
+ return this.socket.send(this.src, {
536
+ ...this.preset,
537
+ filters: [
538
+ ...(this.preset.filters ?? []),
539
+ ...(this.extBar.filters ?? [])
540
+ ],
541
+ pivot: null
542
+ })
543
+ .then((res) => {
544
+ this.data.extItems = res.items
545
+ })
546
+ .finally(_ => this.readyState = 1)
547
+ },
548
+
549
+ loadExtNext(){
550
+ if(!this.preset.filters) return
551
+
552
+ return this.socket.send(this.src, {
553
+ ...this.preset,
554
+ filters: [
555
+ ...(this.preset.filters ?? []),
556
+ ...(this.extBar.filters ?? [])
557
+ ],
558
+ pivot: null,
559
+ afterItem: this.data.extItems[this.data.extItems.length - 1],
560
+ })
561
+ .then(data => {
562
+ this.data.extItems.push(...data.items)
563
+ })
564
+ .catch(err => this.toast(err))
565
+ },
566
+
381
567
  resize1(w){
382
568
  if(this.config.sidebar.width + w >= 270 && this.config.sidebar.width + w <= 600){
383
569
  this.config.sidebar.width += w
@@ -458,11 +644,49 @@ export default{
458
644
  this.$emit('signal', event, items)
459
645
  },
460
646
 
647
+ onTableItemClick(item, column){
648
+ if((this.preset.pivot ?? {}).enabled && (column.key ?? '').startsWith('_')){
649
+
650
+ const filters = []
651
+
652
+ for(let idx in this.preset.pivot.rows){
653
+ filters.push({
654
+ key: this.preset.pivot.rows[idx].key,
655
+ value: [{
656
+ operator: '=',
657
+ value: item[this.preset.pivot.rows[idx].key]
658
+ }]
659
+ })
660
+ }
661
+
662
+ const [ kk, vv ] = column.key.split('-')
663
+ if(![ 'count' ].includes(vv)){
664
+ filters.push({
665
+ key: kk.substring(1),
666
+ value: [{
667
+ operator: '=',
668
+ value: vv
669
+ }]
670
+ })
671
+ }
672
+
673
+ this.extBar.open = true
674
+ this.extBar.filters = filters
675
+ this.loadExt()
676
+ }
677
+ },
678
+
461
679
  resize(){
462
680
  this.$refs.table ? this.$refs.table.resize() : null
463
681
  this.$refs.grid ? this.$refs.grid.resize() : null
464
682
  },
465
683
 
684
+ resize2(_, h){
685
+ if(this.extBar.height - h >= 100 && this.extBar.height - h <= 600){
686
+ this.extBar.height -= h
687
+ }
688
+ },
689
+
466
690
  onKeyDown(e){
467
691
 
468
692
  if(e.altKey){
@@ -490,10 +714,6 @@ export default{
490
714
 
491
715
  components: {PresetBar, PresetSelector, VirtualGrid, VirtualTable},
492
716
 
493
- emits: [ 'after-load', 'open-preset', 'signal' ],
494
-
495
- inject: [ 'socket', 'toast' ],
496
-
497
717
  mounted(){
498
718
  this.$addResizeListener(this.$el, () => this.compPrefix = this.$util.calculateMediaPrefix(this.$el.clientWidth))
499
719
 
@@ -501,6 +721,7 @@ export default{
501
721
  .then(() => {
502
722
  this.readyState = 1
503
723
  this.load()
724
+ this.loadExt()
504
725
  })
505
726
 
506
727
  if(this.subscribeKey){
@@ -525,6 +746,8 @@ export default{
525
746
 
526
747
  columns(){
527
748
  if((this.preset.pivot ?? {}).enabled){
749
+ return this.preset.pivot.columns ?? []
750
+
528
751
 
529
752
  const tableColumns = groupBy(this.preset.columns ?? this.config.columns, 'key')
530
753
 
@@ -636,6 +859,26 @@ export default{
636
859
  }
637
860
  },
638
861
 
862
+ extBar(){
863
+ if(!this.config.extbar)
864
+ this.config.extbar = {
865
+ open: false,
866
+ height: Math.round(window.innerHeight / 2.2)
867
+ }
868
+
869
+ return this.config.extbar
870
+ },
871
+
872
+ extStyle(){
873
+ return {
874
+ height: this.extBar.height + "px"
875
+ }
876
+ },
877
+
878
+ pivotEnabled(){
879
+ return ((this.preset ?? {}).pivot ?? {}).enabled
880
+ }
881
+
639
882
  },
640
883
 
641
884
  data(){
@@ -646,6 +889,8 @@ export default{
646
889
  },
647
890
  observer: null,
648
891
  compPrefix: '',
892
+ enumCache: {},
893
+ extItems: null,
649
894
  }
650
895
  },
651
896
 
@@ -670,4 +915,12 @@ export default{
670
915
  @apply w-[3px] cursor-ew-resize absolute top-0 right-0 bottom-0;
671
916
  }
672
917
 
918
+ .resize2{
919
+ @apply h-[3px] cursor-n-resize absolute top-0 left-0 right-0 flex items-center justify-center;
920
+ }
921
+
922
+ .extClose{
923
+ @apply w-[21px] h-[21px] rounded-full bg-base-300 flex items-center justify-center hover:bg-red-600;
924
+ }
925
+
673
926
  </style>
@@ -5,7 +5,7 @@
5
5
  <div v-if="type === 'date'" class="flex-1 flex flex-row gap-2">
6
6
  <div class="flex-1 flex flex-row gap-2">
7
7
  <Dropdown v-model="value.operator"
8
- class="w-[100px]"
8
+ :class="![ 'yesterday', 'today', 'thisWeek', 'thisMonth', 'lastMonth', 'thisYear' ].includes(value.operator) ? 'w-[100px]' : 'w-full'"
9
9
  @change="apply">
10
10
  <option value="=">=</option>
11
11
  <option value=">">&gt;</option>
@@ -17,9 +17,10 @@
17
17
  <option value="today">Today</option>
18
18
  <option value="thisWeek">This Week</option>
19
19
  <option value="thisMonth">This Month</option>
20
+ <option value="lastMonth">Last Month</option>
20
21
  <option value="thisYear">This Year</option>
21
22
  </Dropdown>
22
- <div v-if="![ 'yesterday', 'today', 'thisWeek', 'thisMonth', 'thisYear' ].includes(value.operator)"
23
+ <div v-if="![ 'yesterday', 'today', 'thisWeek', 'thisMonth', 'lastMonth', 'thisYear' ].includes(value.operator)"
23
24
  class="flex-1 flex flex-row gap-2">
24
25
  <Datepicker class="flex-1"
25
26
  mode="popup"
@@ -57,16 +58,16 @@
57
58
  <Radio :name="value.key" :value="false" v-model="value.value" @change="apply">False</Radio>
58
59
  </div>
59
60
 
60
- <div v-else-if="type === 'enum'" class="flex flex-row gap-2">
61
+ <div v-else-if="type === 'enum' && Array.isArray(cTypeParams) && cTypeParams.length > 0"
62
+ class="flex flex-col gap-1">
61
63
 
62
- <div class="flex flex-col gap-2">
63
- <Checkbox v-for="param in typeParams"
64
- v-model="cValue.value"
65
- :value="param.value"
66
- @change="apply">
67
- {{ param.text }}
68
- </Checkbox>
69
- </div>
64
+ <Checkbox v-for="param in cTypeParams"
65
+ v-model="cValue.value"
66
+ class="flex-1"
67
+ :value="param.value"
68
+ @change="apply">
69
+ {{ param.text }}
70
+ </Checkbox>
70
71
 
71
72
  </div>
72
73
 
@@ -119,12 +120,21 @@ export default{
119
120
 
120
121
  value: Object,
121
122
 
123
+ enumCache: Object,
124
+
122
125
  },
123
126
 
124
127
  computed:{
125
128
 
129
+ cTypeParams(){
130
+ if(this.enumCache && this.column && this.enumCache[this.column.key]){
131
+ return Object.values(this.enumCache[this.column.key])
132
+ }
133
+ return this.typeParams
134
+ },
135
+
126
136
  valueRequired(){
127
- return (!(['yesterday', 'today', 'thisWeek', 'thisMonth', 'thisYear'].includes(this.value.operator) ||
137
+ return (!(['yesterday', 'today', 'thisWeek', 'thisMonth', 'lastMonth', 'thisYear'].includes(this.value.operator) ||
128
138
  this.type === 'boolean'));
129
139
  },
130
140
 
@@ -35,7 +35,8 @@
35
35
  @click="select(item, index)"
36
36
  :class="trClass(item, index)">
37
37
  <td v-for="column in visibleColumns"
38
- :class="tdClass(item, column)">
38
+ :class="tdClass(item, column)"
39
+ @click="$emit('item-click', item, column)">
39
40
  <slot v-if="$slots[column.key]" :name="column.key" :column="column" :item="item" :index="visibleStartIndex + index"></slot>
40
41
  <slot v-else-if="$slots.default" name="default" :column="column" :item="item" :index="visibleStartIndex + index"></slot>
41
42
  <div v-else :class="columnClass(column)" v-html="formatColumn(item, column)"></div>
@@ -86,11 +87,13 @@ const _DEFAULT_COLUMN_WIDTH = '100px'
86
87
 
87
88
  export default{
88
89
 
89
- emits: [ 'scroll-end' ],
90
+ emits: [ 'scroll-end', 'item-click' ],
90
91
 
91
92
  props:{
92
93
  columns: Array,
93
94
 
95
+ enumCache: Object,
96
+
94
97
  itemClass: String,
95
98
 
96
99
  items: Array,
@@ -105,7 +108,7 @@ export default{
105
108
  appearances: {
106
109
  type: Object,
107
110
  default: {}
108
- }
111
+ },
109
112
  },
110
113
 
111
114
  data(){
@@ -397,8 +400,18 @@ export default{
397
400
  break
398
401
 
399
402
  case 'enum':
400
- const enumObj = (column.typeParams ?? []).filter((_) => _.value === value).pop()
401
- text = enumObj ? enumObj.text : value
403
+ let enumText
404
+
405
+ if(Array.isArray(column.typeParams)){
406
+ const typeParam = (column.typeParams ?? []).filter((_) => _.value === value).pop()
407
+ enumText = typeParam && typeParam.text ? typeParam.text : enumText
408
+ }
409
+
410
+ if(this.enumCache && this.enumCache[column.key] && this.enumCache[column.key][value]){
411
+ enumText = this.enumCache[column.key][value].text ?? enumText
412
+ }
413
+
414
+ text = enumText ? enumText : text
402
415
  break
403
416
  }
404
417
 
@@ -99,6 +99,22 @@ const getValue = (filter, opt) => {
99
99
  }
100
100
  break
101
101
 
102
+ case 'lastMonth':
103
+ withoutKey ?
104
+ whereObj = {
105
+ [Op.between]: [
106
+ dayjs().subtract(1, 'month').startOf('month').format('YYYY-MM-DD 00:00:00'),
107
+ dayjs().subtract(1, 'month').endOf('month').format('YYYY-MM-DD 23:59:59'),
108
+ ]
109
+ } :
110
+ whereObj[key] = {
111
+ [Op.between]: [
112
+ dayjs().subtract(1, 'month').startOf('month').format('YYYY-MM-DD 00:00:00'),
113
+ dayjs().subtract(1, 'month').endOf('month').format('YYYY-MM-DD 23:59:59'),
114
+ ]
115
+ }
116
+ break
117
+
102
118
  case 'thisYear':
103
119
  withoutKey ?
104
120
  whereObj = {
@@ -770,6 +786,8 @@ const pivotToSequelizeWhere = async (pivot, opt) => {
770
786
  }
771
787
  }
772
788
 
789
+ attributes.push([ literal(`1`), 'isPivot' ])
790
+
773
791
  return {
774
792
  attributes,
775
793
  group,
@@ -116,11 +116,16 @@
116
116
 
117
117
  <div v-for="(filter, idx) in preset.filters">
118
118
 
119
- <div class="flex flex-row items-start gap-2">
120
- <Checkbox v-model="filter.enabled" :default="true" />
119
+ <div class="flex flex-row items-start">
120
+ <Checkbox v-model="filter.enabled" :default="true" @change="apply" />
121
121
  <div class="flex-1 flex flex-col gap-3">
122
- <div class="flex-1 flex flex-row gap-3">
123
- <strong class="flex-1">{{ column(filter.key).label ?? filter.key }}</strong>
122
+ <div class="flex-1 flex flex-row gap-2">
123
+ <strong class="flex-1 cursor-pointer" @click="filter._collapsed = !filter._collapsed">
124
+ <svg class="inline fill-text transition-transform relative top-[-1px]"
125
+ :class="filter._collapsed === true ? `` : `rotate-90`"
126
+ width="11" height="11" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M118.6 105.4l128 127.1C252.9 239.6 256 247.8 256 255.1s-3.125 16.38-9.375 22.63l-128 127.1c-9.156 9.156-22.91 11.9-34.88 6.943S64 396.9 64 383.1V128c0-12.94 7.781-24.62 19.75-29.58S109.5 96.23 118.6 105.4z"/></svg>
127
+ {{ column(filter.key).label ?? filter.key }}
128
+ </strong>
124
129
  <select v-if="filter.value.length > 1"
125
130
  v-model="filter.modifier"
126
131
  class="appearance-none bg-text-50 min-w-[60px] text-text-400 px-2 outline-none"
@@ -128,34 +133,40 @@
128
133
  <option value="and">And</option>
129
134
  <option value="or">Or</option>
130
135
  </select>
131
- <button type="button" @click="filter.value.push({})">
136
+ <button v-if="typeOf(column(filter.key).type) !== 'enum' && filter._collapsed !== true" type="button" @click="filter.value.push({})">
132
137
  <svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M432 256C432 269.3 421.3 280 408 280h-160v160c0 13.25-10.75 24.01-24 24.01S200 453.3 200 440v-160h-160c-13.25 0-24-10.74-24-23.99C16 242.8 26.75 232 40 232h160v-160c0-13.25 10.75-23.99 24-23.99S248 58.75 248 72v160h160C421.3 232 432 242.8 432 256z"/></svg>
133
138
  </button>
134
139
  <button type="button" class="p-1" @click="preset.filters.splice(idx, 1); apply()">
135
140
  <svg width="14" height="14" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M193.94 256L296.5 153.44l21.15-21.15c3.12-3.12 3.12-8.19 0-11.31l-22.63-22.63c-3.12-3.12-8.19-3.12-11.31 0L160 222.06 36.29 98.34c-3.12-3.12-8.19-3.12-11.31 0L2.34 120.97c-3.12 3.12-3.12 8.19 0 11.31L126.06 256 2.34 379.71c-3.12 3.12-3.12 8.19 0 11.31l22.63 22.63c3.12 3.12 8.19 3.12 11.31 0L160 289.94 262.56 392.5l21.15 21.15c3.12 3.12 8.19 3.12 11.31 0l22.63-22.63c3.12-3.12 3.12-8.19 0-11.31L193.94 256z"/></svg>
136
141
  </button>
137
142
  </div>
138
- <div v-if="Array.isArray(filter.value)" class="flex flex-col gap-2">
139
- <div class="flex flex-row" v-for="(filterVal, filterIdx) in filter.value">
140
- <PresetSelectorFilterItem
141
- class="flex-1"
142
- :type="typeOf(column(filter.key).type)"
143
- :typeParams="typeParamsOf(column(filter.key))"
144
- :value="filterVal"
145
- @change="apply()" />
146
-
147
- <button v-if="filter.value.length > 1"
148
- type="button"
149
- class="p-1"
150
- @click="filter.value.splice(filterIdx, 1); apply()">
151
- <svg width="14" height="14" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M193.94 256L296.5 153.44l21.15-21.15c3.12-3.12 3.12-8.19 0-11.31l-22.63-22.63c-3.12-3.12-8.19-3.12-11.31 0L160 222.06 36.29 98.34c-3.12-3.12-8.19-3.12-11.31 0L2.34 120.97c-3.12 3.12-3.12 8.19 0 11.31L126.06 256 2.34 379.71c-3.12 3.12-3.12 8.19 0 11.31l22.63 22.63c3.12 3.12 8.19 3.12 11.31 0L160 289.94 262.56 392.5l21.15 21.15c3.12 3.12 8.19 3.12 11.31 0l22.63-22.63c3.12-3.12 3.12-8.19 0-11.31L193.94 256z"/></svg>
152
- </button>
143
+ <div v-if="filter._collapsed !== true">
144
+ <div v-if="Array.isArray(filter.value)" class="flex flex-col gap-2">
145
+ <div class="flex flex-row" v-for="(filterVal, filterIdx) in filter.value">
146
+ <PresetSelectorFilterItem
147
+ class="flex-1"
148
+ :column="column(filter.key)"
149
+ :type="typeOf(column(filter.key).type)"
150
+ :typeParams="typeParamsOf(column(filter.key))"
151
+ :value="filterVal"
152
+ :enumCache="enumCache"
153
+ @change="apply()" />
154
+
155
+ <button v-if="filter.value.length > 1"
156
+ type="button"
157
+ class="p-1"
158
+ @click="filter.value.splice(filterIdx, 1); apply()">
159
+ <svg width="14" height="14" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M193.94 256L296.5 153.44l21.15-21.15c3.12-3.12 3.12-8.19 0-11.31l-22.63-22.63c-3.12-3.12-8.19-3.12-11.31 0L160 222.06 36.29 98.34c-3.12-3.12-8.19-3.12-11.31 0L2.34 120.97c-3.12 3.12-3.12 8.19 0 11.31L126.06 256 2.34 379.71c-3.12 3.12-3.12 8.19 0 11.31l22.63 22.63c3.12 3.12 8.19 3.12 11.31 0L160 289.94 262.56 392.5l21.15 21.15c3.12 3.12 8.19 3.12 11.31 0l22.63-22.63c3.12-3.12 3.12-8.19 0-11.31L193.94 256z"/></svg>
160
+ </button>
161
+ </div>
153
162
  </div>
163
+ <PresetSelectorFilterItem v-else
164
+ :column="column(filter.key)"
165
+ :type="typeOf(column(filter.key).type)"
166
+ :value="filter"
167
+ :enumCache="enumCache"
168
+ @change="apply()" />
154
169
  </div>
155
- <PresetSelectorFilterItem v-else
156
- :type="typeOf(column(filter.key).type)"
157
- :value="filter"
158
- @change="apply()" />
159
170
  </div>
160
171
  </div>
161
172
 
@@ -453,7 +464,7 @@
453
464
 
454
465
  <div>
455
466
  <div class="flex flex-row items-center gap-2">
456
- <label>Rows</label>
467
+ <label>Groups</label>
457
468
  <button type="button" @click="$refs.columnSelector.open(obj => addPivot('rows', obj))">
458
469
  <svg width="16" height="16" class="fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M432 256C432 269.3 421.3 280 408 280h-160v160c0 13.25-10.75 24.01-24 24.01S200 453.3 200 440v-160h-160c-13.25 0-24-10.74-24-23.99C16 242.8 26.75 232 40 232h160v-160c0-13.25 10.75-23.99 24-23.99S248 58.75 248 72v160h160C421.3 232 432 242.8 432 256z"/></svg>
459
470
  </button>
@@ -468,7 +479,8 @@
468
479
  <svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M496 288H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm0-128H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16z"/></svg>
469
480
  </div>
470
481
  <div class="flex-1 flex flex-row gap-3">
471
- <strong class="flex-1 cursor-pointer" @click="$refs.columnSelector.open(obj => Object.assign(item, obj))">
482
+ <strong class="flex-1 cursor-pointer text-ellipsis overflow-hidden whitespace-nowrap"
483
+ @click="$refs.columnSelector.open(obj => { Object.assign(item, obj); apply() });">
472
484
  {{ item.label ? item.label : item.key }}
473
485
  </strong>
474
486
  <select v-if="item.type === 'date'"
@@ -490,7 +502,7 @@
490
502
 
491
503
  <div>
492
504
  <div class="flex flex-row items-center gap-2">
493
- <label>Values</label>
505
+ <label>Aggregrates</label>
494
506
  <button type="button" @click="$refs.columnSelector.open(obj => addPivot('values', obj))">
495
507
  <svg width="16" height="16" class="fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M432 256C432 269.3 421.3 280 408 280h-160v160c0 13.25-10.75 24.01-24 24.01S200 453.3 200 440v-160h-160c-13.25 0-24-10.74-24-23.99C16 242.8 26.75 232 40 232h160v-160c0-13.25 10.75-23.99 24-23.99S248 58.75 248 72v160h160C421.3 232 432 242.8 432 256z"/></svg>
496
508
  </button>
@@ -504,7 +516,8 @@
504
516
  <svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M496 288H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm0-128H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16z"/></svg>
505
517
  </div>
506
518
  <div class="flex-1 flex flex-row gap-3">
507
- <strong class="flex-1">
519
+ <strong class="flex-1 cursor-pointer text-ellipsis overflow-hidden whitespace-nowrap"
520
+ @click="$refs.columnSelector.open(obj => { Object.assign(item, obj); apply() });">
508
521
  {{ item.label ? item.label : item.key }}
509
522
  </strong>
510
523
  <select v-model="item.aggregrate"
@@ -518,7 +531,7 @@
518
531
  <option v-if="[ 'number' ].includes(item.type)" value="max">Max</option>
519
532
  </select>
520
533
  </div>
521
- <button type="button" @click="presetPivot.values.splice(index, 1)">
534
+ <button type="button" @click="presetPivot.values.splice(index, 1); apply()">
522
535
  <svg width="16" height="16" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"/></svg>
523
536
  </button>
524
537
  </div>
@@ -576,7 +589,9 @@ export default{
576
589
 
577
590
  props: {
578
591
 
579
- config: Object
592
+ config: Object,
593
+
594
+ enumCache: Object
580
595
 
581
596
  },
582
597
 
@@ -611,11 +626,12 @@ export default{
611
626
 
612
627
  if(this.preset){
613
628
  if(this.config.presetBarTabIndex === 2){
614
- if(Array.isArray(this.preset.filters)){
629
+ return true
630
+ /*if(Array.isArray(this.preset.filters)){
615
631
  if(this.preset.filters.filter(_ => !_.key).length > 0){
616
632
  return false
617
633
  }
618
- }
634
+ }*/
619
635
  }
620
636
  else if(this.config.presetBarTabIndex === 3){
621
637
  if(Array.isArray(this.preset.sorts)){
@@ -643,7 +659,7 @@ export default{
643
659
  },
644
660
 
645
661
  column(key){
646
- return this.config.columns.filter(_ => _.key === key).pop() ?? {}
662
+ return this.preset.columns.filter(_ => _.key === key).pop() ?? {}
647
663
  },
648
664
 
649
665
  duplicate(idx){
@@ -689,8 +705,9 @@ export default{
689
705
  },
690
706
 
691
707
  typeParamsOf(column){
692
- if([ 'enum' ].includes(column.type))
708
+ if([ 'enum' ].includes(column.type)){
693
709
  return column.typeParams
710
+ }
694
711
  return []
695
712
  },
696
713