@hostlink/nuxt-light 1.22.2 → 1.23.0

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 (30) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +15 -0
  3. package/dist/runtime/components/L/CustomField/Add.vue +94 -0
  4. package/dist/runtime/components/L/CustomField/List.vue +35 -0
  5. package/dist/runtime/components/L/System/Setting/developer.vue +20 -3
  6. package/dist/runtime/components/L/ValidationInput.vue +105 -0
  7. package/dist/runtime/components/l-date-picker.vue +4 -0
  8. package/dist/runtime/components/l-file-manager-labels.vue +6 -1
  9. package/dist/runtime/components/l-file-manager.vue +0 -4
  10. package/dist/runtime/components/l-group-select.vue +115 -0
  11. package/dist/runtime/components/l-input.vue +1 -1
  12. package/dist/runtime/components/l-table.vue +1 -1
  13. package/dist/runtime/components/l-time-picker.vue +4 -0
  14. package/dist/runtime/formkit/DatePicker.vue +4 -0
  15. package/dist/runtime/formkit/GroupSelect.vue +34 -0
  16. package/dist/runtime/formkit/Input.vue +4 -0
  17. package/dist/runtime/formkit/Repeater.vue +35 -38
  18. package/dist/runtime/formkit/TimePicker.vue +4 -0
  19. package/dist/runtime/formkit/index.js +6 -0
  20. package/dist/runtime/index.d.ts +1 -1
  21. package/dist/runtime/light.d.ts +6 -1123
  22. package/dist/runtime/model/CustomField.d.ts +26 -0
  23. package/dist/runtime/model/CustomField.js +25 -0
  24. package/dist/runtime/pages/CustomField/[custom_field_id]/edit.vue +64 -0
  25. package/dist/runtime/pages/CustomField/index.vue +43 -0
  26. package/dist/runtime/pages/Role/add2.vue +68 -0
  27. package/dist/runtime/pages/System/database/table.vue +2 -2
  28. package/dist/runtime/pages/System/setting.vue +3 -0
  29. package/dist/runtime/plugin.js +2 -0
  30. package/package.json +2 -2
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "light",
3
3
  "configKey": "light",
4
- "version": "1.22.2",
4
+ "version": "1.23.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
package/dist/module.mjs CHANGED
@@ -5,6 +5,16 @@ import { quasar } from '@quasar/vite-plugin';
5
5
 
6
6
  const resolver = createResolver(import.meta.url);
7
7
  const routes = [
8
+ {
9
+ name: "CustomField",
10
+ path: "/CustomField",
11
+ file: resolver.resolve("./runtime/pages/CustomField/index.vue")
12
+ },
13
+ {
14
+ name: "CustomField-custom_field_id-edit",
15
+ path: "/CustomField/:custom_field_id/edit",
16
+ file: resolver.resolve("./runtime/pages/CustomField/[custom_field_id]/edit.vue")
17
+ },
8
18
  {
9
19
  name: "EventLog",
10
20
  path: "/EventLog",
@@ -75,6 +85,11 @@ const routes = [
75
85
  path: "/Role/add",
76
86
  file: resolver.resolve("./runtime/pages/Role/add.vue")
77
87
  },
88
+ {
89
+ name: "Role-add2",
90
+ path: "/Role/add2",
91
+ file: resolver.resolve("./runtime/pages/Role/add2.vue")
92
+ },
78
93
  {
79
94
  name: "System-fs",
80
95
  path: "/System/fs",
@@ -0,0 +1,94 @@
1
+ <script setup>
2
+ import { useDialogPluginComponent, useQuasar } from 'quasar'
3
+ import { m, q } from "#imports"
4
+ const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
5
+
6
+
7
+ defineProps({
8
+ models: {
9
+ type: Array,
10
+ required: true
11
+ }
12
+ })
13
+
14
+ const $q = useQuasar()
15
+
16
+ defineEmits([
17
+ // REQUIRED; need to specify some events that your
18
+ // component will emit through useDialogPluginComponent()
19
+ ...useDialogPluginComponent.emits
20
+ ])
21
+
22
+ const types = [
23
+ { label: 'Text', value: 'text' },
24
+ { label: 'Textarea', value: 'textarea' },
25
+ { label: 'Select', value: 'select' },
26
+ { label: 'Date', value: 'date' },
27
+ { label: 'Time', value: 'time' }
28
+ ]
29
+
30
+ const onOKClick = async (value) => {
31
+ try {
32
+ await m("addCustomField", {
33
+ data: value
34
+ })
35
+ //show success message
36
+ $q.notify({
37
+ icon: 'sym_o_check',
38
+ message: 'Custom Field added successfully',
39
+ color: 'positive',
40
+ })
41
+ onDialogOK()
42
+
43
+ } catch (e) {
44
+
45
+ }
46
+
47
+
48
+ }
49
+
50
+ </script>
51
+ <template>
52
+ <q-dialog ref="dialogRef" full-width>
53
+ <q-card class="q-dialog-plugin">
54
+ <q-toolbar>
55
+ <q-toolbar-title>Add Custom Field</q-toolbar-title>
56
+ <q-space />
57
+ <q-btn dense flat icon="sym_o_close" v-close-popup />
58
+ </q-toolbar>
59
+
60
+ <form-kit type="l-form"
61
+ :value="{ type: 'text', order: 1, options: [], default_value: '', placeholder: '', validation: '' }"
62
+ #default="{ value, node }" @submit="onOKClick">
63
+ <form-kit type="l-input" name="name" label="Name" validation="required" />
64
+
65
+ <form-kit type="l-select" name="model" label="Model" :options="models" validation="required" />
66
+
67
+ <form-kit type="l-select" name="type" label="Type" :options="types" validation="required" />
68
+
69
+ <form-kit type="hidden" name="validation" />
70
+ <l-validation-input v-model="value.validation" @update:model-value="node.at('validation').input($event)"
71
+ :type="value.type" />
72
+
73
+
74
+ <form-kit type="l-input" name="label" label="Label" validation="required" />
75
+
76
+ <form-kit type="hidden" name="options" />
77
+ <template v-if="value.type === 'select'">
78
+ <form-kit type="l-select" label="Options" name="options" use-input use-chips multiple
79
+ hide-dropdown-icon input-debounce="0" new-value-mode="add-unique" stack-label outlined
80
+ placeholder="Press enter to add new option" />
81
+ </template>
82
+
83
+ <form-kit type="l-input" name="default_value" label="Default Value" />
84
+
85
+ <form-kit type="l-input" name="placeholder" label="Placeholder" />
86
+
87
+ <form-kit type="l-input" input-type="number" name="order" label="Order" number />
88
+
89
+
90
+ </form-kit>
91
+
92
+ </q-card>
93
+ </q-dialog>
94
+ </template>
@@ -0,0 +1,35 @@
1
+ <script setup lang="ts">
2
+ import { q } from '#imports'
3
+
4
+ import type { QListProps } from 'quasar';
5
+
6
+ export interface LCustomFieldListProps extends QListProps {
7
+ model: String,
8
+ data: { [key: string]: any }
9
+ }
10
+
11
+ const props = withDefaults(defineProps<LCustomFieldListProps>(), {
12
+ bordered: true,
13
+ separator: true,
14
+ dense: true,
15
+ });
16
+
17
+
18
+ const { app } = await q({
19
+ app: {
20
+ customFieldSchema: {
21
+ __args: {
22
+ model: props.model
23
+ }
24
+ }
25
+ }
26
+ })
27
+ const { customFieldSchema } = app
28
+ </script>
29
+ <template>
30
+ <q-list v-bind="$props">
31
+ <l-item v-for="field in customFieldSchema" :label="field.label">
32
+ {{ data[field.name] }}
33
+ </l-item>
34
+ </q-list>
35
+ </template>
@@ -6,7 +6,8 @@ const $q = useQuasar()
6
6
 
7
7
 
8
8
  export interface LSystemSettingDeveloperProps {
9
- mode: string
9
+ mode: string,
10
+ custom_field_models: string,
10
11
  }
11
12
 
12
13
  const modelValue = defineModel<LSystemSettingDeveloperProps>({
@@ -19,6 +20,14 @@ if (modelValue.value.mode != 'prod') {
19
20
  modelValue.value.mode = 'prod'
20
21
  }
21
22
 
23
+ //split the custom_field_models by comma
24
+ if (modelValue.value.custom_field_models != "") {
25
+ modelValue.value.custom_field_models = modelValue.value.custom_field_models.split(',')
26
+ } else {
27
+ modelValue.value.custom_field_models = []
28
+ }
29
+
30
+
22
31
  const onSubmit = async (d: LSystemSettingDeveloperProps) => {
23
32
  let data: { name: string, value: string }[] = [];
24
33
  Object.keys(d).forEach((key) => {
@@ -39,13 +48,21 @@ const onSubmit = async (d: LSystemSettingDeveloperProps) => {
39
48
  </script>
40
49
  <template>
41
50
  <FormKit type="l-form" :bordered="false" :value="{
42
- mode: modelValue.mode
43
- }" @submit="onSubmit">
51
+ mode: modelValue.mode,
52
+ custom_field_models: modelValue.custom_field_models
53
+ }" @submit="onSubmit" #default="{ value }">
54
+
55
+ <form-kit type="l-select" name="custom_field_models" label="Custom Field Model" use-input use-chips multiple
56
+ hide-dropdown-icon input-debounce="0" new-value-mode="add-unique" />
57
+
44
58
  <FormKit label="Mode" type="l-select" :options="[
45
59
  { label: 'Production', value: 'prod' },
46
60
  { label: 'Development', value: 'dev' },
47
61
 
48
62
  ]" name="mode" validation="required">
63
+
49
64
  </FormKit>
65
+
66
+
50
67
  </FormKit>
51
68
  </template>
@@ -0,0 +1,105 @@
1
+ <script setup>
2
+ import { computed, watch, reactive } from 'vue';
3
+ const model = defineModel({
4
+ type: String,
5
+ default: ""
6
+ });
7
+
8
+ const props = defineProps({
9
+ type: {
10
+ type: String,
11
+ default: "text"
12
+ }
13
+ })
14
+
15
+ const isRequired = computed(() => {
16
+ return model.value.split("|").includes("required");
17
+ });
18
+
19
+ const isEmail = computed(() => {
20
+ return model.value.split("|").includes("email");
21
+ });
22
+
23
+ const isNumber = computed(() => {
24
+ return model.value.split("|").includes("number");
25
+ });
26
+
27
+ const lenghtMin = computed(() => {
28
+ const length = model.value.split("|").find((v) => v.includes("length"));
29
+ if (length) {
30
+ return length.split(":")[1].split(",")[0];
31
+ }
32
+ return 0;
33
+ });
34
+
35
+ const lenghtMax = computed(() => {
36
+ const length = model.value.split("|").find((v) => v.includes("length"));
37
+ if (length) {
38
+ return length.split(":")[1].split(",")[1];
39
+ }
40
+ return 0;
41
+ });
42
+
43
+
44
+ const form = reactive({
45
+ required: isRequired.value,
46
+ email: isEmail.value,
47
+ number: isNumber.value,
48
+ length_min: lenghtMin.value,
49
+ length_max: lenghtMax.value
50
+ });
51
+
52
+ watch(form, () => {
53
+
54
+ //repack the validation string
55
+ let validation = [];
56
+ if (form.required) {
57
+ validation.push("required");
58
+ }
59
+
60
+
61
+ if (props.type == "text") {
62
+ if (form.email) {
63
+ validation.push("email");
64
+ }
65
+
66
+ if (form.number) {
67
+ validation.push("number");
68
+ }
69
+
70
+ if (form.length_min > 0 || form.length_max > 0) {
71
+
72
+ if (form.length_max > 0) {
73
+ validation.push(`length:${form.length_min},${form.length_max}`);
74
+ } else {
75
+ validation.push(`length:${form.length_min}`);
76
+ }
77
+ }
78
+ }
79
+
80
+ model.value = validation.join("|");
81
+
82
+ });
83
+
84
+ </script>
85
+
86
+ <template>
87
+
88
+
89
+ <q-checkbox left-label v-model="form.required" label="Required" :color="$light.color" />
90
+
91
+ <q-checkbox left-label v-model="form.email" label="Email" :color="$light.color" v-if="type == 'text'" />
92
+
93
+ <q-checkbox left-label v-model="form.number" label="Number" :color="$light.color" v-if="type == 'text'" />
94
+
95
+ <l-row v-if="type = 'text'">
96
+ <l-col :md="6">
97
+ <q-input label="Length min" v-model="form.length_min" type="number" stack-label outlined dense :min="0" />
98
+ </l-col>
99
+ <l-col :md="6">
100
+ <q-input label="Length max" v-model="form.length_max" type="number" stack-label outlined dense :min="0" />
101
+ </l-col>
102
+ </l-row>
103
+
104
+
105
+ </template>
@@ -156,5 +156,9 @@ const date_attrs = computed(() => {
156
156
  </q-popup-proxy>
157
157
  </q-btn>
158
158
  </template>
159
+
160
+ <template v-for="(s, name) in $slots" v-slot:[name]="props" :key="name">
161
+ <slot :name="name" v-bind="props ?? {}"></slot>
162
+ </template>
159
163
  </q-input>
160
164
  </template>
@@ -53,4 +53,9 @@ const LABLES = [
53
53
  </q-item-section>
54
54
  <q-item-section> {{ $t(label.label) }} </q-item-section>
55
55
  </q-item>
56
- </template>
56
+ </template>
57
+
58
+
59
+ <style scoped>
60
+ .q-item--active{background-color:#e0e0e0}
61
+ </style>
@@ -880,7 +880,3 @@ selectedNodePath.value = drives[0].index.toString();
880
880
  </q-page-container>
881
881
  </q-layout>
882
882
  </template>
883
-
884
- <style>
885
- .q-item--active{background-color:#e0e0e0}
886
- </style>
@@ -0,0 +1,115 @@
1
+ <script setup>
2
+ import collect from "collect.js";
3
+
4
+ import { computed, onMounted, useAttrs, ref } from "vue";
5
+
6
+ const props = defineProps({
7
+ options: {
8
+ type: Array,
9
+ required: true,
10
+ },
11
+ emitValue: {
12
+ type: Boolean,
13
+ default: true,
14
+ },
15
+ mapOptions: {
16
+ type: Boolean,
17
+ default: true,
18
+ },
19
+ });
20
+
21
+ const userString = ref("");
22
+ const items = computed(() => {
23
+
24
+ //filter out items based on user input
25
+
26
+ const filteredItems = collect(props.options)
27
+ .filter((item) => {
28
+ if (userString.value === "") {
29
+ return true;
30
+ }
31
+ return item.label.toLowerCase().includes(userString.value);
32
+ })
33
+ .toArray();
34
+
35
+ const groups = collect(filteredItems).unique("group").pluck("group").sort();
36
+
37
+ const options = collect();
38
+ groups.each((group) => {
39
+ const items = collect(filteredItems).where("group", group).toArray();
40
+ options
41
+ .push({ label: group, is_group: true, disable: true })
42
+ .push(...items);
43
+ });
44
+
45
+ return options.toArray();
46
+ });
47
+
48
+ const model = defineModel();
49
+
50
+ const attrs = useAttrs();
51
+ const handleItemUniqueByGroup = (items) => {
52
+ return;
53
+ if (!attrs.hasOwnProperty("multiple") || attrs.multiple === false) {
54
+ return;
55
+ }
56
+ /*
57
+ model.value = collect(items)
58
+ .unique((item) => {
59
+ // Allow selecting multiple items that are not grouped
60
+ if (item.group === null) {
61
+ return item.value;
62
+ }
63
+
64
+ return item.group;
65
+ })
66
+ .toArray(); */
67
+ };
68
+
69
+ onMounted(() => {
70
+ //這個功能是為了避免在多選的情況下,選擇同一個group的item
71
+ // handleItemUniqueByGroup(model.value);
72
+
73
+ });
74
+
75
+ const filterFn = (val, update, abort) => {
76
+ if (val === "") {
77
+ update(() => {
78
+ userString.value = "";
79
+ });
80
+ return;
81
+ }
82
+
83
+ const needle = val.toLowerCase();
84
+ update(() => {
85
+ userString.value = needle;
86
+ });
87
+ };
88
+
89
+ </script>
90
+
91
+ <template>
92
+
93
+
94
+ <q-select v-model="model" v-bind="$light.getInputProps($props)" :options="items" @update:model-value="handleItemUniqueByGroup"
95
+ input-debounce="0" @filter="filterFn" :color="$light.color" :style="$light.styles.input"
96
+ :emit-value="emitValue" :map-options="mapOptions">
97
+ <template v-slot:option="scope">
98
+ <q-item v-if="scope.opt.is_group" v-bind="{ ...scope.itemProps }" class="text-xs bg-grey-4" :key="scope.opt.label">
99
+ <q-item-section class="!cursor-default">
100
+ {{ scope.opt.label || "Ungroup" }}
101
+ </q-item-section>
102
+ </q-item>
103
+ <q-item v-else v-bind="{ ...scope.itemProps }" :key="scope.opt.value">
104
+ <q-item-section class="q-pl-md">{{ scope.opt.label }}</q-item-section>
105
+ </q-item>
106
+ </template>
107
+ <template v-slot:no-option>
108
+ <q-item>
109
+ <q-item-section class="text-grey">
110
+ No results
111
+ </q-item-section>
112
+ </q-item>
113
+ </template>
114
+ </q-select>
115
+ </template>
@@ -145,6 +145,6 @@ const onClickTc2Sc = () => {
145
145
  <q-icon name="sym_o_visibility_off" class="cursor-pointer" :color="showPassword ? 'grey-5' : 'primary'"
146
146
  @click="isShowPassword = true" v-else />
147
147
  </template>
148
-
148
+
149
149
  </q-input>
150
150
  </template>
@@ -550,7 +550,7 @@ const onAdd = () => {
550
550
  v-model:minimized="minimized" v-model:maximized="maximized">
551
551
 
552
552
  <q-toolbar v-if="addComponent">
553
- <q-btn icon="sym_o_add" :label="$t('Add')" :color="$light.color" @click="onAdd" v-if="addComponent" />
553
+ <q-btn icon="sym_o_add" :label="$t('Add')" :color="$light.color" @click="onAdd" v-if="addComponent" outline />
554
554
  </q-toolbar>
555
555
 
556
556
 
@@ -59,5 +59,9 @@ if (props.required) {
59
59
  </q-popup-proxy>
60
60
  </q-btn>
61
61
  </template>
62
+
63
+ <template v-for="(s, name) in $slots" v-slot:[name]="props" :key="name">
64
+ <slot :name="name" v-bind="props ?? {}"></slot>
65
+ </template>
62
66
  </q-input>
63
67
  </template>
@@ -33,5 +33,9 @@ const value = computed({
33
33
  <template v-for="(s, name) in $slots" v-slot:[name]="props" :key="name">
34
34
  <slot :name="name" v-bind="props ?? {}"></slot>
35
35
  </template>
36
+
37
+ <template v-if="context.help" #hint>
38
+ {{ context.help }}
39
+ </template>
36
40
  </l-date-picker>
37
41
  </template>
@@ -0,0 +1,34 @@
1
+ <script setup>
2
+ import { getErrorMessage } from 'formkit-quasar';
3
+ import { computed } from 'vue'
4
+ const props = defineProps({
5
+ context: Object
6
+ });
7
+
8
+ const { error, errorMessage } = getErrorMessage(props.context.node);
9
+
10
+
11
+
12
+ const value = computed({
13
+ get: () => props.context.value,
14
+ set: (val) => props.context.node.input(val)
15
+ })
16
+
17
+
18
+ let clearable = false;
19
+ if (props.context.state.required) { //no clearable
20
+ clearable = false;
21
+ } else {
22
+ clearable = true;
23
+ }
24
+
25
+ </script>
26
+ <template>
27
+ <l-group-select v-model="value" :label="context.label" v-bind="context.attrs" :error="error"
28
+ :error-message="errorMessage" :required="context.state.required"
29
+ :disable="context.disabled">
30
+ <template v-for="(s, name) in $slots" v-slot:[name]="props" :key="name">
31
+ <slot :name="name" v-bind="props ?? {}"></slot>
32
+ </template>
33
+ </l-group-select>
34
+ </template>
@@ -64,5 +64,9 @@ const label = computed(() => {
64
64
  <template v-for="(s, name) in $slots" v-slot:[name]="props" :key="name">
65
65
  <slot :name="name" v-bind="props ?? {}"></slot>
66
66
  </template>
67
+
68
+ <template v-if="context.help" #hint>
69
+ {{ context.help }}
70
+ </template>
67
71
  </l-input>
68
72
  </template>
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import { computed } from 'vue'
3
- import { useDragAndDrop } from "@formkit/drag-and-drop/vue";
3
+ import { useDragAndDrop, dragAndDrop } from "@formkit/drag-and-drop/vue";
4
4
  import { animations } from "@formkit/drag-and-drop";
5
5
 
6
6
  const props = defineProps({
@@ -69,49 +69,46 @@ const isAllowMoveDown = (index) => {
69
69
 
70
70
  </script>
71
71
 
72
+ <style>
73
+ .cursor-grab{cursor:grab}.cursor-grabbing{cursor:grabbing}
74
+ </style>
75
+
72
76
  <template>
73
77
  <FormKit type="list" v-model="localValue" dynamic #default="{ items, node, value }" :name="node.name">
78
+ {{ sortable }}
74
79
 
75
- <div ref="parent">
80
+ <q-list bordered separator ref="parent">
76
81
  <FormKit type="group" v-for="(item, index) in items" :index="index" :key="item">
77
- <q-card class="q-mb-sm" flat bordered>
78
- <q-card-section>
79
- <div class="row q-col-gutter-md">
80
- <div class="col-shrink">
81
- <div class="q-mb-xs" v-if="sortable">
82
- <q-btn icon="sym_o_drag_handle" dense unelevated :color="$light.color"
83
- style="cursor: grab;" class="l-repeater-handle">
84
- </q-btn>
85
- </div>
86
- <div class="q-mb-xs" v-if="sortable">
87
- <!-- up -->
88
- <q-btn type="button" @click="onMoveUp(index)" icon="sym_o_arrow_upward"
89
- :color="$light.color" dense unelevated :disable="!isAllowMoveUp(index)" />
90
- </div>
91
-
92
- <div class="q-mb-xs">
93
- <q-btn type="button" @click="onRemove" icon="sym_o_remove" :color="$light.color"
94
- dense unelevated :disable="!isAllowRemove" />
95
- </div>
96
- <div class="q-mb-xs" v-if="sortable">
97
- <!-- down -->
98
- <q-btn type="button" @click="onMoveDown(index)" icon="sym_o_arrow_downward"
99
- :color="$light.color" dense unelevated :disable="!isAllowMoveDown(index)" />
100
- </div>
101
- </div>
102
-
103
- <div class="col">
104
- <slot v-bind="{ value: localValue[index] }"></slot>
105
- </div>
82
+ <q-item class="q-pa-xs">
83
+ <q-item-section avatar class="">
84
+ <div class="l-repeater-handle cursor-grab active:cursor-grabbing" v-if="sortable">
85
+ <q-icon name="sym_o_drag_indicator" :color="$light.color" size="sm" />
106
86
  </div>
107
- </q-card-section>
108
- </q-card>
87
+ </q-item-section>
88
+
89
+ <q-item-section >
90
+ <slot v-bind="{ value: localValue[index] }"></slot>
91
+ </q-item-section>
92
+
93
+ <q-item-section side>
94
+ <!-- up -->
95
+ <q-btn type="button" @click="onMoveUp(index)" icon="sym_o_arrow_upward" :color="$light.color"
96
+ dense flat :disable="!isAllowMoveUp(index)" />
97
+
98
+ <q-btn type="button" @click="onRemove" icon="sym_o_delete" :color="$light.color" dense
99
+ :disable="!isAllowRemove" flat />
100
+ <!-- down -->
101
+ <q-btn type="button" @click="onMoveDown(index)" icon="sym_o_arrow_downward"
102
+ :color="$light.color" dense flat :disable="!isAllowMoveDown(index)" />
103
+
104
+ </q-item-section>
105
+
106
+
107
+ </q-item>
108
+
109
109
  </FormKit>
110
- </div>
111
110
 
112
- <q-toolbar>
113
- <q-btn :color="$light.color" @click="onAdd" icon="sym_o_add" :label="$t('Add')"
114
- :disable="localValue.length >= max" unelevated></q-btn>
115
- </q-toolbar>
111
+ </q-list>
112
+ <q-btn @click="onAdd" label="Add" icon="sym_o_add" :color="$light.color" outline />
116
113
  </FormKit>
117
114
  </template>
@@ -29,5 +29,9 @@ const label = computed(() => {
29
29
  <template v-for="(s, name) in $slots" v-slot:[name]="props" :key="name">
30
30
  <slot :name="name" v-bind="props ?? {}"></slot>
31
31
  </template>
32
+
33
+ <template v-if="context.help" #hint>
34
+ {{ context.help }}
35
+ </template>
32
36
  </l-time-picker>
33
37
  </template>