@bagelink/vue 0.0.466 → 0.0.471

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 (179) hide show
  1. package/dist/components/Accordion.vue.d.ts.map +1 -1
  2. package/dist/components/AccordionItem.vue.d.ts.map +1 -1
  3. package/dist/components/Alert.vue.d.ts +2 -2
  4. package/dist/components/Alert.vue.d.ts.map +1 -1
  5. package/dist/components/Avatar.vue.d.ts.map +1 -1
  6. package/dist/components/Badge.vue.d.ts +2 -2
  7. package/dist/components/Badge.vue.d.ts.map +1 -1
  8. package/dist/components/BglVideo.vue.d.ts +2 -2
  9. package/dist/components/BglVideo.vue.d.ts.map +1 -1
  10. package/dist/components/Btn.vue.d.ts.map +1 -1
  11. package/dist/components/Card.vue.d.ts.map +1 -1
  12. package/dist/components/Carousel.vue.d.ts +4 -2
  13. package/dist/components/Carousel.vue.d.ts.map +1 -1
  14. package/dist/components/DataPreview.vue.d.ts.map +1 -1
  15. package/dist/components/Drop.vue.d.ts +34 -0
  16. package/dist/components/Drop.vue.d.ts.map +1 -0
  17. package/dist/components/FileUploader.vue.d.ts +60 -0
  18. package/dist/components/FileUploader.vue.d.ts.map +1 -0
  19. package/dist/components/Flag.vue.d.ts +2 -2
  20. package/dist/components/Flag.vue.d.ts.map +1 -1
  21. package/dist/components/ListItem.vue.d.ts.map +1 -1
  22. package/dist/components/ListView.vue.d.ts.map +1 -1
  23. package/dist/components/MapEmbed.vue.d.ts +2 -2
  24. package/dist/components/MapEmbed.vue.d.ts.map +1 -1
  25. package/dist/components/MaterialIcon.vue.d.ts.map +1 -1
  26. package/dist/components/Modal.vue.d.ts +2 -1
  27. package/dist/components/Modal.vue.d.ts.map +1 -1
  28. package/dist/components/ModalConfirm.vue.d.ts.map +1 -1
  29. package/dist/components/ModalForm.vue.d.ts.map +1 -1
  30. package/dist/components/NavBar.vue.d.ts.map +1 -1
  31. package/dist/components/PageTitle.vue.d.ts.map +1 -1
  32. package/dist/components/TableSchema.vue.d.ts.map +1 -1
  33. package/dist/components/Title.vue.d.ts.map +1 -1
  34. package/dist/components/dashboard/Lineart.vue.d.ts.map +1 -1
  35. package/dist/components/dashboard/index.d.ts.map +1 -1
  36. package/dist/components/form/BglField.vue.d.ts.map +1 -1
  37. package/dist/components/form/BglForm.vue.d.ts +4 -2
  38. package/dist/components/form/BglForm.vue.d.ts.map +1 -1
  39. package/dist/components/form/ItemRef.vue.d.ts +1 -0
  40. package/dist/components/form/ItemRef.vue.d.ts.map +1 -1
  41. package/dist/components/form/index.d.ts.map +1 -1
  42. package/dist/components/form/inputs/CheckInput.vue.d.ts.map +1 -1
  43. package/dist/components/form/inputs/Checkbox.vue.d.ts.map +1 -1
  44. package/dist/components/form/inputs/ColorPicker.vue.d.ts.map +1 -1
  45. package/dist/components/form/inputs/DateInput.vue.d.ts.map +1 -1
  46. package/dist/components/form/inputs/DatePicker.vue.d.ts.map +1 -1
  47. package/dist/components/form/inputs/FileUpload.vue.d.ts +2 -1
  48. package/dist/components/form/inputs/FileUpload.vue.d.ts.map +1 -1
  49. package/dist/components/form/inputs/JSONInput.vue.d.ts.map +1 -1
  50. package/dist/components/form/inputs/RadioPillsInput.vue.d.ts.map +1 -1
  51. package/dist/components/form/inputs/RichText.vue.d.ts.map +1 -1
  52. package/dist/components/form/inputs/SelectField.vue.d.ts +1 -4
  53. package/dist/components/form/inputs/SelectField.vue.d.ts.map +1 -1
  54. package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
  55. package/dist/components/form/inputs/SignaturePad.vue.d.ts +56 -0
  56. package/dist/components/form/inputs/SignaturePad.vue.d.ts.map +1 -0
  57. package/dist/components/form/inputs/TableField.vue.d.ts.map +1 -1
  58. package/dist/components/form/inputs/TelInput.vue.d.ts +2 -2
  59. package/dist/components/form/inputs/TelInput.vue.d.ts.map +1 -1
  60. package/dist/components/form/inputs/TextInput.vue.d.ts.map +1 -1
  61. package/dist/components/form/inputs/ToggleInput.vue.d.ts.map +1 -1
  62. package/dist/components/form/inputs/index.d.ts +1 -0
  63. package/dist/components/form/inputs/index.d.ts.map +1 -1
  64. package/dist/components/index.d.ts.map +1 -1
  65. package/dist/components/layout/BottomMenu.vue.d.ts.map +1 -1
  66. package/dist/components/layout/Layout.vue.d.ts +2 -2
  67. package/dist/components/layout/Layout.vue.d.ts.map +1 -1
  68. package/dist/components/layout/SidebarMenu.vue.d.ts.map +1 -1
  69. package/dist/components/layout/TabbedLayout.vue.d.ts.map +1 -1
  70. package/dist/components/layout/Tabs.vue.d.ts.map +1 -1
  71. package/dist/components/layout/TabsBody.vue.d.ts.map +1 -1
  72. package/dist/components/layout/TabsNav.vue.d.ts.map +1 -1
  73. package/dist/components/layout/index.d.ts.map +1 -1
  74. package/dist/index.cjs +17616 -20818
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.mjs +17616 -20818
  77. package/dist/plugins/bagel.d.ts +1 -1
  78. package/dist/plugins/bagel.d.ts.map +1 -1
  79. package/dist/plugins/modal.d.ts +14 -14
  80. package/dist/plugins/modal.d.ts.map +1 -1
  81. package/dist/style.css +330 -309
  82. package/dist/types/BagelForm.d.ts +16 -16
  83. package/dist/types/BagelForm.d.ts.map +1 -1
  84. package/dist/types/BtnOptions.d.ts +1 -1
  85. package/dist/types/BtnOptions.d.ts.map +1 -1
  86. package/dist/types/NavLink.d.ts +2 -2
  87. package/dist/types/NavLink.d.ts.map +1 -1
  88. package/dist/types/file.d.ts +2 -2
  89. package/dist/types/file.d.ts.map +1 -1
  90. package/dist/types/index.d.ts.map +1 -1
  91. package/dist/types/materialIcons.d.ts.map +1 -1
  92. package/dist/utils/BagelFormUtils.d.ts.map +1 -1
  93. package/dist/utils/allCountries.d.ts +2 -2
  94. package/dist/utils/allCountries.d.ts.map +1 -1
  95. package/dist/utils/clickOutside.d.ts.map +1 -1
  96. package/dist/utils/index.d.ts +5 -5
  97. package/dist/utils/index.d.ts.map +1 -1
  98. package/package.json +19 -17
  99. package/src/components/Accordion.vue +10 -10
  100. package/src/components/AccordionItem.vue +47 -47
  101. package/src/components/Alert.vue +21 -21
  102. package/src/components/Avatar.vue +15 -15
  103. package/src/components/Badge.vue +17 -16
  104. package/src/components/BglVideo.vue +43 -43
  105. package/src/components/Btn.vue +64 -65
  106. package/src/components/Card.vue +26 -26
  107. package/src/components/Carousel.vue +130 -134
  108. package/src/components/DataPreview.vue +30 -30
  109. package/src/components/Flag.vue +26 -26
  110. package/src/components/ListItem.vue +13 -13
  111. package/src/components/ListView.vue +2 -2
  112. package/src/components/MapEmbed.vue +33 -33
  113. package/src/components/MaterialIcon.vue +12 -12
  114. package/src/components/Modal.vue +73 -66
  115. package/src/components/ModalConfirm.vue +18 -18
  116. package/src/components/ModalForm.vue +79 -76
  117. package/src/components/NavBar.vue +33 -30
  118. package/src/components/PageTitle.vue +10 -8
  119. package/src/components/TableSchema.vue +123 -113
  120. package/src/components/Title.vue +8 -8
  121. package/src/components/dashboard/Lineart.vue +103 -96
  122. package/src/components/dashboard/index.ts +1 -1
  123. package/src/components/form/BglField.vue +61 -61
  124. package/src/components/form/BglFieldSet.vue +9 -8
  125. package/src/components/form/BglForm.vue +71 -70
  126. package/src/components/form/index.ts +4 -4
  127. package/src/components/form/inputs/CheckInput.vue +25 -23
  128. package/src/components/form/inputs/Checkbox.vue +10 -10
  129. package/src/components/form/inputs/ColorPicker.vue +26 -24
  130. package/src/components/form/inputs/DateInput.vue +39 -39
  131. package/src/components/form/inputs/DatePicker.vue +37 -36
  132. package/src/components/form/inputs/FileUpload.vue +165 -161
  133. package/src/components/form/inputs/FileUploadURL.vue +134 -132
  134. package/src/components/form/inputs/JSONInput.vue +29 -29
  135. package/src/components/form/inputs/RadioPillsInput.vue +49 -49
  136. package/src/components/form/inputs/RichText.vue +62 -63
  137. package/src/components/form/inputs/SelectInput.vue +173 -167
  138. package/src/components/form/inputs/SignaturePad.vue +150 -0
  139. package/src/components/form/inputs/TableField.vue +77 -76
  140. package/src/components/form/inputs/TelInput.vue +280 -285
  141. package/src/components/form/inputs/TextInput.vue +81 -76
  142. package/src/components/form/inputs/ToggleInput.vue +13 -13
  143. package/src/components/form/inputs/index.ts +17 -15
  144. package/src/components/formkit/FileUploader.vue +162 -158
  145. package/src/components/formkit/MiscFields.vue +20 -20
  146. package/src/components/formkit/Toggle.vue +13 -13
  147. package/src/components/index.ts +28 -28
  148. package/src/components/layout/BottomMenu.vue +25 -21
  149. package/src/components/layout/Layout.vue +29 -29
  150. package/src/components/layout/SidebarMenu.vue +31 -27
  151. package/src/components/layout/TabbedLayout.vue +47 -45
  152. package/src/components/layout/Tabs.vue +32 -32
  153. package/src/components/layout/TabsBody.vue +10 -10
  154. package/src/components/layout/TabsNav.vue +44 -44
  155. package/src/components/layout/index.ts +7 -7
  156. package/src/components/layout/tabsManager.ts +6 -6
  157. package/src/index.ts +13 -8
  158. package/src/plugins/bagel.ts +37 -37
  159. package/src/plugins/modal.ts +66 -67
  160. package/src/styles/layout.css +5 -3
  161. package/src/styles/mobilLayout.css +7 -0
  162. package/src/types/BagelForm.ts +27 -27
  163. package/src/types/BtnOptions.ts +13 -13
  164. package/src/types/NavLink.ts +8 -8
  165. package/src/types/file.ts +11 -11
  166. package/src/types/index.ts +16 -12
  167. package/src/types/materialIcons.ts +3249 -3249
  168. package/src/utils/BagelFormUtils.ts +23 -23
  169. package/src/utils/allCountries.ts +11 -12
  170. package/src/utils/clickOutside.ts +6 -6
  171. package/src/utils/index.ts +39 -41
  172. package/src/utils/lang.ts +13 -13
  173. package/src/utils/strings.ts +9 -9
  174. package/tsconfig.json +0 -1
  175. package/vite.config.ts +8 -8
  176. package/dist/components/Popover.vue.d.ts +0 -10
  177. package/dist/components/Popover.vue.d.ts.map +0 -1
  178. package/dist/types/materialIcon.d.ts +0 -2
  179. package/dist/types/materialIcon.d.ts.map +0 -1
@@ -1,239 +1,245 @@
1
- <template>
2
- <Dropdown
3
- @click.stop
4
- @hide="updateOpen(false)"
5
- ref="dropdown"
6
- placement="bottom-start"
7
- class="bagel-input selectinput"
8
- >
9
- <label>
10
- {{ label }}
11
- <button
12
- @keydown="openOptions"
13
- @click="updateOpen(true)"
14
- :disabled="disabled"
15
- type="button"
16
- class="selectinput-btn"
17
- :class="{ isEmpty: selectedItems.length === 0 }"
18
- >
19
- <Icon v-if="icon" :icon="icon" />
20
- <p v-if="!hideLabel">
21
- {{ selectedLabel }}
22
- </p>
23
- <Icon
24
- v-if="!disabled"
25
- thin
26
- v-bind="{ icon: open ? 'unfold_less' : 'unfold_more' }"
27
- />
28
- </button>
29
- <input
30
- tabindex="-1"
31
- style="width: 0; height: 0; position: absolute; opacity: 0; z-index: -1"
32
- v-if="required"
33
- @input="updateOpen(true)"
34
- :value="selectedItems"
35
- required
36
- />
37
- </label>
38
- <template #popper>
39
- <Card
40
- class="selectinput-options p-05"
41
- :style="{ width: fullWidth ? '100%' : 'auto' }"
42
- >
43
- <TextInput
44
- v-if="searchable"
45
- ref="searchInput"
46
- dense
47
- :placeholder="'Search'"
48
- icon="search"
49
- v-model="search"
50
- />
51
- <div
52
- class="selectinput-option hover gap-1"
53
- v-for="(option, i) in filteredOptions"
54
- :key="`${option}${i}`"
55
- @click="select(option)"
56
- :class="{ selected: isSelected(option) }"
57
- >
58
- <Icon v-if="isSelected(option)" icon="check" />
59
- <Icon
60
- class="opacity-3"
61
- v-if="!isSelected(option)"
62
- icon="fiber_manual_record"
63
- />
64
- <span>
65
- {{ getLabel(option) }}
66
- </span>
67
- </div>
68
- <slot name="last" />
69
- </Card>
70
- </template>
71
- </Dropdown>
72
- </template>
73
-
74
1
  <script lang="ts" setup>
75
- import { watch } from 'vue';
76
- import { Dropdown } from 'floating-vue';
77
- import 'floating-vue/style.css';
2
+ import { watch } from 'vue'
3
+ import { Dropdown } from 'floating-vue'
4
+ import 'floating-vue/style.css'
78
5
  import {
79
- TextInput, Card, Icon, type MaterialIcons,
80
- } from '@bagelink/vue';
6
+ Card,
7
+ Icon,
8
+ type MaterialIcons,
9
+ TextInput,
10
+ } from '@bagelink/vue'
81
11
 
82
12
  type Option =
83
13
  | string
84
14
  | number
85
15
  | Record<string, any>
86
- | { label: string; value: string | number };
87
-
88
- const searchInput = $ref<HTMLInputElement | null>(null);
16
+ | { label: string, value: string | number }
89
17
 
90
18
  const props = defineProps<{
91
- options: Option[];
92
- placeholder?: string;
93
- disabled?: boolean;
94
- modelValue?: Option;
95
- searchable?: boolean;
96
- required?: boolean;
97
- label?: string;
98
- fullWidth?: boolean;
99
- multiselect?: boolean;
100
- icon?: MaterialIcons;
101
- hideLabel?: boolean;
102
- }>();
103
-
104
- let selectedItems = $ref<Option[]>([]);
105
- let search = $ref('');
106
-
107
- const dropdown = $ref<InstanceType<typeof Dropdown> | null>(null);
108
-
109
- let open = $ref(false);
19
+ options: Option[]
20
+ placeholder?: string
21
+ disabled?: boolean
22
+ modelValue?: Option
23
+ searchable?: boolean
24
+ required?: boolean
25
+ label?: string
26
+ fullWidth?: boolean
27
+ multiselect?: boolean
28
+ icon?: MaterialIcons
29
+ hideLabel?: boolean
30
+ }>()
31
+
32
+ const emit = defineEmits(['update:modelValue'])
33
+
34
+ const searchInput = $ref<HTMLInputElement | null>(null)
35
+
36
+ let selectedItems = $ref<Option[]>([])
37
+ let search = $ref('')
38
+
39
+ const dropdown = $ref<InstanceType<typeof Dropdown> | null>(null)
40
+
41
+ let open = $ref(false)
110
42
 
111
43
  function openOptions() {
112
- dropdown?.show();
44
+ dropdown?.show()
113
45
  // updateOpen(true);
114
46
  }
115
47
 
116
48
  function updateOpen(visible: boolean) {
117
- open = visible;
118
- if (!open) search = '';
49
+ open = visible
50
+ if (!open) { search = ''
51
+ }
119
52
  else {
120
53
  setTimeout(
121
54
  () => (searchInput as any)?.$el?.querySelector('input')?.focus(),
122
55
  100,
123
- );
56
+ )
124
57
  }
125
58
  }
126
59
 
127
60
  const selectedLabel = $computed(() => {
128
- if (selectedItems.length === 0) return props.placeholder || 'Select';
61
+ if (selectedItems.length === 0) return props.placeholder || 'Select'
129
62
  if (selectedItems.length > 4) {
130
63
  const str = selectedItems
131
64
  .slice(0, 4)
132
- .map((item) => getLabel(item))
133
- .join(', ');
134
- return `${str}... +${selectedItems.length - 4}`;
65
+ .map(item => getLabel(item))
66
+ .join(', ')
67
+ return `${str}... +${selectedItems.length - 4}`
135
68
  }
136
- return selectedItems.map((item) => getLabel(item)).join(', ');
137
- });
138
-
139
- const emit = defineEmits(['update:modelValue']);
140
-
141
- const getLabel = (option: Option) => {
142
- if (!option) return '';
143
- if (typeof option === 'string') return option;
144
- if (typeof option === 'number') return `${option}`;
145
- return option.label;
146
- };
69
+ return selectedItems.map(item => getLabel(item)).join(', ')
70
+ })
71
+
72
+ function getLabel(option: Option) {
73
+ if (!option) return ''
74
+ if (typeof option === 'string') return option
75
+ if (typeof option === 'number') return `${option}`
76
+ return option.label
77
+ }
147
78
 
148
- const getValue = (option?: Option) => {
149
- if (!option) return undefined;
150
- if (typeof option === 'string') return option;
151
- if (typeof option === 'number') return option;
152
- return option.value;
153
- };
79
+ function getValue(option?: Option) {
80
+ if (!option) return undefined
81
+ if (typeof option === 'string') return option
82
+ if (typeof option === 'number') return option
83
+ return option.value
84
+ }
154
85
 
155
- const isSelected = (option: Option) => !!selectedItems.find((item) => getValue(option) === getValue(item));
86
+ const isSelected = (option: Option) => !!selectedItems.find(item => getValue(option) === getValue(item))
156
87
 
157
88
  const filteredOptions = $computed(() => props.options.filter((option) => {
158
- const searchTerm = search
159
- .split(/\s+/)
160
- .filter(Boolean)
161
- .map((t) => new RegExp(t, 'gi'));
162
- return (
163
- Boolean(option) &&
164
- (searchTerm.every((s) => getLabel(option).match(s)) ||
165
- searchTerm.length === 0)
166
- );
167
- }));
168
-
169
- const select = (option: Option) => {
89
+ const searchTerm = search
90
+ .split(/\s+/)
91
+ .filter(Boolean)
92
+ .map(t => new RegExp(t, 'gi'))
93
+ return (
94
+ Boolean(option)
95
+ && (searchTerm.every(s => getLabel(option).match(s))
96
+ || searchTerm.length === 0)
97
+ )
98
+ }))
99
+
100
+ function select(option: Option) {
170
101
  const existingIndex = selectedItems.findIndex(
171
- (item) => getValue(item) === getValue(option),
172
- );
173
- if (existingIndex > -1) selectedItems.splice(existingIndex, 1);
102
+ item => getValue(item) === getValue(option),
103
+ )
104
+ if (existingIndex > -1) { selectedItems.splice(existingIndex, 1)
105
+ }
174
106
  else if (props.multiselect) {
175
- const current = [...selectedItems];
176
- current.push(option);
107
+ const current = [...selectedItems]
108
+ current.push(option)
177
109
 
178
- selectedItems = current;
179
- } else selectedItems.splice(0, selectedItems.length, option);
110
+ selectedItems = current
111
+ } else { selectedItems.splice(0, selectedItems.length, option)
112
+ }
180
113
 
181
- if (!props.multiselect) dropdown?.hide();
182
- emitUpdate();
183
- };
114
+ if (!props.multiselect) dropdown?.hide()
115
+ emitUpdate()
116
+ }
184
117
 
185
118
  function emitUpdate() {
186
119
  if (props.multiselect) {
187
- emit('update:modelValue', selectedItems.map(getValue).filter(Boolean));
120
+ emit('update:modelValue', selectedItems.map(getValue).filter(Boolean))
188
121
  } else {
189
122
  // selectedItems.splice(1, selectedItems.length - 1);
190
- const [item] = selectedItems;
191
- emit('update:modelValue', getValue(item));
123
+ const [item] = selectedItems
124
+ emit('update:modelValue', getValue(item))
192
125
  }
193
126
  }
194
127
 
195
128
  function compareArrays(arr1: Option[], arr2: Option[]) {
196
- const arr1Values = [...arr1].map((a) => getValue(a)).filter(Boolean);
197
- const arr2Values = [...arr2].map((a) => getValue(a)).filter(Boolean);
198
- const isSame = arr1Values.every((item: Option) => arr2Values.includes(item));
199
- return isSame;
129
+ const arr1Values = [...arr1].map(a => getValue(a)).filter(Boolean)
130
+ const arr2Values = [...arr2].map(a => getValue(a)).filter(Boolean)
131
+ const isSame = arr1Values.every((item: Option) => arr2Values.includes(item))
132
+ return isSame
200
133
  }
201
134
 
202
135
  watch(
203
136
  () => props.modelValue,
204
137
  (newVal: Option | Option[]) => {
205
138
  if (!props.multiselect) {
206
- const newOption =
207
- props.options?.find((o) => getValue(o) === newVal) || newVal;
208
- if (newOption && !isSelected(newOption)) selectedItems = [newOption];
139
+ const newOption
140
+ = props.options.find(o => getValue(o) === newVal) || newVal
141
+ if (newOption && !isSelected(newOption)) selectedItems = [newOption]
209
142
  } else {
210
- const isSame = compareArrays([newVal].flat(), selectedItems);
143
+ const isSame = compareArrays([newVal].flat(), selectedItems)
211
144
  if (!isSame) {
212
- selectedItems.splice(0, selectedItems.length, ...[newVal].flat());
145
+ selectedItems.splice(0, selectedItems.length, ...[newVal].flat())
213
146
  }
214
147
  }
215
148
  },
216
149
  { immediate: true },
217
- );
150
+ )
218
151
 
219
152
  watch(
220
153
  () => props.options,
221
154
  () => {
222
155
  selectedItems.forEach((option, i) => {
223
156
  const exists = props.options.find(
224
- (o) => getValue(o) === getValue(option),
225
- );
226
- if (!exists) selectedItems.splice(i, 1);
227
- else selectedItems.splice(i, 1, exists);
228
- });
157
+ o => getValue(o) === getValue(option),
158
+ )
159
+ if (!exists) selectedItems.splice(i, 1)
160
+ else selectedItems.splice(i, 1, exists)
161
+ })
229
162
  // const original = JSON.stringify(props.options.map(getValue));
230
163
  // const newSelection = JSON.stringify(selectedItems.map(getValue));
231
164
  // if (original !== newSelection) emitUpdate();
232
165
  },
233
166
  { deep: true, immediate: true },
234
- );
167
+ )
235
168
  </script>
236
169
 
170
+ <template>
171
+ <Dropdown
172
+ ref="dropdown"
173
+ placement="bottom-start"
174
+ class="bagel-input selectinput"
175
+ @click.stop
176
+ @hide="updateOpen(false)"
177
+ >
178
+ <label>
179
+ {{ label }}
180
+ <button
181
+ :disabled="disabled"
182
+ type="button"
183
+ class="selectinput-btn"
184
+ :class="{ isEmpty: selectedItems.length === 0 }"
185
+ @keydown="openOptions"
186
+ @click="updateOpen(true)"
187
+ >
188
+ <Icon v-if="icon" :icon="icon" />
189
+ <p v-if="!hideLabel">
190
+ {{ selectedLabel }}
191
+ </p>
192
+ <Icon
193
+ v-if="!disabled"
194
+ thin
195
+ v-bind="{ icon: open ? 'unfold_less' : 'unfold_more' }"
196
+ />
197
+ </button>
198
+ <input
199
+ v-if="required"
200
+ tabindex="-1"
201
+ style="width: 0; height: 0; position: absolute; opacity: 0; z-index: -1"
202
+ :value="selectedItems"
203
+ required
204
+ @input="updateOpen(true)"
205
+ >
206
+ </label>
207
+ <template #popper>
208
+ <Card
209
+ class="selectinput-options p-05"
210
+ :style="{ width: fullWidth ? '100%' : 'auto' }"
211
+ >
212
+ <TextInput
213
+ v-if="searchable"
214
+ ref="searchInput"
215
+ v-model="search"
216
+ dense
217
+ placeholder="Search"
218
+ icon="search"
219
+ />
220
+ <div
221
+ v-for="(option, i) in filteredOptions"
222
+ :key="`${option}${i}`"
223
+ class="selectinput-option hover gap-1"
224
+ :class="{ selected: isSelected(option) }"
225
+ @click="select(option)"
226
+ >
227
+ <Icon v-if="isSelected(option)" icon="check" />
228
+ <Icon
229
+ v-if="!isSelected(option)"
230
+ class="opacity-3"
231
+ icon="fiber_manual_record"
232
+ />
233
+ <span>
234
+ {{ getLabel(option) }}
235
+ </span>
236
+ </div>
237
+ <slot name="last" />
238
+ </Card>
239
+ </template>
240
+ </Dropdown>
241
+ </template>
242
+
237
243
  <style scoped>
238
244
  .selectinput {
239
245
  width: 100%;
@@ -0,0 +1,150 @@
1
+ <script setup lang="ts">
2
+ import SignaturePad from 'signature_pad'
3
+ import type { Options as SignaturePadOptions } from 'signature_pad'
4
+
5
+ import { onMounted, watch } from 'vue'
6
+
7
+ interface WaterMark {
8
+ text: string
9
+ x: number
10
+ y: number
11
+ sx: number
12
+ sy: number
13
+ font: string
14
+ fillStyle: string
15
+ strokeStyle: string
16
+ style: string
17
+ }
18
+
19
+ type FormatType = 'image/png' | 'image/jpeg' | 'image/svg+xml'
20
+
21
+ const props = defineProps<{
22
+ sigOption?: SignaturePadOptions
23
+ width?: string
24
+ height?: string
25
+ ratio?: number
26
+ clearOnResize?: boolean
27
+ waterMark?: WaterMark
28
+ disabled?: boolean
29
+ defaultUrl?: string
30
+ format?: FormatType
31
+ }>()
32
+
33
+ const vCanvas = $ref<HTMLCanvasElement>()
34
+
35
+ let sig = $ref<SignaturePad>()
36
+
37
+ const clear = () => sig?.clear()
38
+
39
+ function save(format?: FormatType) {
40
+ format = format || props.format
41
+ return format ? sig?.toDataURL(format) : sig?.toDataURL()
42
+ }
43
+
44
+ const fromDataURL = (url: string) => sig?.fromDataURL(url)
45
+
46
+ const isEmpty = () => sig?.isEmpty()
47
+ function undo() {
48
+ const data = sig?.toData()
49
+ if (data) {
50
+ data.pop()
51
+ sig?.fromData(data)
52
+ }
53
+ }
54
+
55
+ const defaultOptions = $ref<SignaturePadOptions>({
56
+ penColor: 'rgb(0, 0, 0)',
57
+ })
58
+
59
+ const signatureOptions = $computed(() => ({ ...defaultOptions, ...props.sigOption }))
60
+
61
+ watch(
62
+ () => props.disabled,
63
+ (val) => {
64
+ if (val) sig?.off()
65
+ else sig?.on()
66
+ },
67
+ )
68
+
69
+ function addWaterMark(data: WaterMark) {
70
+ if (!(Object.prototype.toString.call(data) === '[object Object]')) {
71
+ throw new Error(`Expected Object, got ${typeof data}.`)
72
+ } else {
73
+ const textData = {
74
+ text: data.text || '',
75
+ x: data.x || 20,
76
+ y: data.y || 20,
77
+ sx: data.sx || 40,
78
+ sy: data.sy || 40,
79
+ }
80
+
81
+ const ctx = vCanvas?.getContext('2d')
82
+ if (!ctx) return
83
+ ctx.font = data.font || '20px sans-serif'
84
+ ctx.fillStyle = data.fillStyle || '#333'
85
+ ctx.strokeStyle = data.strokeStyle || '#333'
86
+ if (data.style === 'all') {
87
+ ctx.fillText(textData.text, textData.x, textData.y)
88
+ ctx.strokeText(textData.text, textData.sx, textData.sy)
89
+ } else if (data.style === 'stroke') {
90
+ ctx.strokeText(textData.text, textData.sx, textData.sy)
91
+ } else {
92
+ ctx.fillText(textData.text, textData.x, textData.y)
93
+ }
94
+ }
95
+ }
96
+
97
+ function draw() {
98
+ const canvas = vCanvas!
99
+ sig = new SignaturePad(canvas, signatureOptions)
100
+ function resizeCanvas(c: HTMLCanvasElement) {
101
+ let url
102
+ if (!isEmpty()) url = save()
103
+ const ratio = props.ratio || 1 / 2
104
+ const { width } = c.getBoundingClientRect()
105
+ c.width = props.width ? Number(props.width.replace(/px/g, '')) : width
106
+ c.height = props.height ? Number(props.height.replace(/px/g, '')) : width * ratio
107
+ clear()
108
+ if (!props.clearOnResize && url !== undefined) fromDataURL(url)
109
+ if (props.waterMark) addWaterMark(props.waterMark)
110
+ }
111
+ window.addEventListener('resize', () => { resizeCanvas(canvas) })
112
+ resizeCanvas(canvas)
113
+ if (props.disabled) sig.off()
114
+ else sig.on()
115
+ }
116
+
117
+ onMounted(draw)
118
+
119
+ defineExpose({
120
+ save,
121
+ clear,
122
+ isEmpty,
123
+ undo,
124
+ addWaterMark,
125
+ fromDataURL,
126
+ })
127
+ </script>
128
+
129
+ <template>
130
+ <div class="bgl_input signature-pad" @touchmove.prevent>
131
+ <canvas ref="vCanvas" class="canvas" :disabled="disabled" />
132
+ </div>
133
+ </template>
134
+
135
+ <style>
136
+ .bgl_input .canvas {
137
+ width: 100%;
138
+ height: 100%;
139
+ }
140
+
141
+ .bgl_input.signature-pad {
142
+ background: var(--input-bg);
143
+ border: none;
144
+ border-radius: var(--input-border-radius);
145
+ }
146
+
147
+ .bgl_input.signature-pad .canvas[disabled] {
148
+ background: var(--input-disabled-bg);
149
+ }
150
+ </style>