@mixd-id/web-scaffold 0.1.230406001

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.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/package.json +71 -0
  4. package/public/images/mixd-logo2.png +0 -0
  5. package/src/App.vue +17 -0
  6. package/src/components/Ahref.vue +34 -0
  7. package/src/components/Alert.vue +160 -0
  8. package/src/components/Button.vue +253 -0
  9. package/src/components/ButtonGroup.vue +101 -0
  10. package/src/components/Carousel.vue +293 -0
  11. package/src/components/ChatTyping.vue +69 -0
  12. package/src/components/Checkbox.vue +152 -0
  13. package/src/components/ContextMenu.vue +261 -0
  14. package/src/components/CopyToClipboard.vue +59 -0
  15. package/src/components/Countdown.vue +213 -0
  16. package/src/components/Datepicker.vue +312 -0
  17. package/src/components/Dropdown.vue +198 -0
  18. package/src/components/DynamicTemplate.vue +44 -0
  19. package/src/components/ErrorText.vue +36 -0
  20. package/src/components/Feed.vue +118 -0
  21. package/src/components/Gmaps.vue +227 -0
  22. package/src/components/Grid.vue +29 -0
  23. package/src/components/GridColumn.vue +31 -0
  24. package/src/components/HTMLEditor.vue +396 -0
  25. package/src/components/Image.vue +207 -0
  26. package/src/components/Image360.vue +140 -0
  27. package/src/components/ImageFullScreen.vue +101 -0
  28. package/src/components/ImagePreview.vue +71 -0
  29. package/src/components/ImportModal.vue +247 -0
  30. package/src/components/ListItem.vue +147 -0
  31. package/src/components/ListPage1.vue +1331 -0
  32. package/src/components/ListPage1Filter.vue +170 -0
  33. package/src/components/Modal.vue +253 -0
  34. package/src/components/OTPField.vue +126 -0
  35. package/src/components/Radio.vue +134 -0
  36. package/src/components/SearchButton.vue +57 -0
  37. package/src/components/Slider.vue +285 -0
  38. package/src/components/SplitPane.vue +129 -0
  39. package/src/components/Switch.vue +89 -0
  40. package/src/components/TabView.vue +106 -0
  41. package/src/components/TableView.vue +201 -0
  42. package/src/components/TableViewHead.vue +159 -0
  43. package/src/components/Tabs.vue +74 -0
  44. package/src/components/TextEditor.vue +85 -0
  45. package/src/components/Textarea.vue +184 -0
  46. package/src/components/Textbox.vue +200 -0
  47. package/src/components/Timepicker.vue +108 -0
  48. package/src/components/Toast.vue +93 -0
  49. package/src/components/VirtualScroll.vue +215 -0
  50. package/src/components/VirtualTable.vue +497 -0
  51. package/src/entry-client.js +27 -0
  52. package/src/entry-server.js +73 -0
  53. package/src/index.css +3 -0
  54. package/src/index.js +255 -0
  55. package/src/main.js +38 -0
  56. package/src/router.js +57 -0
  57. package/src/themes/default/index.js +200 -0
  58. package/src/utils/helpers.js +185 -0
  59. package/src/utils/helpers.mjs +197 -0
  60. package/src/utils/importer.js +156 -0
  61. package/src/utils/listpage1.js +1371 -0
  62. package/src/utils/selection.js +64 -0
@@ -0,0 +1,312 @@
1
+ <template>
2
+
3
+ <div :class="className">
4
+
5
+ <div v-if="mode === '' || mode === 'simple'">
6
+ <select v-model="DD">
7
+ <option disabled selected>Tanggal</option>
8
+ <option v-for="d in 31" :value="d.toString().padStart(2, '0')">{{ d }}</option>
9
+ </select>
10
+ <select v-model="MM">
11
+ <option disabled selected>Bulan</option>
12
+ <option v-for="mmm in months" :value="mmm[0]">{{ mmm[1] }}</option>
13
+ </select>
14
+ <select v-model="YYYY">
15
+ <option disabled selected>Tahun</option>
16
+ <option v-for="yyyy in years" :value="yyyy">{{ yyyy }}</option>
17
+ </select>
18
+ </div>
19
+
20
+ <div v-else-if="mode === 'popup'" ref="popup" @click="contextMenu = { caller:$refs.popup, value:this.modelValue }"
21
+ class="flex-1">
22
+ <input class="flex-1" type="text" readonly :value="DMMMYYYY"/>
23
+ <div :class="$style.arrow">
24
+ <svg width="16" height="16" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
25
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M4.96967 8.71967C5.26256 8.42678 5.73744 8.42678 6.03033 8.71967L11.8232 14.5126C11.9209 14.6102 12.0791 14.6102 12.1768 14.5126L17.9697 8.71967C18.2626 8.42677 18.7374 8.42677 19.0303 8.71967C19.3232 9.01256 19.3232 9.48743 19.0303 9.78033L13.2374 15.5732C12.554 16.2566 11.446 16.2566 10.7626 15.5732L4.96967 9.78033C4.67678 9.48744 4.67678 9.01256 4.96967 8.71967Z"/>
26
+ </svg>
27
+ </div>
28
+ </div>
29
+
30
+ <div v-else-if="mode === 'calendar'" class="p-2">
31
+ <div class="flex items-center">
32
+ <h4 class="flex-1">{{ contextMenuTitle }}</h4>
33
+ <button type="button" @click.stop="contextMenuPrevMonth()" class="p-2">
34
+ <svg width="24" height="24" class="fill-text" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
35
+ <path d="M10.5303 17.9697C10.8232 18.2626 10.8232 18.7374 10.5303 19.0303C10.2374 19.3232 9.76253 19.3232 9.46964 19.0303L3.67675 13.2374C2.99333 12.554 2.99333 11.446 3.67675 10.7626L9.46964 4.96967C9.76253 4.67678 10.2374 4.67678 10.5303 4.96967C10.8232 5.26256 10.8232 5.73744 10.5303 6.03033L5.31063 11.25H20C20.4142 11.25 20.75 11.5858 20.75 12C20.75 12.4142 20.4142 12.75 20 12.75H5.31063L10.5303 17.9697Z"/>
36
+ </svg>
37
+ </button>
38
+ <button type="button" @click.stop="contextMenuNextMonth()" class="p-2">
39
+ <svg width="24" height="24" class="fill-text" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
40
+ <path d="M13.4697 17.9697C13.1768 18.2626 13.1768 18.7374 13.4697 19.0303C13.7626 19.3232 14.2374 19.3232 14.5303 19.0303L20.3232 13.2374C21.0066 12.554 21.0066 11.446 20.3232 10.7626L14.5303 4.96967C14.2374 4.67678 13.7626 4.67678 13.4697 4.96967C13.1768 5.26256 13.1768 5.73744 13.4697 6.03033L18.6893 11.25H4C3.58579 11.25 3.25 11.5858 3.25 12C3.25 12.4142 3.58579 12.75 4 12.75H18.6893L13.4697 17.9697Z"/>
41
+ </svg>
42
+ </button>
43
+ </div>
44
+ <div class="grid grid-cols-7 gap-2">
45
+ <div v-for="i in 7">{{ getDayOfWeekLabel(i) }}</div>
46
+ <button type="button" :class="buttonStyle(d.value)"
47
+ v-for="d in contextMenuDates" @click="setValue(d.value)">
48
+ {{ d.date }}
49
+ </button>
50
+ </div>
51
+ </div>
52
+
53
+ <ContextMenu v-if="mode === 'popup'" :state="!!(contextMenu.caller)" :caller="contextMenu.caller ?? null"
54
+ @dismiss="contextMenu = {}">
55
+ <div class="p-4">
56
+ <div class="flex items-center">
57
+ <h4 class="flex-1">{{ contextMenuTitle }}</h4>
58
+ <button type="button" @click.stop="contextMenuPrevMonth()" class="p-2">
59
+ <svg width="24" height="24" class="fill-text" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
60
+ <path d="M10.5303 17.9697C10.8232 18.2626 10.8232 18.7374 10.5303 19.0303C10.2374 19.3232 9.76253 19.3232 9.46964 19.0303L3.67675 13.2374C2.99333 12.554 2.99333 11.446 3.67675 10.7626L9.46964 4.96967C9.76253 4.67678 10.2374 4.67678 10.5303 4.96967C10.8232 5.26256 10.8232 5.73744 10.5303 6.03033L5.31063 11.25H20C20.4142 11.25 20.75 11.5858 20.75 12C20.75 12.4142 20.4142 12.75 20 12.75H5.31063L10.5303 17.9697Z"/>
61
+ </svg>
62
+ </button>
63
+ <button type="button" @click.stop="contextMenuNextMonth()" class="p-2">
64
+ <svg width="24" height="24" class="fill-text" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
65
+ <path d="M13.4697 17.9697C13.1768 18.2626 13.1768 18.7374 13.4697 19.0303C13.7626 19.3232 14.2374 19.3232 14.5303 19.0303L20.3232 13.2374C21.0066 12.554 21.0066 11.446 20.3232 10.7626L14.5303 4.96967C14.2374 4.67678 13.7626 4.67678 13.4697 4.96967C13.1768 5.26256 13.1768 5.73744 13.4697 6.03033L18.6893 11.25H4C3.58579 11.25 3.25 11.5858 3.25 12C3.25 12.4142 3.58579 12.75 4 12.75H18.6893L13.4697 17.9697Z"/>
66
+ </svg>
67
+ </button>
68
+ </div>
69
+ <div class="grid grid-cols-7 gap-2">
70
+ <div v-for="i in 7">{{ getDayOfWeekLabel(i) }}</div>
71
+ <button type="button" :class="buttonStyle(d.value)"
72
+ v-for="d in contextMenuDates" @click="setValue(d.value)">
73
+ {{ d.date }}
74
+ </button>
75
+ </div>
76
+ </div>
77
+ </ContextMenu>
78
+
79
+ </div>
80
+
81
+ </template>
82
+
83
+ <script>
84
+ import dayjs from 'dayjs'
85
+ import {ref, watch} from "vue";
86
+
87
+ const MONTHS = []
88
+ for(let i = 0 ; i < 12 ; i++){
89
+ MONTHS.push([ dayjs(new Date(2020, i, 1)).format('MM'), dayjs(new Date(2020, i, 1)).format('MMM')])
90
+ }
91
+
92
+ const YEARS = []
93
+ const currentYear = parseInt(dayjs().format('YYYY'))
94
+ for(let i = -100 ; i < 50 ; i++){
95
+ YEARS.push(currentYear + i)
96
+ }
97
+
98
+ const currentDate = dayjs().format('YYYY-MM-DD')
99
+
100
+ export default{
101
+
102
+ setup(props, { emit }){
103
+
104
+ const DD = ref(dayjs(props.modelValue).format('DD'))
105
+ const MM = ref(dayjs(props.modelValue).format('MM'))
106
+ const YYYY = ref(dayjs(props.modelValue).format('YYYY'))
107
+
108
+ watch([ YYYY, MM, DD ], (to) => {
109
+ emit('update:modelValue', to.join('-'))
110
+ })
111
+
112
+ return {
113
+ DD,
114
+ MM,
115
+ YYYY
116
+ }
117
+ },
118
+
119
+ props:{
120
+
121
+ mode:{
122
+ type: [ String ],
123
+ default: '' // simple|calendar|popup|range, default: simple
124
+ },
125
+
126
+ variant:{
127
+ type: String,
128
+ default: ''
129
+ },
130
+
131
+ size:{
132
+ type: String,
133
+ default: ''
134
+ },
135
+
136
+ modelValue:{
137
+ type: String,
138
+ default: currentDate
139
+ }
140
+
141
+ },
142
+
143
+ computed:{
144
+
145
+ DMMMYYYY(){
146
+ return this.modelValue ? dayjs(this.modelValue).format('D MMM YYYY') : ''
147
+ },
148
+
149
+ YYYYMMM(){
150
+ return dayjs(this.contextMenu.value).format('YYYY-MM')
151
+ },
152
+
153
+ className(){
154
+ return [
155
+ this.$style.datepicker,
156
+ this.$style['mode-' + this.mode]
157
+ ]
158
+ .join(' ')
159
+ .trim()
160
+ },
161
+
162
+ contextMenuTitle(){
163
+ return dayjs(this.contextMenu.value ?? undefined).format('MMM YYYY')
164
+ },
165
+
166
+ contextMenuDates(){
167
+
168
+ const dayjsInput = dayjs(this.contextMenu.value ?? undefined)
169
+ const year = parseInt(dayjsInput.format('YYYY'))
170
+ const month = parseInt(dayjsInput.format('M')) - 1
171
+
172
+ const dates = []
173
+ let startDayOfMonth = dayjs(new Date(year, month, 1)).format('d')
174
+ let endDateOfMonth = dayjs(new Date(year, month + 1, 0)).format('D')
175
+ let endDateOfLastMonth = dayjs(new Date(year, month, 0)).format('D')
176
+ let currentDate = 1
177
+ let nextDate = 1
178
+ for(let i = 1 ; i <= 42 ; i++){
179
+ if(startDayOfMonth > 0){
180
+ startDayOfMonth--
181
+ dates.push({
182
+ date: endDateOfLastMonth - startDayOfMonth,
183
+ value: year + '-' + month.toString().padStart(2, '0') + '-' + (endDateOfLastMonth - startDayOfMonth).toString().padStart(2, '0'),
184
+ type: -1
185
+ })
186
+ }
187
+ else if(currentDate > endDateOfMonth){
188
+ dates.push({
189
+ date: nextDate,
190
+ value: year + '-' + (month + 2).toString().padStart(2, '0') + '-' + nextDate.toString().padStart(2, '0'),
191
+ type: 1
192
+ })
193
+ nextDate++
194
+ }
195
+ else{
196
+ dates.push({
197
+ date:currentDate,
198
+ value: year + '-' + (month + 1).toString().padStart(2, '0') + '-' + currentDate.toString().padStart(2, '0'),
199
+ type: 0
200
+ })
201
+ currentDate++
202
+ }
203
+ }
204
+
205
+ return dates
206
+ }
207
+
208
+ },
209
+
210
+ data(){
211
+ return {
212
+ months: MONTHS,
213
+ years: YEARS,
214
+ contextMenu: {}
215
+ }
216
+ },
217
+
218
+ watch:{
219
+
220
+ },
221
+
222
+ methods:{
223
+
224
+ buttonStyle(val){
225
+ return [
226
+ this.$style.button,
227
+ this.modelValue === val ? this.$style.selected : '',
228
+ dayjs(val).format('YYYY-MM') !== this.YYYYMMM ? this.$style.otherMonth : ''
229
+ ]
230
+ .join(' ')
231
+ },
232
+
233
+ getDayOfWeekLabel(i){
234
+ return dayjs('2022-07-30').add(i, 'day').format('ddd')
235
+ },
236
+
237
+ contextMenuNextMonth(){
238
+ this.contextMenu.value = dayjs(this.contextMenu.value ?? undefined).add(1, 'month').format('YYYY-MM-DD')
239
+ },
240
+
241
+ contextMenuPrevMonth(){
242
+
243
+ this.contextMenu.value = dayjs(this.contextMenu.value ?? undefined).add(-1, 'month').format('YYYY-MM-DD')
244
+
245
+ },
246
+
247
+ setValue(d){
248
+ this.$emit('update:modelValue', d)
249
+ this.$emit('change', d)
250
+ }
251
+
252
+ },
253
+
254
+ mounted() {
255
+ }
256
+
257
+ }
258
+
259
+ </script>
260
+
261
+ <style module>
262
+
263
+ .datepicker {
264
+ @apply h-[var(--h-cp)];
265
+ @apply flex items-center rounded-lg overflow-hidden cursor-pointer relative;
266
+ @apply border-[1px] border-text-200 bg-base-50;
267
+ @apply hover:border-text-300 cursor-pointer;
268
+ }
269
+ .datepicker select{
270
+ @apply appearance-none p-2 bg-transparent text-center w-full outline-none;
271
+ }
272
+ .datepicker input{
273
+ @apply appearance-none p-2 bg-transparent w-full outline-none;
274
+ }
275
+ .datepicker label{
276
+ @apply block p-2 text-center;
277
+ }
278
+ .datepicker input[type=radio]{
279
+ @apply hidden;
280
+ }
281
+ .datepicker input[type=radio]:checked + label{
282
+ }
283
+
284
+ .button{
285
+ @apply rounded-full aspect-square;
286
+ }
287
+ .button.otherMonth{
288
+ @apply text-text-300;
289
+ }
290
+
291
+ .arrow{
292
+ @apply absolute right-0 pointer-events-none bg-base-50 px-2;
293
+ top: 50%;
294
+ transform: translate3d(0, -50%, 0);
295
+ }
296
+ .arrow svg{
297
+ @apply block fill-text-300;
298
+ }
299
+
300
+ .datepicker.mode-calendar{
301
+ @apply p-2 h-auto;
302
+ }
303
+
304
+ .datepicker-date--1, .datepicker-date-1{
305
+ @apply text-gray-500;
306
+ }
307
+
308
+ .selected{
309
+ @apply bg-primary;
310
+ }
311
+
312
+ </style>
@@ -0,0 +1,198 @@
1
+ <template>
2
+
3
+ <div :class="computedClass">
4
+
5
+ <select v-if="[ 1 ].includes(mode)" class="flex-1" ref="select" @change="onChanged" :disabled="disabled"
6
+ :value="modelValue" @input="$emit('update:modelValue', $event.target.value)">
7
+ <slot></slot>
8
+ </select>
9
+
10
+ <input v-else-if="[ 2, 'custom' ].includes(mode)" type="text" class="flex-1" readonly :disabled="disabled"
11
+ @click="openContextMenu" ref="input"
12
+ :value="modelValue ?? value" @input="$emit('update:modelValue', $event.target.value)" />
13
+ <div :class="$style.arrow">
14
+ <svg width="16" height="16" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
15
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M4.96967 8.71967C5.26256 8.42678 5.73744 8.42678 6.03033 8.71967L11.8232 14.5126C11.9209 14.6102 12.0791 14.6102 12.1768 14.5126L17.9697 8.71967C18.2626 8.42677 18.7374 8.42677 19.0303 8.71967C19.3232 9.01256 19.3232 9.48743 19.0303 9.78033L13.2374 15.5732C12.554 16.2566 11.446 16.2566 10.7626 15.5732L4.96967 9.78033C4.67678 9.48744 4.67678 9.01256 4.96967 8.71967Z"/>
16
+ </svg>
17
+ </div>
18
+
19
+ <ContextMenu v-if="[ 2, 'custom' ].includes(mode)" ref="contextMenu" :position="position"
20
+ :state="!!contextMenu"
21
+ :caller="(contextMenu ?? {}).caller ?? null"
22
+ @dismiss="contextMenu = null"
23
+ @open="attachEvent">
24
+ <slot></slot>
25
+ </ContextMenu>
26
+ </div>
27
+
28
+ </template>
29
+
30
+ <script>
31
+
32
+ export default {
33
+
34
+ props: {
35
+
36
+ mode: {
37
+ type: [ String, Number ],
38
+ default: 1
39
+ },
40
+
41
+ disabled: undefined,
42
+
43
+ modelValue: [ String, Number ],
44
+
45
+ variant: String,
46
+
47
+ size: String,
48
+
49
+ position: String,
50
+
51
+ width: String,
52
+
53
+ state: {
54
+ type: [ Number, String ],
55
+ default: 1 // 1:normal, -1:disabled, -2:error
56
+ },
57
+
58
+ errors:{
59
+ type: [ String, Array ],
60
+ default: ''
61
+ },
62
+
63
+ value: undefined
64
+
65
+ },
66
+
67
+ computed:{
68
+
69
+ computedState(){
70
+ if(this.errors){
71
+ return -2
72
+ }
73
+ else{
74
+ return this.state
75
+ }
76
+ },
77
+
78
+ computedClass(){
79
+
80
+ return [
81
+ this.$style.dropdown,
82
+ this.$style['state-' + this.computedState],
83
+ this.$style['variant-' + this.variant],
84
+ this.$style['size-' + this.size],
85
+ ]
86
+ .join(' ')
87
+ }
88
+
89
+ },
90
+
91
+ emits: [
92
+ "update:modelValue", "change"
93
+ ],
94
+
95
+ data(){
96
+ return {
97
+ contextMenu: null,
98
+ contextMenuLeft: null,
99
+ contextMenuTop: null,
100
+ }
101
+ },
102
+
103
+ mounted(){
104
+
105
+ },
106
+
107
+ methods: {
108
+
109
+ attachEvent(){
110
+ this.$refs.contextMenu.$refs.contextMenu.querySelectorAll('[data-value]').forEach((itemEl) => {
111
+ itemEl.addEventListener('click', this.onItemClick)
112
+ })
113
+ },
114
+
115
+ onItemClick(e){
116
+
117
+ let [ value, text ] = e.target.closest('[data-value]').getAttribute('data-value').split('|')
118
+ if(!text) text = value
119
+
120
+ this.$refs.input.value = text
121
+ this.contextMenu = null
122
+ this.$emit('update:modelValue', value)
123
+ this.$emit('change', value)
124
+ },
125
+
126
+ onChanged(){
127
+ this.$emit('update:modelValue', this.$refs.select.value)
128
+ this.$emit('change', this.$refs.select.value)
129
+ },
130
+
131
+ openContextMenu(){
132
+
133
+ const { x, y } = this.$el.getBoundingClientRect()
134
+ this.contextMenuLeft = Math.round(x)
135
+ this.contextMenuTop = Math.round(y)
136
+
137
+ this.contextMenu = {
138
+ caller: this.$el
139
+ }
140
+
141
+ }
142
+
143
+ }
144
+
145
+
146
+ }
147
+
148
+ </script>
149
+
150
+ <style module>
151
+
152
+ .dropdown{
153
+ @apply h-[var(--h-cp)];
154
+ @apply flex items-center rounded-lg overflow-hidden cursor-pointer relative;
155
+ @apply border-[1px] border-text-200 bg-base-50 hover:border-text-300;
156
+ }
157
+
158
+ .dropdown select,
159
+ .dropdown input{
160
+ @apply appearance-none outline-none p-2 bg-transparent cursor-pointer;
161
+ }
162
+
163
+ .arrow{
164
+ @apply absolute right-0 pointer-events-none px-2;
165
+ top: 50%;
166
+ transform: translate3d(0, -50%, 0);
167
+ }
168
+
169
+ .arrow svg{
170
+ @apply block fill-text-200;
171
+ }
172
+ .dropdown:hover svg{
173
+ @apply fill-text-300;
174
+ }
175
+
176
+
177
+ .size-sm{
178
+ @apply border-[1px]
179
+ }
180
+
181
+ .size-sm select,
182
+ .size-sm input{
183
+ @apply p-0;
184
+ }
185
+
186
+ .size-sm .arrow{
187
+ @apply p-2
188
+ }
189
+
190
+ .variant-transparent{
191
+ @apply bg-transparent border-transparent hover:border-transparent
192
+ }
193
+
194
+ .state--2{
195
+ @apply border-red-500
196
+ }
197
+
198
+ </style>
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <div :class="$style.comp">
3
+ <component :is="components"></component>
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+
9
+ import * as Vue from "vue/dist/vue.esm-bundler";
10
+
11
+ export default{
12
+
13
+ props: [ 'template' ],
14
+
15
+ data(){
16
+ return {
17
+ components: null
18
+ }
19
+ },
20
+
21
+ watch: {
22
+
23
+ template: {
24
+ immediate: true,
25
+ handler(to){
26
+ this.components = Vue.compile(to)
27
+ }
28
+ }
29
+
30
+ }
31
+
32
+
33
+
34
+ }
35
+
36
+ </script>
37
+
38
+ <style module>
39
+
40
+ .comp{
41
+
42
+ }
43
+
44
+ </style>
@@ -0,0 +1,36 @@
1
+ <template>
2
+ <div :class="$style.comp" v-if="error" @click="$emit('dismissError')">
3
+ <div v-if="error.response && error.response.data && error.response.data.errors">
4
+ <div v-for="(text, key) in error.response.data.errors">
5
+ <div v-if="!errorKey || key === errorKey">
6
+ <div v-for="txt in text">{{ txt }}</div>
7
+ </div>
8
+ </div>
9
+ </div>
10
+ <div v-else-if="error.message" class="p-2 bg-base-500">{{ error.message }}</div>
11
+ <div v-else>
12
+ <pre>{{ error }}</pre>
13
+ </div>
14
+ </div>
15
+ </template>
16
+
17
+ <script>
18
+
19
+ export default{
20
+
21
+ props: {
22
+ error: Object,
23
+ errorKey: String
24
+ }
25
+
26
+ }
27
+
28
+ </script>
29
+
30
+ <style module>
31
+
32
+ .comp *{
33
+ @apply text-red-500;
34
+ }
35
+
36
+ </style>
@@ -0,0 +1,118 @@
1
+ <template>
2
+
3
+ <div :class="$style.comp">
4
+ <div v-if="items" v-for="(item, idx) in items">
5
+ <slot name="item" :item="item"></slot>
6
+ </div>
7
+ <div @click="loadNext" ref="next">
8
+ <div v-if="nextPage && nextPage > page && this.state === 1">
9
+ <slot v-if="$slots.next" name="next" :nextPage="nextPage" :page="page"></slot>
10
+ <div v-else class="flex items-center justify-center cursor-pointer">
11
+ Load more...
12
+ </div>
13
+ </div>
14
+ </div>
15
+ <div v-if="state === 2">
16
+ <slot v-if="$slots.loading" name="loading"></slot>
17
+ <div v-else class="flex items-center justify-center p-2">
18
+ <svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
19
+ <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
20
+ <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>
21
+ </svg>
22
+ </div>
23
+ </div>
24
+ </div>
25
+
26
+ </template>
27
+
28
+ <script>
29
+
30
+ export default{
31
+
32
+ props:{
33
+
34
+ src: [ String, Function ],
35
+
36
+ itemsPerPage: {
37
+ type: [ Number, String ],
38
+ default: 12
39
+ },
40
+
41
+ manual: {
42
+ type: Boolean,
43
+ default: false
44
+ }
45
+
46
+ },
47
+
48
+ mounted() {
49
+ if(!this.manual){
50
+ this.load()
51
+ }
52
+ },
53
+
54
+ data(){
55
+ return {
56
+ page: 1,
57
+ nextPage: null,
58
+ items: null,
59
+ state: 1,
60
+ }
61
+ },
62
+
63
+ methods:{
64
+
65
+ async load(){
66
+ if(typeof this.src === 'function'){
67
+
68
+ this.state = 2
69
+ this.items = null
70
+ this.page = 1
71
+ const { items, page, nextPage } = await this.src({
72
+ page: this.page,
73
+ itemsPerPage: this.itemsPerPage
74
+ })
75
+ this.state = 1
76
+
77
+ this.items = items
78
+ this.nextPage = nextPage
79
+ if(page) this.page = page
80
+
81
+ this.$observe.always(this.$refs.next, () => {
82
+ this.loadNext()
83
+ })
84
+ }
85
+ },
86
+
87
+ async loadNext(){
88
+
89
+ if(!(this.nextPage && this.nextPage > this.page)) return
90
+
91
+ if(typeof this.src === 'function'){
92
+
93
+ this.state = 2
94
+ const { items, page, nextPage } = await this.src({
95
+ page: this.nextPage,
96
+ itemsPerPage: this.itemsPerPage
97
+ })
98
+ this.state = 1
99
+
100
+ this.items.push(...items)
101
+ this.nextPage = nextPage
102
+ if(page) this.page = page
103
+ }
104
+ }
105
+
106
+ }
107
+
108
+ }
109
+
110
+ </script>
111
+
112
+ <style module>
113
+
114
+ .comp{
115
+
116
+ }
117
+
118
+ </style>