adminforth 1.0.31 → 1.0.33
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/dataConnectors/postgres.ts +9 -6
- package/dataConnectors/sqlite.ts +1 -0
- package/dist/dataConnectors/postgres.js +9 -6
- package/dist/dataConnectors/sqlite.js +1 -0
- package/dist/index.js +87 -65
- package/dist/modules/utils.js +9 -0
- package/dist/spa/spa/src/App.vue +0 -7
- package/dist/spa/spa/src/components/CustomDatePicker.vue +3 -3
- package/dist/spa/spa/src/components/CustomDateRangePicker.vue +3 -3
- package/dist/spa/spa/src/components/CustomRangePicker.vue +32 -19
- package/dist/spa/spa/src/components/Dropdown.vue +6 -2
- package/dist/spa/spa/src/components/ResourceForm.vue +125 -102
- package/dist/spa/spa/src/components/ValueRenderer.vue +11 -1
- package/dist/spa/spa/src/stores/core.ts +21 -19
- package/dist/spa/spa/src/views/CreateView.vue +16 -6
- package/dist/spa/spa/src/views/EditView.vue +15 -5
- package/dist/spa/spa/src/views/ListView.vue +31 -21
- package/dist/spa/spa/src/views/ResourceParent.vue +1 -1
- package/dist/spa/spa/src/views/ShowView.vue +21 -6
- package/dist/types/AdminForthConfig.js +1 -0
- package/index.ts +73 -141
- package/modules/utils.ts +14 -0
- package/package.json +1 -1
- package/spa/src/App.vue +0 -7
- package/spa/src/components/CustomDatePicker.vue +3 -3
- package/spa/src/components/CustomDateRangePicker.vue +3 -3
- package/spa/src/components/CustomRangePicker.vue +32 -19
- package/spa/src/components/Dropdown.vue +6 -2
- package/spa/src/components/ResourceForm.vue +125 -102
- package/spa/src/components/ValueRenderer.vue +11 -1
- package/spa/src/stores/core.ts +21 -19
- package/spa/src/views/CreateView.vue +16 -6
- package/spa/src/views/EditView.vue +15 -5
- package/spa/src/views/ListView.vue +31 -21
- package/spa/src/views/ResourceParent.vue +1 -1
- package/spa/src/views/ShowView.vue +21 -6
- package/types/AdminForthConfig.ts +159 -0
package/index.ts
CHANGED
|
@@ -8,109 +8,13 @@ import { guessLabelFromName } from './modules/utils.js';
|
|
|
8
8
|
import ExpressServer from './servers/express.js';
|
|
9
9
|
import {v1 as uuid} from 'uuid';
|
|
10
10
|
import fs from 'fs';
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
import { ADMINFORTH_VERSION } from './modules/utils.js';
|
|
13
12
|
import { AdminForthFilterOperators, AdminForthTypes, AdminForthTypesValues } from './types.js';
|
|
13
|
+
import { AdminForthConfig } from './types/AdminForthConfig.js';
|
|
14
14
|
|
|
15
15
|
const AVAILABLE_SHOW_IN = ['list', 'edit', 'create', 'filter', 'show'];
|
|
16
16
|
const DEFAULT_ALLOWED_ACTIONS = {create: true, edit: true, show: true, delete: true};
|
|
17
17
|
|
|
18
|
-
type AdminForthConfigMenuItem = {
|
|
19
|
-
label: string,
|
|
20
|
-
icon?: string,
|
|
21
|
-
path?: string,
|
|
22
|
-
component?: string,
|
|
23
|
-
resourceId?: string,
|
|
24
|
-
homepage?: boolean,
|
|
25
|
-
children?: Array<AdminForthConfigMenuItem>,
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
type AdminForthResourceColumn = {
|
|
30
|
-
name: string,
|
|
31
|
-
label?: string,
|
|
32
|
-
type?: AdminForthTypesValues,
|
|
33
|
-
primaryKey?: boolean,
|
|
34
|
-
required?: boolean | { create: boolean, edit: boolean },
|
|
35
|
-
editingNote?: string | { create: string, edit: string },
|
|
36
|
-
showIn?: Array<string>,
|
|
37
|
-
fillOnCreate?: Function,
|
|
38
|
-
isUnique?: boolean,
|
|
39
|
-
virtual?: boolean,
|
|
40
|
-
allowMinMaxQuery?: boolean,
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
type AdminForthResource = {
|
|
44
|
-
resourceId: string,
|
|
45
|
-
label?: string,
|
|
46
|
-
table: string,
|
|
47
|
-
dataSource: string,
|
|
48
|
-
columns: Array<AdminForthResourceColumn>,
|
|
49
|
-
itemLabel?: Function,
|
|
50
|
-
hooks?: {
|
|
51
|
-
show?: Function,
|
|
52
|
-
create?: {
|
|
53
|
-
beforeSave?: Function,
|
|
54
|
-
afterSave?: Function,
|
|
55
|
-
},
|
|
56
|
-
edit?: {
|
|
57
|
-
beforeSave?: Function,
|
|
58
|
-
afterSave?: Function,
|
|
59
|
-
},
|
|
60
|
-
delete?: {
|
|
61
|
-
beforeSave?: Function,
|
|
62
|
-
afterSave?: Function,
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
options?: {
|
|
66
|
-
bulkActions?: Array<{
|
|
67
|
-
label: string,
|
|
68
|
-
state: string,
|
|
69
|
-
icon: string,
|
|
70
|
-
action: Function,
|
|
71
|
-
}>,
|
|
72
|
-
allowedActions?: AllowedActions,
|
|
73
|
-
|
|
74
|
-
},
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
type AdminForthDataSource = {
|
|
78
|
-
id: string,
|
|
79
|
-
url: string,
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
type AdminForthConfig = {
|
|
83
|
-
rootUser?: {
|
|
84
|
-
username: string,
|
|
85
|
-
password: string,
|
|
86
|
-
},
|
|
87
|
-
auth?: {
|
|
88
|
-
resourceId: string,
|
|
89
|
-
usernameField: string,
|
|
90
|
-
passwordHashField: string,
|
|
91
|
-
loginBackgroundImage?: string,
|
|
92
|
-
userFullName?: string,
|
|
93
|
-
},
|
|
94
|
-
resources: Array<any>,
|
|
95
|
-
menu: Array<AdminForthConfigMenuItem>,
|
|
96
|
-
databaseConnectors?: any,
|
|
97
|
-
dataSources: Array<any>,
|
|
98
|
-
customization?: {
|
|
99
|
-
customComponentsDir?: string,
|
|
100
|
-
vueUsesFile?: string,
|
|
101
|
-
},
|
|
102
|
-
baseUrl?: string,
|
|
103
|
-
brandName?: string,
|
|
104
|
-
datesFormat?: string,
|
|
105
|
-
deleteConfirmation?: boolean,
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
type AllowedActions = {
|
|
109
|
-
create: boolean,
|
|
110
|
-
edit: boolean,
|
|
111
|
-
show: boolean,
|
|
112
|
-
delete: boolean,
|
|
113
|
-
}
|
|
114
18
|
|
|
115
19
|
class AdminForth {
|
|
116
20
|
static Types = AdminForthTypes;
|
|
@@ -124,8 +28,6 @@ class AdminForth {
|
|
|
124
28
|
|
|
125
29
|
#defaultConfig = {
|
|
126
30
|
deleteConfirmation: true,
|
|
127
|
-
|
|
128
|
-
|
|
129
31
|
}
|
|
130
32
|
|
|
131
33
|
config: AdminForthConfig;
|
|
@@ -150,6 +52,7 @@ class AdminForth {
|
|
|
150
52
|
this.codeInjector = new CodeInjector(this);
|
|
151
53
|
this.connectors = {};
|
|
152
54
|
this.statuses = {}
|
|
55
|
+
console.log(`🚀 AdminForth v${ADMINFORTH_VERSION} starting up`)
|
|
153
56
|
}
|
|
154
57
|
|
|
155
58
|
validateConfig() {
|
|
@@ -396,7 +299,7 @@ class AdminForth {
|
|
|
396
299
|
if (!this.config.databaseConnectors[dbType]) {
|
|
397
300
|
throw new Error(`Database type ${dbType} is not supported, consider using databaseConnectors in AdminForth config`);
|
|
398
301
|
}
|
|
399
|
-
this.connectors[ds.id] = new this.config.databaseConnectors[dbType]({url: ds.url
|
|
302
|
+
this.connectors[ds.id] = new this.config.databaseConnectors[dbType]({url: ds.url});
|
|
400
303
|
});
|
|
401
304
|
|
|
402
305
|
await Promise.all(this.config.resources.map(async (res) => {
|
|
@@ -434,10 +337,6 @@ class AdminForth {
|
|
|
434
337
|
// console.log('⚙️⚙️⚙️ Database discovery done', JSON.stringify(this.config.resources, null, 2));
|
|
435
338
|
}
|
|
436
339
|
|
|
437
|
-
async init() {
|
|
438
|
-
console.log('AdminForth init');
|
|
439
|
-
}
|
|
440
|
-
|
|
441
340
|
async bundleNow({ hotReload=false, verbose=false }) {
|
|
442
341
|
this.codeInjector.bundleNow({ hotReload, verbose });
|
|
443
342
|
}
|
|
@@ -546,12 +445,12 @@ class AdminForth {
|
|
|
546
445
|
return { error: 'Unauthorized' };
|
|
547
446
|
}
|
|
548
447
|
username = user.data[0][this.config.auth.usernameField];
|
|
549
|
-
userFullName = user.data[0][this.config.auth.
|
|
448
|
+
userFullName = user.data[0][this.config.auth.userFullNameField];
|
|
550
449
|
}
|
|
551
450
|
|
|
552
451
|
const userData = {
|
|
553
452
|
[this.config.auth.usernameField]: username,
|
|
554
|
-
[this.config.auth.
|
|
453
|
+
[this.config.auth.userFullNameField]: userFullName
|
|
555
454
|
};
|
|
556
455
|
return {
|
|
557
456
|
user: userData,
|
|
@@ -568,6 +467,7 @@ class AdminForth {
|
|
|
568
467
|
usernameField: this.config.auth.usernameField,
|
|
569
468
|
},
|
|
570
469
|
adminUser,
|
|
470
|
+
version: ADMINFORTH_VERSION,
|
|
571
471
|
};
|
|
572
472
|
},
|
|
573
473
|
});
|
|
@@ -593,8 +493,11 @@ class AdminForth {
|
|
|
593
493
|
server.endpoint({
|
|
594
494
|
method: 'POST',
|
|
595
495
|
path: '/get_resource_data',
|
|
596
|
-
handler: async ({ body }) => {
|
|
597
|
-
const { resourceId,
|
|
496
|
+
handler: async ({ body, adminUser }) => {
|
|
497
|
+
const { resourceId, source } = body;
|
|
498
|
+
if (['show', 'list'].includes(source) === false) {
|
|
499
|
+
return { error: 'Invalid source, should be list or show' };
|
|
500
|
+
}
|
|
598
501
|
if (!this.statuses.dbDiscover) {
|
|
599
502
|
return { error: 'Database discovery not started' };
|
|
600
503
|
}
|
|
@@ -605,6 +508,19 @@ class AdminForth {
|
|
|
605
508
|
if (!resource) {
|
|
606
509
|
return { error: `Resource ${resourceId} not found` };
|
|
607
510
|
}
|
|
511
|
+
if (resource.hooks?.[source]?.beforeDatasourceRequest) {
|
|
512
|
+
const resp = await resource.hooks?.[source]?.beforeDatasourceRequest({ resource, query: body, adminUser });
|
|
513
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
514
|
+
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (resp.error) {
|
|
518
|
+
return { error: resp.error };
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
const { limit, offset, filters, sort } = body;
|
|
522
|
+
|
|
523
|
+
|
|
608
524
|
const data = await this.connectors[resource.dataSource].getData({
|
|
609
525
|
resource,
|
|
610
526
|
limit,
|
|
@@ -612,6 +528,50 @@ class AdminForth {
|
|
|
612
528
|
filters,
|
|
613
529
|
sort,
|
|
614
530
|
});
|
|
531
|
+
// for foreign keys, add references
|
|
532
|
+
await Promise.all(
|
|
533
|
+
resource.columns.filter((col) => col.foreignResource).map(async (col) => {
|
|
534
|
+
const targetResource = this.config.resources.find((res) => res.resourceId == col.foreignResource.resourceId);
|
|
535
|
+
const targetConnector = this.connectors[targetResource.dataSource];
|
|
536
|
+
const targetResourcePkField = targetResource.columns.find((col) => col.primaryKey).name;
|
|
537
|
+
const targetData = await targetConnector.getData({
|
|
538
|
+
resource: targetResource,
|
|
539
|
+
limit: limit,
|
|
540
|
+
offset: 0,
|
|
541
|
+
filters: [
|
|
542
|
+
{
|
|
543
|
+
field: targetResourcePkField,
|
|
544
|
+
operator: AdminForthFilterOperators.IN,
|
|
545
|
+
value: data.data.map((item) => item[col.name]),
|
|
546
|
+
}
|
|
547
|
+
],
|
|
548
|
+
sort: [],
|
|
549
|
+
});
|
|
550
|
+
const targetDataMap = targetData.data.reduce((acc, item) => {
|
|
551
|
+
acc[item[targetResourcePkField]] = {
|
|
552
|
+
label: targetResource.itemLabel ? targetResource.itemLabel(item) : item[targetResourcePkField],
|
|
553
|
+
pk: item[targetResourcePkField],
|
|
554
|
+
}
|
|
555
|
+
return acc;
|
|
556
|
+
}, {});
|
|
557
|
+
data.data.forEach((item) => {
|
|
558
|
+
item[col.name] = targetDataMap[item[col.name]];
|
|
559
|
+
});
|
|
560
|
+
})
|
|
561
|
+
);
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
if (resource.hooks?.[source]?.afterDatasourceRequest) {
|
|
565
|
+
const resp = await resource.hooks?.[source]?.afterDatasourceRequest({ resource, response: data.data, adminUser });
|
|
566
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
567
|
+
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (resp.error) {
|
|
571
|
+
return { error: resp.error };
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
615
575
|
return {...data, options: resource?.options };
|
|
616
576
|
},
|
|
617
577
|
});
|
|
@@ -666,8 +626,8 @@ class AdminForth {
|
|
|
666
626
|
const response = {
|
|
667
627
|
items
|
|
668
628
|
};
|
|
669
|
-
if (columnConfig.foreignResource.hooks?.
|
|
670
|
-
const resp = await column.foreignResource.hooks?.
|
|
629
|
+
if (columnConfig.foreignResource.hooks?.afterDatasourceResponse) {
|
|
630
|
+
const resp = await column.foreignResource.hooks?.afterDatasourceResponse({ response, adminUser });
|
|
671
631
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
672
632
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
673
633
|
}
|
|
@@ -676,6 +636,7 @@ class AdminForth {
|
|
|
676
636
|
return { error: resp.error };
|
|
677
637
|
}
|
|
678
638
|
}
|
|
639
|
+
|
|
679
640
|
return response;
|
|
680
641
|
},
|
|
681
642
|
});
|
|
@@ -709,36 +670,7 @@ class AdminForth {
|
|
|
709
670
|
return item;
|
|
710
671
|
},
|
|
711
672
|
});
|
|
712
|
-
server.endpoint({
|
|
713
|
-
method: 'POST',
|
|
714
|
-
path: '/get_record',
|
|
715
|
-
handler: async ({ body, adminUser }) => {
|
|
716
|
-
const { resourceId, primaryKey } = body;
|
|
717
|
-
const resource = this.config.resources.find((res) => res.resourceId == resourceId);
|
|
718
|
-
const primaryKeyColumn = resource.columns.find((col) => col.primaryKey);
|
|
719
|
-
const connector = this.connectors[resource.dataSource];
|
|
720
|
-
const record = await connector.getRecordByPrimaryKey(resource, primaryKey);
|
|
721
|
-
if (!record) {
|
|
722
|
-
return { error: `Record with ${primaryKeyColumn.name} ${primaryKey} not found` };
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
// execute hook if needed
|
|
726
|
-
if (resource.hooks?.show) {
|
|
727
|
-
const resp = await resource.hooks?.show({ resource, record, adminUser });
|
|
728
|
-
if (!resp || (!resp.ok && !resp.error)) {
|
|
729
|
-
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
if (resp.error) {
|
|
733
|
-
return { error: resp.error };
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
673
|
|
|
737
|
-
const labler = resource.itemLabel || ((record) => `${resource.label} ${record[primaryKeyColumn.name]}`);
|
|
738
|
-
record._label = labler(record);
|
|
739
|
-
return record;
|
|
740
|
-
}
|
|
741
|
-
});
|
|
742
674
|
server.endpoint({
|
|
743
675
|
noAuth: true, // TODO
|
|
744
676
|
method: 'POST',
|
|
@@ -771,7 +703,7 @@ class AdminForth {
|
|
|
771
703
|
});
|
|
772
704
|
}
|
|
773
705
|
}
|
|
774
|
-
if (column.required?.create && body['record'][column.name] === undefined) {
|
|
706
|
+
if ((column.required as {create?: boolean, edit?: boolean}) ?.create && body['record'][column.name] === undefined) {
|
|
775
707
|
return { error: `Column '${column.name}' is required` };
|
|
776
708
|
}
|
|
777
709
|
|
package/modules/utils.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import fs from 'fs';
|
|
1
4
|
|
|
2
5
|
|
|
3
6
|
export function guessLabelFromName(name) {
|
|
@@ -10,3 +13,14 @@ export function guessLabelFromName(name) {
|
|
|
10
13
|
return name.split(/(?=[A-Z])/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(' ');
|
|
11
14
|
}
|
|
12
15
|
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
let package_json;
|
|
19
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
const __dirname = path.join(path.dirname(__filename), '..');
|
|
21
|
+
|
|
22
|
+
export const ADMIN_FORTH_ABSOLUTE_PATH = __dirname;
|
|
23
|
+
|
|
24
|
+
package_json = JSON.parse(fs.readFileSync(path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'package.json'), 'utf8'));
|
|
25
|
+
|
|
26
|
+
export const ADMINFORTH_VERSION = package_json.version;
|
package/package.json
CHANGED
package/spa/src/App.vue
CHANGED
|
@@ -42,9 +42,6 @@
|
|
|
42
42
|
</p>
|
|
43
43
|
</div>
|
|
44
44
|
<ul class="py-1" role="none">
|
|
45
|
-
<li>
|
|
46
|
-
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white" role="menuitem">Dashboard</a>
|
|
47
|
-
</li>
|
|
48
45
|
<li >
|
|
49
46
|
<span @click="toggleTheme" class=" cursor-pointer flex items-center gap-1 block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white" role="menuitem">
|
|
50
47
|
{{ theme === 'dark' ? 'Light' : 'Dark' }}
|
|
@@ -141,10 +138,6 @@ const routerIsReady = ref(false);
|
|
|
141
138
|
|
|
142
139
|
const loggedIn = computed(() => route.name !== 'login' && routerIsReady.value);
|
|
143
140
|
|
|
144
|
-
watch(loggedIn, (value) => {
|
|
145
|
-
console.log('🔻🔻🔻 loggedIn', value);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
141
|
const theme = ref('light');
|
|
149
142
|
|
|
150
143
|
function toggleTheme() {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<label for="start-time" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">{{ label }}</label>
|
|
6
6
|
|
|
7
7
|
<div class="relative">
|
|
8
|
-
<div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5">
|
|
8
|
+
<div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
|
|
9
9
|
<IconCalendar class="w-4 h-4 text-gray-500 dark:text-gray-400"/>
|
|
10
10
|
</div>
|
|
11
11
|
|
|
@@ -97,7 +97,7 @@ const start = computed(() => {
|
|
|
97
97
|
})
|
|
98
98
|
|
|
99
99
|
function updateFromProps() {
|
|
100
|
-
if (props.valueStart
|
|
100
|
+
if (!props.valueStart) {
|
|
101
101
|
datepickerStartEl.value.value = '';
|
|
102
102
|
startTime.value = '';
|
|
103
103
|
}
|
|
@@ -157,5 +157,5 @@ onMounted(() => {
|
|
|
157
157
|
|
|
158
158
|
onBeforeUnmount(() => {
|
|
159
159
|
removeChangeDateListener();
|
|
160
|
-
})
|
|
160
|
+
});
|
|
161
161
|
</script>
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
<div class="mx-auto grid grid-cols-2 gap-4 mb-2" :class="{hidden: !showTimeInputs}">
|
|
27
27
|
<div>
|
|
28
28
|
<div class="relative">
|
|
29
|
-
<div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5">
|
|
29
|
+
<div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
|
|
30
30
|
<IconTime class="w-4 h-4 text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-700"/>
|
|
31
31
|
</div>
|
|
32
32
|
|
|
@@ -132,11 +132,11 @@ const end = computed(() => {
|
|
|
132
132
|
})
|
|
133
133
|
|
|
134
134
|
function updateFromProps() {
|
|
135
|
-
if (props.valueStart
|
|
135
|
+
if (!props.valueStart) {
|
|
136
136
|
datepickerStartEl.value.value = '';
|
|
137
137
|
startTime.value = '';
|
|
138
138
|
}
|
|
139
|
-
if (props.valueEnd
|
|
139
|
+
if (!props.valueEnd) {
|
|
140
140
|
datepickerEndEl.value.value = '';
|
|
141
141
|
endTime.value = '';
|
|
142
142
|
}
|
|
@@ -71,33 +71,52 @@ const sliderValue = ref([start.value, end.value]);
|
|
|
71
71
|
|
|
72
72
|
const updateFromSlider =
|
|
73
73
|
debounce((value: [number, number]) => {
|
|
74
|
+
console.log('start end', value)
|
|
74
75
|
start.value = value[0];
|
|
75
76
|
end.value = value[1];
|
|
76
77
|
}, 500);
|
|
77
78
|
|
|
78
|
-
function updateFromProps() {
|
|
79
|
-
if (props.valueStart || props.valueEnd) {
|
|
80
|
-
setFromProps(props.valueStart, props.valueEnd)
|
|
81
|
-
} else {
|
|
82
|
-
clear();
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
79
|
onMounted(() => {
|
|
87
|
-
|
|
80
|
+
updateStartFromProps();
|
|
81
|
+
updateEndFromProps();
|
|
88
82
|
|
|
89
|
-
watch(() =>
|
|
90
|
-
|
|
83
|
+
watch(() => props.valueStart, (value) => {
|
|
84
|
+
updateStartFromProps();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
watch(() => props.valueEnd, (value) => {
|
|
88
|
+
updateEndFromProps();
|
|
91
89
|
});
|
|
92
90
|
})
|
|
93
91
|
|
|
92
|
+
function updateStartFromProps() {
|
|
93
|
+
if (props.valueStart || props.valueStart === 0) {
|
|
94
|
+
start.value = props.valueStart ? props.valueStart : minFormatted.value;
|
|
95
|
+
sliderValue.value = [start.value, end.value]
|
|
96
|
+
} else {
|
|
97
|
+
console.log(props.valueStart)
|
|
98
|
+
start.value = minFormatted.value;
|
|
99
|
+
sliderValue.value = [minFormatted.value, end.value];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function updateEndFromProps() {
|
|
104
|
+
if (props.valueEnd || props.valueStart === 0) {
|
|
105
|
+
end.value = props.valueEnd ? props.valueEnd : minFormatted.value;
|
|
106
|
+
sliderValue.value = [start.value, end.value]
|
|
107
|
+
} else {
|
|
108
|
+
end.value = maxFormatted.value;
|
|
109
|
+
sliderValue.value = [start.value, maxFormatted.value];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
94
113
|
watch(start, () => {
|
|
95
|
-
|
|
114
|
+
console.log('⚡ emit', start.value)
|
|
96
115
|
emit('update:valueStart', start.value)
|
|
97
116
|
})
|
|
98
117
|
|
|
99
118
|
watch(end, () => {
|
|
100
|
-
|
|
119
|
+
console.log('⚡ emit', end.value)
|
|
101
120
|
emit('update:valueEnd', end.value);
|
|
102
121
|
})
|
|
103
122
|
|
|
@@ -106,12 +125,6 @@ const clear = () => {
|
|
|
106
125
|
end.value = maxFormatted.value;
|
|
107
126
|
sliderValue.value = [start.value, end.value]
|
|
108
127
|
}
|
|
109
|
-
|
|
110
|
-
function setFromProps(startValue: number, endValue: number) {
|
|
111
|
-
start.value = startValue ? startValue : minFormatted.value
|
|
112
|
-
end.value = endValue ? endValue : maxFormatted.value
|
|
113
|
-
sliderValue.value = [start.value, end.value]
|
|
114
|
-
}
|
|
115
128
|
</script>
|
|
116
129
|
|
|
117
130
|
<style lang="scss" scoped>
|
|
@@ -76,10 +76,14 @@ const showDropdown = ref(false);
|
|
|
76
76
|
const selectedItems = ref([]);
|
|
77
77
|
|
|
78
78
|
function updateFromProps() {
|
|
79
|
-
console.log('⚡ updateFromProps', props.modelValue)
|
|
80
79
|
if (props.modelValue !== undefined) {
|
|
81
80
|
if (props.single) {
|
|
82
|
-
|
|
81
|
+
const el = props.options.find(item => item.value === props.modelValue);
|
|
82
|
+
if (el) {
|
|
83
|
+
selectedItems.value = [el];
|
|
84
|
+
} else {
|
|
85
|
+
selectedItems.value = [];
|
|
86
|
+
}
|
|
83
87
|
} else {
|
|
84
88
|
selectedItems.value = props.options.filter(item => props.modelValue.includes(item.value));
|
|
85
89
|
}
|