@mixd-id/web-scaffold 0.1.2301231316 → 0.1.2301231318

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.2301231316",
4
+ "version": "0.1.2301231318",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -123,9 +123,7 @@ export default{
123
123
  })
124
124
  left *= -1
125
125
 
126
- //console.log('`setPosition`', index, left)
127
-
128
- if(noAnimation){
126
+ if(!noAnimation){
129
127
  this.$el.firstElementChild.addEventListener('transitionend', function(){
130
128
  this.classList.remove('transition-carousel')
131
129
  })
@@ -1,8 +1,11 @@
1
1
  <template>
2
- <div :class="$style.img">
2
+ <div :class="computedOuterClass">
3
3
  <img :class="computedClass" :src="actualSrc" ref="img" @click="onClick" :style="imageStyle"/>
4
4
  <input v-if="Boolean(editable)" class="hidden" type="file" accept="image/*" ref="file" @change="onChange"/>
5
5
  <slot></slot>
6
+ <div :class="$style.isLoading" v-if="isLoading">
7
+ <svg class="animate-spin aspect-square w-[15%] text-primary" 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>
8
+ </div>
6
9
  </div>
7
10
  </template>
8
11
 
@@ -23,6 +26,10 @@ export default{
23
26
 
24
27
  props:{
25
28
 
29
+ class: String,
30
+
31
+ outerClass: String,
32
+
26
33
  editable:{
27
34
  type: [ Boolean, String ],
28
35
  default: false
@@ -38,23 +45,31 @@ export default{
38
45
 
39
46
  defaultSrc: {
40
47
  type: String,
41
- default: '/images/no-image.png'
48
+ default: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yMS4xNTkyIDIxLjA4MjhDMjEuNjc5OCAyMC41NDMyIDIyIDE5LjgwOSAyMiAxOVY1QzIyIDMuMzQzMTUgMjAuNjU2OSAyIDE5IDJINUMzLjM0MzE1IDIgMiAzLjM0MzE1IDIgNVYxOUMyIDIwLjY1NjkgMy4zNDMxNSAyMiA1IDIySDE5QzE5LjI5NjIgMjIgMTkuNTgyMyAyMS45NTcxIDE5Ljg1MjUgMjEuODc3MUMxOS44NjU4IDIxLjg3MzIgMTkuODc5IDIxLjg2OTIgMTkuODkyMSAyMS44NjUxQzIwLjI1ODYgMjEuNzUxMSAyMC41OTUgMjEuNTY4NyAyMC44ODYzIDIxLjMzMjlDMjAuODkzNyAyMS4zMjY5IDIwLjkwMSAyMS4zMjEgMjAuOTA4MyAyMS4zMTQ5QzIwLjk4MjQgMjEuMjUzOCAyMS4wNTM1IDIxLjE4OTIgMjEuMTIxMyAyMS4xMjEzQzIxLjEzNDEgMjEuMTA4NiAyMS4xNDY3IDIxLjA5NTcgMjEuMTU5MiAyMS4wODI4Wk0xOSAzLjVINUM0LjE3MTU3IDMuNSAzLjUgNC4xNzE1NyAzLjUgNVYxMi43TDYuMDUyOTIgOS42MzY0OUM3LjIxNTY2IDguMjQxMjEgOS4zNDE3NyA4LjE5MTAyIDEwLjU2OSA5LjUyOTg3TDIwLjE0MTggMTkuOTcyOEMyMC4zNjUyIDE5LjcxMDkgMjAuNSAxOS4zNzEyIDIwLjUgMTlWNUMyMC41IDQuMTcxNTcgMTkuODI4NCAzLjUgMTkgMy41Wk01IDIwLjVDNC4xNzE1NyAyMC41IDMuNSAxOS44Mjg0IDMuNSAxOVYxNS4wNDMxTDcuMjA1MjYgMTAuNTk2OEM3LjcxMzk1IDkuOTg2MzMgOC41OTE0NCA5Ljg5MDgxIDkuMjEzOTUgMTAuMzI0OUM5LjIxOTUxIDEwLjMyODggOS4yMjUwNSAxMC4zMzI3IDkuMjMwNTcgMTAuMzM2N0M5LjMxMzMyIDEwLjM5NjEgOS4zOTE0MSAxMC40NjUgOS40NjMzMiAxMC41NDM1TDE4LjU5MDEgMjAuNUg1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iLjA4Ii8+CjxjaXJjbGUgY3g9IjE3IiBjeT0iNyIgcj0iMS4yNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9Ii4wOCIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPC9zdmc+Cg=="
42
49
  }
43
50
 
44
51
  },
45
52
 
46
53
  computed:{
47
54
 
48
- computedClass(){
55
+ computedOuterClass(){
49
56
  return [
50
57
  this.$style.img
51
58
  ]
52
59
  .join(' ')
53
60
  },
54
61
 
62
+ computedClass(){
63
+ return [
64
+ this.class
65
+ ]
66
+ .join(' ')
67
+ },
68
+
55
69
  imageStyle(){
56
70
  return {
57
- ...(this.aspectRatio ? { 'aspect-ratio':this.aspectRatio } : {})
71
+ ...(this.aspectRatio ? { 'aspect-ratio':this.aspectRatio } : {}),
72
+ ...(this.actualSrc ? {} : { visibility:'hidden' })
58
73
  }
59
74
  }
60
75
 
@@ -63,7 +78,9 @@ export default{
63
78
  data(){
64
79
  return {
65
80
  visible: 0,
66
- actualSrc: ''
81
+ isLoading: false,
82
+ actualSrc: null,
83
+ loaded: false
67
84
  }
68
85
  },
69
86
 
@@ -92,8 +109,12 @@ export default{
92
109
  },
93
110
 
94
111
  async loadSrc(){
112
+
113
+ this.loaded = false
114
+
95
115
  if(typeof this.src === 'string') {
96
116
  if (this.src.startsWith('data:image')) {
117
+ this.loaded = true
97
118
  this.actualSrc = this.src
98
119
  }
99
120
  else{
@@ -113,33 +134,52 @@ export default{
113
134
 
114
135
  var img = new Image()
115
136
  img.addEventListener('load', () => {
137
+ this.loaded = true
116
138
  this.actualSrc = img.src
139
+ this.isLoading = false
140
+ })
141
+ img.addEventListener('error', () => {
142
+ this.loaded = true
143
+ this.actualSrc = this.defaultSrc
144
+ this.isLoading = false
117
145
  })
118
- img.addEventListener('error', () => {})
119
146
  img.src = src[screens[b]]
120
-
121
- //this.actualSrc = src[screens[b]]
122
147
  }
123
148
  }
124
149
  }
125
150
  }
126
-
127
151
  else if(this.src instanceof File){
128
152
  var reader = new FileReader();
153
+
129
154
  reader.addEventListener('load', () => {
155
+ this.loaded = true
130
156
  this.actualSrc = reader.result
157
+ this.isLoading = false
131
158
  }, false)
159
+
160
+ reader.addEventListener('error', () => {
161
+ this.loaded = true
162
+ this.actualSrc = this.defaultSrc
163
+ this.isLoading = false
164
+ })
165
+
132
166
  reader.readAsDataURL(this.src)
133
167
  }
134
-
135
168
  }
136
169
 
137
170
  },
138
171
 
139
172
  watch:{
140
173
 
141
- src(){
174
+ src(to){
142
175
  this.loadSrc()
176
+
177
+ window.setTimeout(() => {
178
+ if(!this.loaded){
179
+ this.actualSrc = null
180
+ this.isLoading = true
181
+ }
182
+ }, 200)
143
183
  }
144
184
 
145
185
  }
@@ -150,10 +190,12 @@ export default{
150
190
  <style module>
151
191
 
152
192
  .img{
193
+ @apply relative;
153
194
  }
154
195
 
155
- .img img{
156
- height: 100%;
196
+ .isLoading{
197
+ @apply absolute top-0 left-0 right-0 bottom-0;
198
+ @apply flex items-center justify-center;
157
199
  }
158
200
 
159
201
 
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <Modal :state="isOpen" width="400" height="400" :class="$style.previewModal">
3
+ <div class="flex-1">
4
+ <div class="flex flex-row justify-end items-center p-3">
5
+ <a :href="imageUrl" download>
6
+ <svg xmlns="http://www.w3.org/2000/svg" width="42" height="42" viewBox="0 0 24 24" class="fill-text"><path class="primary" d="M15 15v-3a3 3 0 0 0-6 0v3H6a4 4 0 0 1-.99-7.88 5.5 5.5 0 0 1 10.86-.82A4.49 4.49 0 0 1 22 10.5a4.5 4.5 0 0 1-4.5 4.5H15z"/><path class="secondary" d="M11 18.59V12a1 1 0 0 1 2 0v6.59l1.3-1.3a1 1 0 0 1 1.4 1.42l-3 3a1 1 0 0 1-1.4 0l-3-3a1 1 0 0 1 1.4-1.42l1.3 1.3z"/></svg>
7
+ </a>
8
+ <button class="ml-3" @click="close">
9
+ <svg xmlns="http://www.w3.org/2000/svg" width="56" height="56" viewBox="0 0 24 24" class="fill-text"><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"/></svg>
10
+ </button>
11
+ </div>
12
+ <div class="flex items-center justify-center">
13
+ <img :src="imageUrl" class="max-h-[85vh]"/>
14
+ </div>
15
+ </div>
16
+ </Modal>
17
+ </template>
18
+
19
+ <script>
20
+
21
+ export default{
22
+
23
+ data(){
24
+ return {
25
+ isOpen: false,
26
+ imageUrl: null
27
+ }
28
+ },
29
+
30
+ methods: {
31
+
32
+ open(imageUrl){
33
+ this.imageUrl = imageUrl
34
+ this.isOpen = true
35
+ },
36
+
37
+ close(){
38
+ this.isOpen = false
39
+ this.imageUrl = null
40
+ }
41
+
42
+ }
43
+
44
+ }
45
+
46
+ </script>
47
+
48
+ <style module>
49
+
50
+ .comp{
51
+
52
+ }
53
+
54
+ .previewModal{
55
+ @apply !w-screen !h-screen !bg-base-300;
56
+ max-width: unset !important;
57
+ max-height: unset !important;
58
+ }
59
+
60
+ </style>
@@ -8,7 +8,7 @@
8
8
  <div class="mr-6">
9
9
  <h2>{{ title }}</h2>
10
10
  </div>
11
- <div class="flex-1 overflow-hidden">
11
+ <div class="flex-1 overflow-hidden mr-4">
12
12
  <slot v-if="$slots['lp-search']" name="lp-search" :preset="preset"/>
13
13
  <Textbox v-else :clearable="true" placeholder="Cari..." class="w-[200px]" v-model="preset.search"
14
14
  @clear="clearSearch" @submit="load">
@@ -24,25 +24,28 @@
24
24
  <div>
25
25
  <slot name="lp-tabspace" :preset="preset"/>
26
26
  </div>
27
- <div>
27
+ <div class="flex flex-row gap-1 items-center">
28
28
  <Dropdown class="w-[150px]" :value="preset.name" mode="custom" position="bottom-right">
29
- <div class="min-w-[240px] divide-y divide-text-50 p-3">
30
- <div v-for="(_preset, idx) in presets" class="cursor-pointer flex flex-row">
29
+ <div class="min-w-[240px] divide-y divide-text-50 p-2 flex flex-col gap-2">
30
+ <div v-for="(_preset, idx) in presets" class="cursor-pointer flex flex-row hover:bg-primary-100 rounded-md">
31
31
  <div class="flex-1 p-3" @click="selectPreset(_preset)">
32
32
  {{ _preset.name }}
33
33
  </div>
34
- <div>
35
- <button class="p-3 text-primary" @click="selectPreset(_preset, true)">
36
- Edit
37
- </button>
38
- </div>
39
34
  </div>
40
35
  </div>
41
36
  </Dropdown>
37
+ <button class="border-[1px] border-text-200 bg-base-50 hover:border-text-300 rounded-lg p-2"
38
+ @click="openPreset">
39
+ <svg width="20.5" height="20.5" viewBox="0 0 24 24" class="fill-text-300 hover:fill-primary" xmlns="http://www.w3.org/2000/svg">
40
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M5 13.5C5.82843 13.5 6.5 12.8284 6.5 12C6.5 11.1716 5.82843 10.5 5 10.5C4.17157 10.5 3.5 11.1716 3.5 12C3.5 12.8284 4.17157 13.5 5 13.5ZM5 15C6.65685 15 8 13.6569 8 12C8 10.3431 6.65685 9 5 9C3.34315 9 2 10.3431 2 12C2 13.6569 3.34315 15 5 15Z" />
41
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M12 13.5C12.8284 13.5 13.5 12.8284 13.5 12C13.5 11.1716 12.8284 10.5 12 10.5C11.1716 10.5 10.5 11.1716 10.5 12C10.5 12.8284 11.1716 13.5 12 13.5ZM12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" />
42
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M19 13.5C19.8284 13.5 20.5 12.8284 20.5 12C20.5 11.1716 19.8284 10.5 19 10.5C18.1716 10.5 17.5 11.1716 17.5 12C17.5 12.8284 18.1716 13.5 19 13.5ZM19 15C20.6569 15 22 13.6569 22 12C22 10.3431 20.6569 9 19 9C17.3431 9 16 10.3431 16 12C16 13.6569 17.3431 15 19 15Z" />
43
+ </svg>
44
+ </button>
42
45
  </div>
43
46
  </div>
44
47
 
45
- <VirtualTable class="flex-1 m-6" :columns="columns" :items="items"
48
+ <VirtualTable class="flex-1 m-6" :columns="presetColumns" :items="items"
46
49
  :appearances="appearances" @scroll-end="loadNext">
47
50
  <template v-for="(_, slot) in $slots" #[slot]="{ item, index }">
48
51
  <slot :name="slot" :item="item" :index="index"></slot>
@@ -54,10 +57,11 @@
54
57
  <div v-if="layout.presetOpen" class="w-[360px] border-l-[1px] border-text-50 bg-base-500 flex flex-col">
55
58
 
56
59
  <div class="p-6 pb-0">
57
- <div class="flex flex-row gap-2 mb-3">
58
- <input class="flex-1 text-xl bg-transparent outline-none" :value="preset.name" @blur="preset.name = $event.target.value" />
59
- <button @click="layout.presetContextMenu = { caller:$event.target }">
60
- <svg width="21" height="21" viewBox="0 0 24 24" class="fill-text-300 pointer-events-none" xmlns="http://www.w3.org/2000/svg">
60
+ <div class="flex flex-row gap-4 mb-3">
61
+ <input class="flex-1 text-xl bg-transparent outline-none" :value="preset.name" @blur="preset.name = $event.target.value"
62
+ @keyup.enter="preset.name = $event.target.value" />
63
+ <button ref="presetMenu" @click="layout.presetContextMenu = { caller:$refs.presetMenu }">
64
+ <svg width="21" height="21" viewBox="0 0 24 24" class="fill-text-300 hover:fill-primary" xmlns="http://www.w3.org/2000/svg">
61
65
  <path fill-rule="evenodd" clip-rule="evenodd" d="M12 6.5C12.8284 6.5 13.5 5.82843 13.5 5C13.5 4.17157 12.8284 3.5 12 3.5C11.1716 3.5 10.5 4.17157 10.5 5C10.5 5.82843 11.1716 6.5 12 6.5ZM12 8C13.6569 8 15 6.65685 15 5C15 3.34315 13.6569 2 12 2C10.3431 2 9 3.34315 9 5C9 6.65685 10.3431 8 12 8Z"/>
62
66
  <path fill-rule="evenodd" clip-rule="evenodd" d="M12 13.5C12.8284 13.5 13.5 12.8284 13.5 12C13.5 11.1716 12.8284 10.5 12 10.5C11.1716 10.5 10.5 11.1716 10.5 12C10.5 12.8284 11.1716 13.5 12 13.5ZM12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z"/>
63
67
  <path fill-rule="evenodd" clip-rule="evenodd" d="M12 20.5C12.8284 20.5 13.5 19.8284 13.5 19C13.5 18.1716 12.8284 17.5 12 17.5C11.1716 17.5 10.5 18.1716 10.5 19C10.5 19.8284 11.1716 20.5 12 20.5ZM12 22C13.6569 22 15 20.6569 15 19C15 17.3431 13.6569 16 12 16C10.3431 16 9 17.3431 9 19C9 20.6569 10.3431 22 12 22Z"/>
@@ -68,10 +72,11 @@
68
72
  @dismiss="layout.presetContextMenu = null" position="bottom-right">
69
73
  <div class="min-w-[200px]">
70
74
  <button class="w-full p-3 hover:bg-text-50" @click="copyPreset">Copy this Preset</button>
75
+ <button class="w-full p-3 text-red-500 hover:text-red-700" @click="removePreset">Remove</button>
71
76
  </div>
72
77
  </ContextMenu>
73
- <button @click="layout.presetOpen = false">
74
- <svg width="21" height="21" viewBox="0 0 24 24" class="fill-text-300" xmlns="http://www.w3.org/2000/svg">
78
+ <button @click="closePreset">
79
+ <svg width="21" height="21" viewBox="0 0 24 24" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg">
75
80
  <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"/>
76
81
  </svg>
77
82
  </button>
@@ -85,7 +90,7 @@
85
90
  <div class="flex-1 overflow-y-auto px-6 mb-6">
86
91
 
87
92
  <div class="p-2 mt-6 flex flex-col" v-if="layout.presetTab === 'column'">
88
- <ListItem :items="columns" @reorder="reorderColumns">
93
+ <ListItem :items="presetColumns" @reorder="reorderColumns">
89
94
  <template v-slot="{ item }">
90
95
  <div class="flex flex-row items-center gap-2" :key="item">
91
96
  <div class="cursor-move" data-reorder>
@@ -94,10 +99,9 @@
94
99
  <path fill-rule="evenodd" clip-rule="evenodd" d="M4 8C4 7.44772 4.44772 7 5 7H19C19.5523 7 20 7.44772 20 8C20 8.55228 19.5523 9 19 9H5C4.44772 9 4 8.55228 4 8Z"/>
95
100
  </svg>
96
101
  </div>
97
- <input :id="`lp1column${item.key}`" type="checkbox" v-model="item.visible" :true-value="true" :false-value="false" @change="load"/>
98
- <label :for="`lp1column${item.key}`" class="p-2">
102
+ <Checkbox v-model="item.visible" :true-value="true" :false-value="false" @change="load">
99
103
  {{ item.label ?? '(No Name)' }}
100
- </label>
104
+ </Checkbox>
101
105
  </div>
102
106
  </template>
103
107
  </ListItem>
@@ -106,7 +110,7 @@
106
110
  <div class="flex flex-col" v-if="layout.presetTab === 'filter'">
107
111
  <div v-if="filterableColumns.length > 0">
108
112
  <ListPage1Filter v-if="preset.filters" v-for="filter in preset.filters"
109
- :filter="filter" :column="preset.columns.filter((_) => _.key === filter.key).pop()"
113
+ :filter="filter" :column="columns[filter.key]"
110
114
  @remove="removeFilter(filter)" @change="load"/>
111
115
 
112
116
  <div class="py-8">
@@ -123,12 +127,12 @@
123
127
  </div>
124
128
  </div>
125
129
 
126
- <div class="p-2 flex flex-col" v-if="layout.presetTab === 'sort'">
127
- <div v-if="sortableColumns.length > 0">
128
- <div v-for="sort in preset.sorts" class="py-4 border-b-[1px] border-text-100">
130
+ <div class="flex flex-col" v-if="layout.presetTab === 'sort'">
131
+ <div v-if="sortableColumns.length > 0" class="my-8">
132
+ <div v-for="sort in preset.sorts" class="py-4">
129
133
  <div class="flex flex-row items-center gap-2">
130
134
  <div class="flex-1">
131
- <Dropdown v-model="sort.key">
135
+ <Dropdown v-model="sort.key" @change="load">
132
136
  <option value="" disabled selected>Tambah Urutan</option>
133
137
  <option v-for="column in sortableColumns" :value="column.key">{{ column.label }}</option>
134
138
  </Dropdown>
@@ -140,14 +144,16 @@
140
144
  </Dropdown>
141
145
  </div>
142
146
  <button @click="removeSort(sort)">
143
- <svg width="21" height="21" class="fill-text-100 hover:fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
147
+ <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">
144
148
  <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"/>
145
149
  </svg>
146
150
  </button>
147
151
  </div>
148
152
  </div>
149
153
 
150
- <div class="py-8">
154
+ <div v-if="preset.sorts && preset.sorts.length > 0" class="h-[1px] my-4 bg-text-100"></div>
155
+
156
+ <div class="py-4">
151
157
  <Dropdown @change="addSort" v-model="layout.presetSortSelector">
152
158
  <option value="" disabled selected>Tambah Urutan</option>
153
159
  <option v-for="column in sortableColumns" :value="column.key">{{ column.label }}</option>
@@ -193,7 +199,18 @@ export default{
193
199
  return this.presets[this.presetIdx] ?? {}
194
200
  },
195
201
 
196
- columns(){
202
+ presetColumns(){
203
+
204
+ for(let i = 0 ; i < (this.preset.columns ?? []).length ; i++){
205
+ const presetColumn = this.preset.columns[i]
206
+ const column = this.columns[presetColumn.key] ?? {}
207
+ for(let key in column){
208
+ if(!presetColumn[key]){
209
+ this.preset.columns[i][key] = column[key]
210
+ }
211
+ }
212
+ }
213
+
197
214
  return this.preset.columns ?? []
198
215
  },
199
216
 
@@ -202,11 +219,11 @@ export default{
202
219
  },
203
220
 
204
221
  filterableColumns(){
205
- return this.preset.columns.filter((_) => _.filterable)
222
+ return Object.values(this.columns).filter((_) => _.filterable)
206
223
  },
207
224
 
208
225
  sortableColumns(){
209
- return this.preset.columns.filter((_) => _.sortable)
226
+ return Object.values(this.columns).filter((_) => _.sortable)
210
227
  }
211
228
 
212
229
  },
@@ -220,6 +237,7 @@ export default{
220
237
  presetFilterSelector: null,
221
238
  presetSortSelector: null,
222
239
  },
240
+ columns: [],
223
241
  presets: [],
224
242
  presetIdx: -1,
225
243
  items: [],
@@ -230,6 +248,10 @@ export default{
230
248
 
231
249
  mounted() {
232
250
  this.socket.onAny(this.onHooks)
251
+
252
+ window.addEventListener('focus', () => {
253
+ this.load()
254
+ })
233
255
  },
234
256
 
235
257
  unmounted() {
@@ -240,8 +262,10 @@ export default{
240
262
 
241
263
  patch(){
242
264
  this.socketEmit(`${this.model}.patch`, {}, (res) => {
265
+ this.columns = res.columns
243
266
  this.presets = res.presets
244
267
  this.presetIdx = res.presetIdx
268
+ this.patchPresets()
245
269
  this.$nextTick(() => {
246
270
  this.items = res.items
247
271
  this.hasNext = res.hasNext
@@ -249,6 +273,45 @@ export default{
249
273
  })
250
274
  },
251
275
 
276
+ patchPresets(){
277
+
278
+ if(!this.columns) return
279
+
280
+ this.presets.forEach((preset) => {
281
+
282
+ const presetColumnKeys = {}
283
+ const removedKeys = []
284
+ for(let i = (preset.columns ?? []).length - 1 ; i >= 0 ; i--){
285
+ const presetColumn = preset.columns[i]
286
+ const presetColumnKey = presetColumn.key
287
+ if(this.columns[presetColumnKey]){
288
+ const updated = {}
289
+ if(this.columns[presetColumnKey].label !== presetColumn.label)
290
+ updated['label'] = this.columns[presetColumnKey].label
291
+ if(this.columns[presetColumnKey].filterable !== presetColumn.filterable)
292
+ updated['filterable'] = this.columns[presetColumnKey].filterable
293
+ if(this.columns[presetColumnKey].sortable !== presetColumn.sortable)
294
+ updated['sortable'] = this.columns[presetColumnKey].sortable
295
+ if(Object.keys(updated).length > 0){
296
+ Object.assign(preset.columns[i], updated)
297
+ }
298
+ }
299
+ else{
300
+ preset.columns.splice(i, 1)
301
+ removedKeys.push(presetColumnKey)
302
+ }
303
+ presetColumnKeys[presetColumn.key] = 1
304
+ }
305
+
306
+ for(let key in this.columns){
307
+ if(typeof presetColumnKeys[key] === 'undefined'){
308
+ preset.columns.push(this.columns[key])
309
+ }
310
+ }
311
+ })
312
+
313
+ },
314
+
252
315
  load(){
253
316
  this.socket.emit(`${this.model}.load`, {
254
317
  preset: this.preset
@@ -281,20 +344,39 @@ export default{
281
344
  this.load()
282
345
  },
283
346
 
284
- selectPreset(preset, edit){
347
+ selectPreset(preset){
285
348
  this.presetIdx = this.presets.findIndex((_) => _ === preset)
286
- if(edit){
287
- this.layout.presetOpen = true
288
- }
349
+ },
350
+
351
+ openPreset(){
352
+ this.layout.presetOpen = true
353
+ },
354
+
355
+ closePreset(){
356
+ this.layout.presetOpen = false
289
357
  },
290
358
 
291
359
  copyPreset(){
292
- const preset = Object.assign({}, this.preset)
360
+ const preset = JSON.parse(JSON.stringify(this.preset))
293
361
  preset.name += '(Copy)'
294
362
  this.presets.push(preset)
295
363
  this.presetIdx = this.presets.length - 1
296
364
  },
297
365
 
366
+ removePreset(){
367
+ if(this.presets.length <= 1){
368
+ this.alert('Unable to remove preset')
369
+ }
370
+ else{
371
+ this.confirm('Remove this preset?', '', () => {
372
+ this.presets.splice(this.presetIdx, 1)
373
+ if(this.presetIdx > this.presets.length - 1){
374
+ this.presetIdx = this.presets.length - 1
375
+ }
376
+ })
377
+ }
378
+ },
379
+
298
380
  onHooks(module, event, items){
299
381
 
300
382
  console.log('onHooks', module, event, items)
@@ -339,7 +421,7 @@ export default{
339
421
 
340
422
  addFilter(key){
341
423
 
342
- const column = this.preset.columns.filter((_) => _.key === key).pop()
424
+ const column = this.columns[key]
343
425
 
344
426
  if(!this.preset.filters){
345
427
  this.preset.filters = []
@@ -49,13 +49,11 @@
49
49
  </div>
50
50
  </div>
51
51
 
52
- <div v-else-if="column && column.type === 'enum'">
53
-
54
- <div v-for="(obj, idx) in filter.typeParams" class="flex flex-row items-center gap-2">
55
- <input :id="`lp1${filter.key}${idx}`" v-model="item.value" type="checkbox" :value="obj.value" />
56
- <label :for="`lp1${filter.key}${idx}`" class="p-1">{{ obj.text }}</label>
57
- </div>
58
-
52
+ <div v-else-if="column && column.type === 'enum'" class="flex flex-col">
53
+ <Checkbox v-for="(obj, idx) in filter.typeParams" :value="obj.value" v-model="item.value"
54
+ @change="onFilterChange(item)">
55
+ {{ obj.text }}
56
+ </Checkbox>
59
57
  </div>
60
58
 
61
59
  <div v-else-if="column && column.type === 'currency'">
@@ -132,7 +130,7 @@
132
130
 
133
131
  export default{
134
132
 
135
- emits: [ 'remove' ],
133
+ emits: [ 'remove', 'change' ],
136
134
 
137
135
  props:{
138
136
  column: Object,
@@ -3,7 +3,7 @@
3
3
  <button v-for="(item, index) in items" :class="tabClass(item)"
4
4
  @click="onClick(item)" type="button" >
5
5
  <slot v-if="$slots.tab" name="tab" :="{ item, index }"></slot>
6
- <div v-else>
6
+ <div v-else :class="$style.item">
7
7
  {{ item.text }}
8
8
  </div>
9
9
  </button>
@@ -65,4 +65,8 @@ export default{
65
65
  @apply border-b-[2px] border-primary;
66
66
  }
67
67
 
68
+ .item{
69
+ @apply whitespace-nowrap overflow-hidden;
70
+ }
71
+
68
72
  </style>
@@ -133,9 +133,9 @@ export default{
133
133
  this.$emit('change', e.target.value)
134
134
  },
135
135
 
136
- onBlur(){
136
+ onBlur(e){
137
137
  this.isActive = false
138
- this.$emit('blur')
138
+ this.$emit('blur', e.target.value)
139
139
  },
140
140
 
141
141
  onKeyDown(e){
package/src/index.js CHANGED
@@ -61,6 +61,7 @@ export default{
61
61
  app.component('Ahref', defineAsyncComponent(() => import("./components/Ahref.vue")))
62
62
  app.component('Switch', defineAsyncComponent(() => import("./components/Switch.vue")))
63
63
  app.component('Image', defineAsyncComponent(() => import("./components/Image.vue")))
64
+ app.component('ImagePreview', defineAsyncComponent(() => import("./components/ImagePreview.vue")))
64
65
  app.component('ImageFullScreen', defineAsyncComponent(() => import("./components/ImageFullScreen.vue")))
65
66
  app.component('ListPage1', defineAsyncComponent(() => import("./components/ListPage1.vue")))
66
67
  app.component('ListPage2', defineAsyncComponent(() => import("./components/ListPage2.vue")))