@mixd-id/web-scaffold 0.1.230406351 → 0.1.230406353

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.230406351",
4
+ "version": "0.1.230406353",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -1,12 +1,39 @@
1
1
  <template>
2
2
  <div :class="$style.comp">
3
+ <div class="flex flex-row items-center gap-2 p-1 px-3 p-2">
4
+ <div class="flex-1">
5
+ <label class="text-text-400">{{ label }}</label>
6
+ </div>
7
+ <label class="text-sm text-text-300">Total points: {{ uniqueCount }}</label>
8
+ </div>
9
+
3
10
  <div v-if="apiKey" ref="map" class="flex-1">
4
11
 
5
12
  </div>
6
13
  <div v-else class="flex-1 flex items-center justify-center text-text-400">{{ $t('Google maps api required') }}</div>
7
14
 
8
- <div class="flex flex-row gap-2 p-1 px-3 pt-2">
9
- <label class="text-sm text-text-400">Total points: {{ uniqueCount }}</label>
15
+ <div class="flex flex-row">
16
+
17
+ <div class="flex-1 flex items-center justify-center divide-x divide-text-50 border-l-[1px] border-r-[1px] border-text-50">
18
+ <button type="button" class="p-1 px-3 hover:bg-primary text-text-400" @click="zoomOut">
19
+ <svg width="16" height="16" class="fill-text-500 hover:fill-text" 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="M400 288h-352c-17.69 0-32-14.32-32-32.01s14.31-31.99 32-31.99h352c17.69 0 32 14.3 32 31.99S417.7 288 400 288z"/></svg>
20
+ </button>
21
+ <button type="button" class="p-1 px-3 hover:bg-primary text-text-400 hover:text-text" @click="panLeft">
22
+ <svg width="16" height="16" class="fill-text-500 hover:fill-text" 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="M137.4 406.6l-128-127.1C3.125 272.4 0 264.2 0 255.1s3.125-16.38 9.375-22.63l128-127.1c9.156-9.156 22.91-11.9 34.88-6.943S192 115.1 192 128v255.1c0 12.94-7.781 24.62-19.75 29.58S146.5 415.8 137.4 406.6z"/></svg>
23
+ </button>
24
+ <button type="button" class="p-1 px-3 hover:bg-primary text-text-400 hover:text-text" @click="panUp">
25
+ <svg width="16" height="16" class="fill-text-500 hover:fill-text" 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="M9.39 265.4l127.1-128C143.6 131.1 151.8 128 160 128s16.38 3.125 22.63 9.375l127.1 128c9.156 9.156 11.9 22.91 6.943 34.88S300.9 320 287.1 320H32.01c-12.94 0-24.62-7.781-29.58-19.75S.2333 274.5 9.39 265.4z"/></svg>
26
+ </button>
27
+ <button type="button" class="p-1 px-3 hover:bg-primary text-text-400 hover:text-text" @click="panRight">
28
+ <svg width="16" height="16" class="fill-text-500 hover:fill-text" 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>
29
+ </button>
30
+ <button type="button" class="p-1 px-3 hover:bg-primary text-text-400 hover:text-text" @click="panDown">
31
+ <svg width="16" height="16" class="fill-text-500 hover:fill-text" 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>
32
+ </button>
33
+ <button type="button" class="p-1 px-3 hover:bg-primary text-text-400 hover:text-text" @click="zoomIn">
34
+ <svg width="16" height="16" class="fill-text-500 hover:fill-text" 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 256c0 17.69-14.33 32.01-32 32.01H256v144c0 17.69-14.33 31.99-32 31.99s-32-14.3-32-31.99v-144H48c-17.67 0-32-14.32-32-32.01s14.33-31.99 32-31.99H192v-144c0-17.69 14.33-32.01 32-32.01s32 14.32 32 32.01v144h144C417.7 224 432 238.3 432 256z"/></svg>
35
+ </button>
36
+ </div>
10
37
 
11
38
  </div>
12
39
  </div>
@@ -25,6 +52,8 @@ export default{
25
52
  props: {
26
53
  column: String,
27
54
 
55
+ label: String,
56
+
28
57
  value: Object,
29
58
 
30
59
  apiKey: {
@@ -219,7 +248,31 @@ export default{
219
248
  ];
220
249
  heatmap.set("gradient", gradient);
221
250
  });
222
- }
251
+ },
252
+
253
+ zoomIn(){
254
+ this.map.setZoom(this.map.getZoom() + 1)
255
+ },
256
+
257
+ zoomOut(){
258
+ this.map.setZoom(this.map.getZoom() - 1)
259
+ },
260
+
261
+ panLeft(){
262
+ this.map.panBy(-50, 0);
263
+ },
264
+
265
+ panUp(){
266
+ this.map.panBy(0, -50);
267
+ },
268
+
269
+ panRight(){
270
+ this.map.panBy(50, 0);
271
+ },
272
+
273
+ panDown(){
274
+ this.map.panBy(0, 50);
275
+ },
223
276
 
224
277
  },
225
278
 
@@ -4,6 +4,8 @@ export const component = {
4
4
 
5
5
  isContainer:true,
6
6
 
7
+ openOnAdd:false,
8
+
7
9
  props:{
8
10
 
9
11
  columns:[
@@ -4,6 +4,8 @@ export const component = {
4
4
 
5
5
  isContainer:true,
6
6
 
7
+ openOnAdd:false,
8
+
7
9
  props:{
8
10
 
9
11
  columns:[
@@ -4,6 +4,8 @@ export const component = {
4
4
 
5
5
  isContainer:true,
6
6
 
7
+ openOnAdd:false,
8
+
7
9
  props:{
8
10
 
9
11
  columns:[
@@ -4,6 +4,8 @@ export const component = {
4
4
 
5
5
  isContainer:true,
6
6
 
7
+ openOnAdd:false,
8
+
7
9
  props:{
8
10
  columns:['grid-cols-2', 'md:grid-cols-5', '', ''],
9
11
  gap:['gap-5', 'md:gap-5', '', ''],
@@ -372,6 +372,17 @@ const round = (num, precision = 0) => {
372
372
  return Math.round(n) / p
373
373
  }
374
374
 
375
+ const invokeAfterIdle = (callback, delay = 1800) => {
376
+ let timeoutId
377
+
378
+ return function(){
379
+ window.clearTimeout(timeoutId)
380
+ timeoutId = window.setTimeout(() => {
381
+ callback.apply(this, arguments)
382
+ }, delay)
383
+ }
384
+ }
385
+
375
386
 
376
387
  export {
377
388
  capitalize,
@@ -397,7 +408,8 @@ export {
397
408
  createFormData,
398
409
  unslugAndCapitalize,
399
410
  applyDatasourceReplacer,
400
- strVars
411
+ strVars,
412
+ invokeAfterIdle
401
413
  }
402
414
 
403
415
  function observeInit(){
@@ -0,0 +1,93 @@
1
+ <template>
2
+ <Modal ref="modal"
3
+ width="330"
4
+ height="360">
5
+ <template v-slot:head>
6
+ <div class="relative p-6">
7
+ <h3>Assign This Filter For</h3>
8
+ <div class="absolute top-0 right-0 p-2">
9
+ <button type="button" class="p-2" @click="close">
10
+ <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">
11
+ <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"/>
12
+ </svg>
13
+ </button>
14
+ </div>
15
+ </div>
16
+ </template>
17
+ <template v-slot:foot>
18
+ <div class="p-6" v-if="items?.length > 0 && !readonly">
19
+ <Button class="w-[80px]" @click="apply">OK</Button>
20
+ </div>
21
+ </template>
22
+ <div class="flex-1 p-6 flex">
23
+
24
+ <div v-if="items?.length > 0" class="flex flex-col gap-3">
25
+ <div v-for="item in items">
26
+ <Checkbox v-model="instance" :value="item.id" :disabled="readonly">
27
+ {{ item.name }}
28
+ </Checkbox>
29
+ </div>
30
+ </div>
31
+
32
+ <div v-else class="flex-1 flex items-center justify-center">
33
+ <label class="text-text-300">No users shared</label>
34
+ </div>
35
+
36
+ </div>
37
+ </Modal>
38
+ </template>
39
+
40
+ <script>
41
+
42
+ export default{
43
+
44
+ inject: [ 'socketEmit2' ],
45
+
46
+ props: {
47
+ items: Array,
48
+ readonly: [ Number, Boolean ],
49
+ },
50
+
51
+ computed: {
52
+
53
+
54
+ },
55
+
56
+ data(){
57
+ return {
58
+ instance: null,
59
+ callback: null
60
+ }
61
+ },
62
+
63
+ methods: {
64
+
65
+ apply(){
66
+ const instance = this.instance.map(_ => this.items.find(__ => __.id === _))
67
+ this.callback(instance)
68
+ this.close()
69
+ },
70
+
71
+ open(instance = [], callback){
72
+ this.instance = instance.map(_ => _.id)
73
+ this.callback = callback
74
+ this.$refs.modal.open()
75
+ },
76
+
77
+ close(){
78
+ this.$refs.modal.close()
79
+ }
80
+
81
+ }
82
+
83
+ }
84
+
85
+ </script>
86
+
87
+ <style module>
88
+
89
+ .comp{
90
+
91
+ }
92
+
93
+ </style>
@@ -77,68 +77,83 @@
77
77
  </ListItem>
78
78
  </div>
79
79
 
80
- <div v-else-if="selectedDatasource.tabIndex === 2" class="flex-1 flex flex-col divide-y divide-text-50">
81
- <div v-for="(filter, idx) in selectedDatasource.filters" class="p-5 py-8">
82
-
83
- <div class="flex flex-row items-start">
84
- <Checkbox v-model="filter.enabled" :disabled="selectedPreset.readonly" :default="true" @change="onDatasourceFilterChanged"/>
85
- <div class="flex-1 flex flex-col gap-3">
86
- <div class="flex-1 flex flex-row gap-2">
87
- <strong class="flex-1 cursor-pointer" @click="filter._collapsed = !filter._collapsed">
88
- <svg class="inline fill-text transition-transform relative top-[-1px]"
89
- :class="filter._collapsed === true ? `` : `rotate-90`"
90
- 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>
91
- {{ selectedDatasourceColumn(filter.key).label ?? filter.key }}
92
- </strong>
93
- <select v-if="filter.value.length > 1"
94
- v-model="filter.modifier"
95
- class="appearance-none bg-text-50 min-w-[60px] text-text-400 px-2 outline-none"
96
- @change="onDatasourceFilterChanged()">
97
- <option value="and">And</option>
98
- <option value="or">Or</option>
99
- </select>
100
- <button v-if="!selectedPreset.readonly && typeOf(selectedDatasourceColumn(filter.key).type) !== 'enum' && filter._collapsed !== true" type="button" @click="filter.value.push({})">
101
- <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>
102
- </button>
103
- <button v-if="!selectedPreset.readonly" type="button" class="p-1" @click="selectedDatasource.filters.splice(idx, 1);onDatasourceFilterChanged()">
104
- <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>
105
- </button>
106
- </div>
107
- <div v-if="filter._collapsed !== true">
108
- <div v-if="Array.isArray(filter.value)" class="flex flex-col gap-2">
109
- <div class="flex flex-row gap-2" v-for="(filterVal, filterIdx) in filter.value">
110
- <PresetSelectorFilterItem
111
- class="flex-1"
112
- :column="selectedDatasourceColumn(filter.key)"
113
- :type="typeOf(selectedDatasourceColumn(filter.key).type)"
114
- :typeParams="typeParamsOf(selectedDatasourceColumn(filter.key))"
115
- :value="filterVal"
116
- :enumCache="enumCache"
117
- :readonly="selectedPreset.readonly"
118
- @change="onDatasourceFilterChanged" />
119
-
120
- <button v-if="filter.value.length > 1 && !selectedPreset.readonly"
121
- type="button"
122
- class="p-1"
123
- @click="filter.value.splice(filterIdx, 1);onDatasourceFilterChanged()">
80
+ <div v-else-if="selectedDatasource.tabIndex === 2" class="flex-1 flex flex-col divide-y divide-text-50 py-3">
81
+
82
+ <ListItem :items="selectedDatasource.filters"
83
+ class="bg-transparent"
84
+ container-class="flex flex-col divide-y divide-text-50"
85
+ @reorder="(from, to) => { selectedDatasource.filters.splice(to, 0, selectedDatasource.filters.splice(from, 1)[0]); }">
86
+ <template v-slot="{ item:filter, index:idx }">
87
+ <div class="p-5">
88
+ <div class="flex flex-row items-start">
89
+ <Checkbox v-model="filter.enabled" :disabled="selectedPreset.readonly" :default="true" @change="onDatasourceFilterChanged"/>
90
+ <div class="flex-1 flex flex-col gap-3">
91
+ <div class="flex-1 flex flex-row items-center gap-2">
92
+ <strong data-reorder class="flex-1 cursor-pointer whitespace-nowrap text-ellipsis overflow-hidden"
93
+ @click="filter._collapsed = !filter._collapsed">
94
+ <svg class="inline fill-text transition-transform relative top-[-1px]"
95
+ :class="filter._collapsed === true ? `` : `rotate-90`"
96
+ 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>
97
+ {{ selectedDatasourceColumn(filter.key).label ?? filter.key }}
98
+ </strong>
99
+ <select v-if="filter.value.length > 1"
100
+ v-model="filter.modifier"
101
+ class="appearance-none bg-text-50 min-w-[60px] text-text-400 px-2 outline-none"
102
+ @change="onDatasourceFilterChanged()">
103
+ <option value="and">And</option>
104
+ <option value="or">Or</option>
105
+ </select>
106
+ <button v-if="!selectedPreset.readonly && typeOf(selectedDatasourceColumn(filter.key).type) !== 'enum' && filter._collapsed !== true" type="button" @click="filter.value.push({})">
107
+ <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>
108
+ </button>
109
+ <button v-if="!selectedPreset.readonly" type="button" class="p-1" @click="selectedDatasource.filters.splice(idx, 1);onDatasourceFilterChanged()">
124
110
  <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>
125
111
  </button>
112
+
113
+ <button v-if="selectedPreset.sharing?.length > 0 && !selectedPreset.readonly"
114
+ type="button"
115
+ class="p-0.5 rounded-full"
116
+ @click="$refs.datasourceFilterSharing.open(filter.sharing, (sharing) => { filter.sharing = sharing; load() })">
117
+ <svg width="14" height="14"
118
+ :class="filter.sharing?.length > 0 ? 'fill-primary' : 'fill-text-300 hover:fill-primary'" 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="M319.9 320c57.41 0 103.1-46.56 103.1-104c0-57.44-46.54-104-103.1-104c-57.41 0-103.1 46.56-103.1 104C215.9 273.4 262.5 320 319.9 320zM369.9 352H270.1C191.6 352 128 411.7 128 485.3C128 500.1 140.7 512 156.4 512h327.2C499.3 512 512 500.1 512 485.3C512 411.7 448.4 352 369.9 352zM512 160c44.18 0 80-35.82 80-80S556.2 0 512 0c-44.18 0-80 35.82-80 80S467.8 160 512 160zM183.9 216c0-5.449 .9824-10.63 1.609-15.91C174.6 194.1 162.6 192 149.9 192H88.08C39.44 192 0 233.8 0 285.3C0 295.6 7.887 304 17.62 304h199.5C196.7 280.2 183.9 249.7 183.9 216zM128 160c44.18 0 80-35.82 80-80S172.2 0 128 0C83.82 0 48 35.82 48 80S83.82 160 128 160zM551.9 192h-61.84c-12.8 0-24.88 3.037-35.86 8.24C454.8 205.5 455.8 210.6 455.8 216c0 33.71-12.78 64.21-33.16 88h199.7C632.1 304 640 295.6 640 285.3C640 233.8 600.6 192 551.9 192z"/></svg>
119
+ </button>
120
+ </div>
121
+ <div v-if="filter._collapsed !== true">
122
+ <div v-if="Array.isArray(filter.value)" class="flex flex-col gap-2">
123
+ <div class="flex flex-row gap-2" v-for="(filterVal, filterIdx) in filter.value">
124
+ <PresetSelectorFilterItem
125
+ class="flex-1"
126
+ :column="selectedDatasourceColumn(filter.key)"
127
+ :type="typeOf(selectedDatasourceColumn(filter.key).type)"
128
+ :typeParams="typeParamsOf(selectedDatasourceColumn(filter.key))"
129
+ :value="filterVal"
130
+ :enumCache="enumCache"
131
+ :readonly="selectedPreset.readonly"
132
+ @change="onDatasourceFilterChanged" />
133
+
134
+ <button v-if="filter.value.length > 1 && !selectedPreset.readonly"
135
+ type="button"
136
+ class="p-1"
137
+ @click="filter.value.splice(filterIdx, 1);onDatasourceFilterChanged()">
138
+ <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>
139
+ </button>
140
+ </div>
141
+ </div>
142
+ <PresetSelectorFilterItem v-else
143
+ :column="selectedDatasourceColumn(filter.key)"
144
+ :type="typeOf(selectedDatasourceColumn(filter.key).type)"
145
+ :value="filter"
146
+ :enumCache="enumCache"
147
+ :readonly="selectedPreset.readonly"
148
+ @change="onDatasourceFilterChanged" />
126
149
  </div>
127
150
  </div>
128
- <PresetSelectorFilterItem v-else
129
- :column="selectedDatasourceColumn(filter.key)"
130
- :type="typeOf(selectedDatasourceColumn(filter.key).type)"
131
- :value="filter"
132
- :enumCache="enumCache"
133
- :readonly="selectedPreset.readonly"
134
- @change="onDatasourceFilterChanged" />
135
151
  </div>
136
152
  </div>
137
- </div>
138
-
139
- </div>
153
+ </template>
154
+ </ListItem>
140
155
 
141
- <div v-for="(filter, idx) in queryFilters[selectedDatasource.uid]" class="p-5 py-8">
156
+ <div v-for="(filter, idx) in queryFilters[selectedDatasource.uid]" class="p-5">
142
157
 
143
158
  <div class="flex flex-col gap-2">
144
159
  <strong>{{ filter.key }}</strong>
@@ -152,7 +167,7 @@
152
167
 
153
168
  </div>
154
169
 
155
- <div class="py-8 flex justify-center" v-if="!selectedPreset.readonly">
170
+ <div class="p-5 flex justify-center" v-if="!selectedPreset.readonly">
156
171
  <button type="button"
157
172
  class="text-primary flex flex-row items-center justify-center p-3"
158
173
  @click="$refs.columnSelector.open(addFilter, selectedDatasourceFilterColumns)">
@@ -160,6 +175,8 @@
160
175
  Add Filter
161
176
  </button>
162
177
  </div>
178
+
179
+ <DatasourceFilterSharing ref="datasourceFilterSharing" :items="selectedPreset.sharing" :readonly="selectedPreset.readonly" />
163
180
  </div>
164
181
 
165
182
  <div v-else-if="selectedDatasource.tabIndex === 3" class="p-5 flex-1 flex flex-col">
@@ -386,13 +403,13 @@
386
403
  <SharingModal ref="sharingModal" :src="sharingSrc" />
387
404
  </div>
388
405
  <div class="flex flex-col gap-1 mt-2">
389
- <div v-for="user in selectedPreset.sharing"
406
+ <div v-for="(user, index) in selectedPreset.sharing"
390
407
  class="bg-base-500 hover:bg-text-50 p-3 rounded-lg flex flex-row gap-2 items-center gap-1">
391
408
  <div class="flex-1 flex flex-row items-center cursor-default gap-2">
392
409
  <svg width="14" height="14" class="fill-text-200" 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="M224 256c70.7 0 128-57.31 128-128s-57.3-128-128-128C153.3 0 96 57.31 96 128S153.3 256 224 256zM274.7 304H173.3C77.61 304 0 381.6 0 477.3c0 19.14 15.52 34.67 34.66 34.67h378.7C432.5 512 448 496.5 448 477.3C448 381.6 370.4 304 274.7 304z"/></svg>
393
410
  {{ user.name }}
394
411
  </div>
395
- <button type="button" @click="selectedPreset.sharing.splice(selectedPreset.sharing.indexOf(item), 1)">
412
+ <button type="button" @click="removeSharing(index)">
396
413
  <svg width="14" height="14" class="fill-text-300 hover:fill-red-600" 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 361.4c12.5 12.5 12.5 32.75 0 45.25C304.4 412.9 296.2 416 288 416s-16.38-3.125-22.62-9.375L160 301.3L54.63 406.6C48.38 412.9 40.19 416 32 416S15.63 412.9 9.375 406.6c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z"/></svg>
397
414
  </button>
398
415
  </div>
@@ -487,7 +504,7 @@
487
504
  </div>
488
505
 
489
506
  <button class="w-full p-3 text-left flex flex-row items-center" :class="appStyle.menuItem"
490
- @click="confirm('Remove this preset?', () => cConfig.presets.splice(context.index, 1))">
507
+ @click="confirm('Remove this preset?', () => removePreset(context.index))">
491
508
  <div class="w-[30px]">
492
509
  <svg width="16" height="16" class="fill-text-300" 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 361.4c12.5 12.5 12.5 32.75 0 45.25C304.4 412.9 296.2 416 288 416s-16.38-3.125-22.62-9.375L160 301.3L54.63 406.6C48.38 412.9 40.19 416 32 416S15.63 412.9 9.375 406.6c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z"/></svg>
493
510
  </div>
@@ -533,10 +550,6 @@
533
550
 
534
551
  </div>
535
552
 
536
- <div class="p-5">
537
- <Button ref="saveConfigBtn" class="w-[90px] py-2" :state="configChanged ? 1 : -1" @click="save">Save</Button>
538
- </div>
539
-
540
553
  </TransitionGroup>
541
554
 
542
555
  <div :class="$style.resize1"
@@ -648,12 +661,12 @@ import TreeView from "./WebPageBuilder4/TreeView.vue";
648
661
  import DoughnutSetting from "./Dashboard/DoughnutSetting.vue";
649
662
  import PieSetting from "./Dashboard/PieSetting.vue";
650
663
  import PolarAreaSetting from "./Dashboard/PolarAreaSetting.vue";
651
- import {useDatasource} from "../stores/datasource";
652
664
  import {defineAsyncComponent} from "vue";
653
665
  import SharingModal from "./Dashboard/SharingModal.vue";
654
666
  import PresetSelectorFilterItem from "../components/PresetSelectorFilterItem.vue";
655
667
  import {generatePivotColumns} from "../utils/preset-selector.mjs";
656
668
  import PresetBarPivot from "./PresetBarPivot.vue";
669
+ import {invokeAfterIdle} from "../utils/helpers.mjs";
657
670
 
658
671
  const findComponents = (items, uid) => {
659
672
  if(!Array.isArray(items)) return
@@ -670,9 +683,13 @@ const findComponents = (items, uid) => {
670
683
  }
671
684
  }
672
685
 
686
+
687
+
688
+
673
689
  export default{
674
690
 
675
691
  components: {
692
+ DatasourceFilterSharing: defineAsyncComponent(() => import('./Dashboard/DatasourceFilterSharing.vue')),
676
693
  PresetBarPivot,
677
694
  DatasourcePreview: defineAsyncComponent(() => import('./Dashboard/DatasourcePreview.vue')),
678
695
  PresetSelectorFilterItem,
@@ -972,7 +989,8 @@ export default{
972
989
 
973
990
  addView(item, items, opt = { unshift:false }){
974
991
  items[opt?.unshift ? 'unshift' : 'push'](item)
975
- this.$nextTick(() => this.cConfig.selectedView = item.uid)
992
+ if(item.openOnAdd !== false)
993
+ this.$nextTick(() => this.cConfig.selectedView = item.uid)
976
994
  },
977
995
 
978
996
  emitRoot(name, component){
@@ -1166,12 +1184,15 @@ export default{
1166
1184
  async load(uids){
1167
1185
  if(this.readyState !== 1) return
1168
1186
  if(!this.viewedComponents) return
1187
+ if(!this.cConfig) return
1169
1188
 
1170
1189
  const views = this.recurseViews(this.viewedComponents, uids)
1171
1190
  if(views?.length < 1) return
1172
1191
 
1192
+ const { datasource } = this.viewedPreset
1193
+
1173
1194
  if(!uids) this.readyState = 0
1174
- this.socket.send(this.src, { views })
1195
+ this.socket.send(this.src, { views, datasource })
1175
1196
  .then(_ => {
1176
1197
  Object.assign(this.values, _)
1177
1198
  })
@@ -1218,18 +1239,14 @@ export default{
1218
1239
  this.$nextTick(() => {
1219
1240
  this.cConfig.viewedUid = newPreset.uid
1220
1241
  this.openPreset(newPreset.uid)
1242
+ this.selectPreset(newPreset.uid)
1243
+ this.saveConfig()
1221
1244
  })
1222
1245
  },
1223
1246
 
1224
1247
  openPreset(uid, opt = {}){
1225
1248
  this.cConfig.selectedUid = uid
1226
- /*this.$router.push({
1227
- query:{
1228
- ...(this.$route.query ?? {}),
1229
- edit:uid,
1230
- ...(opt.select ? { uid } : {})
1231
- }
1232
- })*/
1249
+ this.saveConfig()
1233
1250
  },
1234
1251
 
1235
1252
  selectPreset(uid, params = {}){
@@ -1239,6 +1256,7 @@ export default{
1239
1256
  ...params
1240
1257
  }
1241
1258
  })
1259
+ this.saveConfig()
1242
1260
  },
1243
1261
 
1244
1262
  resize1(w){
@@ -1288,23 +1306,49 @@ export default{
1288
1306
  })
1289
1307
  },
1290
1308
 
1291
- save(){
1292
- this.$refs.saveConfigBtn.setState(2)
1293
- this.socket.send(this.configSrc,
1294
- { key:this.configKey, config:this.cConfig })
1295
- .then(_ => this.initialConfig = JSON.stringify(this.cConfig))
1296
- .finally(_ => this.$refs.saveConfigBtn.resetState())
1309
+ saveConfig: invokeAfterIdle(function() {
1310
+ this.$emit('save', {
1311
+ config:this.cConfig,
1312
+ presets: undefined
1313
+ })
1314
+ }),
1315
+
1316
+ removePreset(index){
1317
+ const [ preset ] = this.cConfig.presets.splice(index, 1)
1318
+ this.$emit('save', { removePreset:{ uid:preset.uid } })
1319
+ },
1320
+
1321
+ savePreset: invokeAfterIdle(function(){
1322
+ if(this.selectedPreset.readonly) return
1323
+ this.$emit('save', { preset:this.selectedPreset })
1324
+ }),
1325
+
1326
+ removeSharing(index){
1327
+ const user = this.selectedPreset.sharing[index]
1328
+
1329
+ for(let datasource of this.selectedPreset.datasource){
1330
+ for(let filter of datasource.filters){
1331
+ const index = (filter.sharing ?? []).findIndex(_ => _.id === user.id)
1332
+ if(index > -1){
1333
+ filter.sharing.splice(index, 1)
1334
+ }
1335
+ }
1336
+ }
1337
+
1338
+ this.selectedPreset.sharing.splice(index, 1)
1297
1339
  }
1298
1340
 
1299
1341
  },
1300
1342
 
1343
+ mounted() {
1344
+ this.load()
1345
+ },
1346
+
1301
1347
  props:{
1302
1348
 
1303
1349
  config: Object,
1304
1350
 
1305
1351
  src: String,
1306
- configSrc: String,
1307
- configKey: String,
1308
1352
 
1309
1353
  datasourceSrc: String,
1310
1354
  datasourceListSrc: String,
@@ -1334,7 +1378,7 @@ export default{
1334
1378
 
1335
1379
  watch: {
1336
1380
 
1337
- 'cConfig.presets'(){
1381
+ 'cConfig.presets'(to){
1338
1382
  this.cConfig.viewedUid = 'uid' in this.$route.query ? this.$route.query.uid : this.cConfig.viewedUid
1339
1383
  },
1340
1384
 
@@ -1342,6 +1386,14 @@ export default{
1342
1386
  this.$emit('change')
1343
1387
  },
1344
1388
 
1389
+ 'cConfig.selectedUid'(){
1390
+ this.saveConfig()
1391
+ },
1392
+
1393
+ 'cConfig.selectedDatasource'(){
1394
+ this.saveConfig()
1395
+ },
1396
+
1345
1397
  '$route.query': {
1346
1398
  handler(to){
1347
1399
  if(this.cConfig){
@@ -1352,10 +1404,18 @@ export default{
1352
1404
  },
1353
1405
 
1354
1406
  cConfig(to, from){
1407
+ console.log('cConfig', from)
1355
1408
  if(!from){
1356
1409
  this.load()
1357
1410
  this.initialConfig = JSON.stringify(this.cConfig)
1358
1411
  }
1412
+ },
1413
+
1414
+ selectedPreset:{
1415
+ deep: true,
1416
+ handler(to) {
1417
+ this.savePreset()
1418
+ }
1359
1419
  }
1360
1420
 
1361
1421
  }