@bagelink/vue 0.0.348 → 0.0.350

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.
@@ -1,53 +1,89 @@
1
1
  <template>
2
- <Dropdown @hide="updateOpen(false)" ref="dropdown" placement="bottom-start" class="bagel-input selectinput">
3
- <label>
4
- {{ label }}
5
- <button @click="updateOpen(true)" :disabled="disabled" type="button" class="selectinput-btn">
6
- {{ selecteLabel }}
7
- <Icon v-bind="{ 'icon': open ? 'unfold_less' : 'unfold_more' }" />
8
- </button>
9
- <input style="width: 0; height: 0; position: absolute; opacity: 0; z-index: -1;" v-if="required"
10
- v-model="selectedItems" required>
11
- </label>
12
- <template #popper="{ hide }">
13
- <Card class="selectinput-options p-05" :style="{ width: fullWidth ? '100%' : 'auto' }">
14
- <TextInput v-if="searchable" ref="searchInput" dense :placeholder="'Search'" icon="search" v-model="search" />
15
- <div class="selectinput-option hover gap-1" v-for="(option, i) in filteredOptions" :key="`${option}${i}`"
16
- @click="select(option)" :class="{ selected: isSelected(option) }">
17
- <Icon v-if="isSelected(option)" icon="check" />
18
- <Icon class="opacity-3" v-if="!isSelected(option)" icon="fiber_manual_record" />
19
- <span>
20
- {{ getLabel(option) }}
21
- </span>
22
- </div>
23
- <slot name="last" />
24
- </Card>
25
- </template>
26
- </Dropdown>
2
+ <Dropdown
3
+ @hide="updateOpen(false)"
4
+ ref="dropdown"
5
+ placement="bottom-start"
6
+ class="bagel-input selectinput"
7
+ >
8
+ <label>
9
+ {{ label }}
10
+ <button
11
+ @click="updateOpen(true)"
12
+ :disabled="disabled"
13
+ type="button"
14
+ class="selectinput-btn"
15
+ :class="{isEmpty: selectedItems.length ===0}"
16
+ >
17
+ <p>{{ selectedLabel }}</p>
18
+ <Icon v-bind="{ icon: open ? 'unfold_less' : 'unfold_more' }" />
19
+ </button>
20
+ <input
21
+ style="width: 0; height: 0; position: absolute; opacity: 0; z-index: -1"
22
+ v-if="required"
23
+ v-model="selectedItems"
24
+ required
25
+ >
26
+ </label>
27
+ <template #popper="{ hide }">
28
+ <Card
29
+ class="selectinput-options p-05"
30
+ :style="{ width: fullWidth ? '100%' : 'auto' }"
31
+ >
32
+ <TextInput
33
+ v-if="searchable"
34
+ ref="searchInput"
35
+ dense
36
+ :placeholder="'Search'"
37
+ icon="search"
38
+ v-model="search"
39
+ />
40
+ <div
41
+ class="selectinput-option hover gap-1"
42
+ v-for="(option, i) in filteredOptions"
43
+ :key="`${option}${i}`"
44
+ @click="select(option)"
45
+ :class="{ selected: isSelected(option) }"
46
+ >
47
+ <Icon v-if="isSelected(option)" icon="check" />
48
+ <Icon
49
+ class="opacity-3"
50
+ v-if="!isSelected(option)"
51
+ icon="fiber_manual_record"
52
+ />
53
+ <span>
54
+ {{ getLabel(option) }}
55
+ </span>
56
+ </div>
57
+ <slot name="last" />
58
+ </Card>
59
+ </template>
60
+ </Dropdown>
27
61
  </template>
28
62
 
29
63
  <script lang="ts" setup>
30
64
  import { watch } from 'vue';
31
65
  import { Dropdown } from 'floating-vue';
32
66
  import 'floating-vue/style.css';
33
- import {
34
- TextInput, Card, Icon,
35
- } from '@bagelink/vue';
67
+ import { TextInput, Card, Icon } from '@bagelink/vue';
36
68
 
37
- type Option = string | number | Record<string, any> | { label: string, value: string | number };
69
+ type Option =
70
+ | string
71
+ | number
72
+ | Record<string, any>
73
+ | { label: string; value: string | number };
38
74
 
39
75
  const searchInput = $ref<HTMLInputElement | null>(null);
40
76
 
41
77
  const props = defineProps<{
42
- options: Option[];
43
- placeholder?: string;
44
- disabled?: boolean;
45
- modelValue?: Option;
46
- searchable?: boolean;
47
- required?: boolean;
48
- label?: string;
49
- fullWidth?: boolean;
50
- multiselect?: boolean;
78
+ options: Option[];
79
+ placeholder?: string;
80
+ disabled?: boolean;
81
+ modelValue?: Option;
82
+ searchable?: boolean;
83
+ required?: boolean;
84
+ label?: string;
85
+ fullWidth?: boolean;
86
+ multiselect?: boolean;
51
87
  }>();
52
88
 
53
89
  const selectedItems = $ref<Option[]>([]);
@@ -58,150 +94,172 @@ const dropdown = $ref<InstanceType<typeof Dropdown> | null>(null);
58
94
  let open = $ref(false);
59
95
 
60
96
  function updateOpen(visible: boolean) {
61
- open = visible;
62
- if (!open) search = '';
63
- else setTimeout(() => (searchInput as any)?.$el?.querySelector('input')?.focus(), 100);
97
+ open = visible;
98
+ if (!open) search = '';
99
+ else {
100
+ setTimeout(
101
+ () => (searchInput as any)?.$el?.querySelector('input')?.focus(),
102
+ 100,
103
+ );
104
+ }
64
105
  }
65
106
 
66
- const selecteLabel = $computed(() => {
67
- if (selectedItems.length === 0) return props.placeholder || 'Select';
68
- if (selectedItems.length > 4) {
69
- const str = selectedItems.slice(0, 4).map((item) => getLabel(item)).join(', ');
70
- return `${str}... +${selectedItems.length - 4}`;
71
- }
72
- return selectedItems.map((item) => getLabel(item)).join(', ');
107
+ const selectedLabel = $computed(() => {
108
+ if (selectedItems.length === 0) return props.placeholder || 'Select';
109
+ if (selectedItems.length > 4) {
110
+ const str = selectedItems
111
+ .slice(0, 4)
112
+ .map((item) => getLabel(item))
113
+ .join(', ');
114
+ return `${str}... +${selectedItems.length - 4}`;
115
+ }
116
+ return selectedItems.map((item) => getLabel(item)).join(', ');
73
117
  });
74
118
 
75
119
  const emit = defineEmits(['update:modelValue']);
76
120
 
77
121
  const getLabel = (option: Option) => {
78
- if (typeof option === 'string') return option;
79
- if (typeof option === 'number') return `${option}`;
80
- return option.label;
122
+ if (typeof option === 'string') return option;
123
+ if (typeof option === 'number') return `${option}`;
124
+ return option.label;
81
125
  };
82
126
 
83
127
  const getValue = (option: Option) => {
84
- if (typeof option === 'string') return option;
85
- if (typeof option === 'number') return option;
86
- return option.value;
128
+ if (typeof option === 'string') return option;
129
+ if (typeof option === 'number') return option;
130
+ return option.value;
87
131
  };
88
132
 
89
- const isSelected = (option: Option) => selectedItems.includes(getValue(option));
133
+ const isSelected = (option: Option) => !!selectedItems.find((item) => getValue(option) === getValue(item));
90
134
 
91
135
  const filteredOptions = $computed(() => props.options.filter((option) => {
92
- const searchTerm = new RegExp(search, 'gi');
93
- if (typeof option === 'string') return option.match(searchTerm);
94
- if (typeof option === 'number') return `${option}`.match(searchTerm);
95
- return option.label.match(searchTerm);
136
+ const searchTerm = new RegExp(search, 'gi');
137
+ return (
138
+ Boolean(option) &&
139
+ (getLabel(option).match(searchTerm) || getValue(option).match(searchTerm))
140
+ );
96
141
  }));
97
142
 
98
143
  const select = (option: Option) => {
99
- const existingIndex = selectedItems.findIndex((item) => {
100
- if (typeof item === 'string' || typeof item === 'number') return item === option;
101
- return item.value === option;
102
- });
103
- if (existingIndex > -1) selectedItems.splice(existingIndex, 1);
104
- else if (props.multiselect) {
105
- selectedItems.push(getValue(option));
106
- } else {
107
- selectedItems.splice(0, selectedItems.length, getValue(option));
108
- dropdown?.hide();
109
- }
110
- emitUpdate();
144
+ const existingIndex = selectedItems.findIndex((item) => getValue(item) === getValue(option));
145
+ if (existingIndex > -1) selectedItems.splice(existingIndex, 1);
146
+ else if (props.multiselect) {
147
+ selectedItems.push(option);
148
+ } else {
149
+ selectedItems.splice(0, selectedItems.length, option);
150
+ }
151
+ if (!props.multiselect) dropdown?.hide();
152
+ emitUpdate();
111
153
  };
112
154
 
113
155
  function emitUpdate() {
114
- if (props.multiselect) {
115
- emit('update:modelValue', selectedItems);
116
- } else {
117
- selectedItems.splice(1, selectedItems.length);
118
- emit('update:modelValue', selectedItems[0]);
119
- }
156
+ if (props.multiselect) {
157
+ emit('update:modelValue', selectedItems.map(getValue));
158
+ } else {
159
+ selectedItems.splice(1, selectedItems.length - 1);
160
+ const [item] = selectedItems;
161
+ emit('update:modelValue', item ? getValue(item) : null);
162
+ }
120
163
  }
121
164
 
122
165
  function compareArrays(arr1: Option[], arr2: Option[]) {
123
- const isSame = arr1.every((item: Option) => arr2.map((a) => getValue(a)).includes(getValue(item)));
124
- return isSame;
166
+ const isSame = arr1.every((item: Option) => arr2.map((a) => getValue(a)).includes(getValue(item)));
167
+ return isSame;
125
168
  }
126
169
 
127
170
  watch(
128
- () => props.modelValue,
129
- (newVal: Option | Option[]) => {
130
- if (!newVal) return;
131
- if (!props.multiselect) {
132
- if (!isSelected(newVal)) selectedItems.splice(0, selectedItems.length, newVal);
133
- } else {
134
- const isSame = compareArrays([newVal].flat(), selectedItems);
135
- if (!isSame) selectedItems.splice(0, selectedItems.length, [newVal].flat());
136
- }
137
- },
138
- { immediate: true },
171
+ () => props.modelValue,
172
+ (newVal: Option | Option[]) => {
173
+ if (!newVal) return;
174
+ if (!props.multiselect) {
175
+ if (!isSelected(newVal)) selectedItems.splice(0, selectedItems.length, newVal);
176
+ } else {
177
+ const isSame = compareArrays([newVal].flat(), selectedItems);
178
+ if (!isSame) selectedItems.splice(0, selectedItems.length, [newVal].flat());
179
+ }
180
+ },
181
+ { immediate: true },
139
182
  );
183
+
184
+ watch(() => props.options, () => {
185
+ const original = JSON.stringify(selectedItems);
186
+ [...selectedItems].forEach((option, i) => {
187
+ const exists = props.options.find((o) => getValue(o) === getValue(option));
188
+ if (!exists) selectedItems.splice(i, 1);
189
+ else selectedItems.splice(i, 1, exists);
190
+ });
191
+ const newSelection = JSON.stringify(selectedItems);
192
+ if (original !== newSelection) emitUpdate();
193
+ });
140
194
  </script>
141
195
 
142
196
  <style scoped>
143
197
  .selectinput {
144
- width: 100%;
198
+ width: 100%;
145
199
  }
146
200
 
147
201
  .selectinput-option {
148
- padding: 6px 12px;
149
- cursor: pointer;
150
- border-radius: 5px;
151
- transition: all 0.2s;
152
- display: grid;
153
- grid-template-columns: 10px 1fr;
154
- justify-content: space-between;
155
- width: 100%;
156
- font-size: var(--input-font-size);
202
+ padding: 6px 12px;
203
+ cursor: pointer;
204
+ border-radius: 5px;
205
+ transition: all 0.2s;
206
+ display: grid;
207
+ grid-template-columns: 10px 1fr;
208
+ justify-content: space-between;
209
+ width: 100%;
210
+ font-size: var(--input-font-size);
157
211
  }
158
212
 
159
213
  .selectinput-options {
160
- max-height: 300px;
161
- overflow-y: auto;
214
+ max-height: 300px;
215
+ overflow-y: auto;
162
216
  }
163
217
 
164
218
  .selectinput-option:hover {
165
- background: var(--bgl-gray-20);
219
+ background: var(--bgl-gray-20);
220
+ }
221
+ .isEmpty p{
222
+ opacity: 0.3;
166
223
  }
167
224
  </style>
168
225
 
169
226
  <style>
170
227
  .bagel-input label {
171
- font-size: var(--label-font-size);
228
+ font-size: var(--label-font-size);
172
229
  }
173
230
 
174
231
  .selectinput-btn {
175
- display: flex;
176
- justify-content: space-between;
177
- align-items: center;
178
- height: var(--input-height);
179
- border-radius: var(--input-border-radius);
180
- border: none;
181
- background: var(--input-bg);
182
- padding: 0.7rem;
183
- color: var(--input-color);
184
- width: 100%;
185
- font-family: inherit;
232
+ display: flex;
233
+ justify-content: space-between;
234
+ align-items: center;
235
+ height: var(--input-height);
236
+ border-radius: var(--input-border-radius);
237
+ border: none;
238
+ background: var(--input-bg);
239
+ padding: 0.7rem;
240
+ color: var(--input-color);
241
+ width: 100%;
242
+ font-family: inherit;
243
+ font-size: var(--input-font-size);
186
244
  }
187
245
 
188
246
  .selectinput-btn:disabled {
189
- background: var(--input-disabled-bg);
190
- color: var(--input-disabled-color);
247
+ color: var(--input-disabled-color);
191
248
  }
192
249
 
193
250
  .selectinput-btn:focus {
194
- outline: none;
195
- box-shadow: inset 0 0 10px #00000012;
251
+ outline: none;
252
+ box-shadow: inset 0 0 10px #00000012;
196
253
  }
197
254
 
198
255
  .v-popper__arrow-container {
199
- display: none;
256
+ display: none;
200
257
  }
201
258
 
202
259
  .v-popper--theme-dropdown .v-popper__inner {
203
- border: none;
204
- background: transparent;
205
- border-radius: var(--card-border-radius);
260
+ border: none;
261
+ background: transparent;
262
+ border-radius: var(--card-border-radius);
263
+ color: var(--input-color);
206
264
  }
207
265
  </style>
@@ -11,3 +11,4 @@ export { default as RadioPillsInput } from './RadioPillsInput.vue';
11
11
  export { default as FileUpload } from './FileUpload.vue';
12
12
  export { default as ToggleInput } from './ToggleInput.vue';
13
13
  export { default as RichText } from './RichText.vue';
14
+ export { Dropdown } from 'floating-vue';
@@ -65,8 +65,16 @@
65
65
  filter: brightness(80%);
66
66
  }
67
67
 
68
+ .border {
69
+ border: 1px solid var(--border-color);
70
+ }
71
+
68
72
  @media screen and (max-width: 910px) {
69
73
  .bgl_btn {
70
74
  padding: 0 20px;
71
75
  }
76
+
77
+ .m_border {
78
+ border: 1px solid var(--border-color);
79
+ }
72
80
  }
@@ -1404,11 +1404,11 @@
1404
1404
  }
1405
1405
 
1406
1406
  .vh-100 {
1407
- height: 100vh;
1407
+ height: 100vh !important;
1408
1408
  }
1409
1409
 
1410
1410
  .h-100px {
1411
- height: 100px;
1411
+ height: 100px !important;
1412
1412
  }
1413
1413
 
1414
1414
  .hm-100px {
@@ -1517,6 +1517,42 @@
1517
1517
  overflow-y: hidden;
1518
1518
  }
1519
1519
 
1520
+ .grid-wrap-50 {
1521
+ grid-template-columns: repeat(auto-fit, minmax(min(50px, 100%), 1fr));
1522
+ height: max-content;
1523
+ width: 100%
1524
+ }
1525
+
1526
+ .grid-wrap-100 {
1527
+ grid-template-columns: repeat(auto-fit, minmax(min(100px, 100%), 1fr));
1528
+ height: max-content;
1529
+ width: 100%
1530
+ }
1531
+
1532
+ .grid-wrap-200 {
1533
+ grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
1534
+ height: max-content;
1535
+ width: 100%
1536
+ }
1537
+
1538
+ .grid-wrap-300 {
1539
+ grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
1540
+ height: max-content;
1541
+ width: 100%
1542
+ }
1543
+
1544
+ .grid-wrap-400 {
1545
+ grid-template-columns: repeat(auto-fit, minmax(min(400px, 100%), 1fr));
1546
+ height: max-content;
1547
+ width: 100%
1548
+ }
1549
+
1550
+ .grid-wrap-500 {
1551
+ grid-template-columns: repeat(auto-fit, minmax(min(500px, 100%), 1fr));
1552
+ height: max-content;
1553
+ width: 100%
1554
+ }
1555
+
1520
1556
  .grid-wrap-1 {
1521
1557
  grid-template-columns: repeat(1, 1fr);
1522
1558
  height: max-content;
@@ -1423,6 +1423,43 @@
1423
1423
  overflow-y: hidden;
1424
1424
  }
1425
1425
 
1426
+
1427
+ .m_grid-wrap-50 {
1428
+ grid-template-columns: repeat(auto-fit, minmax(min(50px, 100%), 1fr));
1429
+ height: max-content;
1430
+ width: 100%
1431
+ }
1432
+
1433
+ .m_grid-wrap-100 {
1434
+ grid-template-columns: repeat(auto-fit, minmax(min(100px, 100%), 1fr));
1435
+ height: max-content;
1436
+ width: 100%
1437
+ }
1438
+
1439
+ .m_grid-wrap-200 {
1440
+ grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
1441
+ height: max-content;
1442
+ width: 100%
1443
+ }
1444
+
1445
+ .m_grid-wrap-300 {
1446
+ grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
1447
+ height: max-content;
1448
+ width: 100%
1449
+ }
1450
+
1451
+ .m_grid-wrap-400 {
1452
+ grid-template-columns: repeat(auto-fit, minmax(min(400px, 100%), 1fr));
1453
+ height: max-content;
1454
+ width: 100%
1455
+ }
1456
+
1457
+ .m_grid-wrap-500 {
1458
+ grid-template-columns: repeat(auto-fit, minmax(min(500px, 100%), 1fr));
1459
+ height: max-content;
1460
+ width: 100%
1461
+ }
1462
+
1426
1463
  .m_grid-wrap-1 {
1427
1464
  grid-template-columns: repeat(1, 1fr);
1428
1465
  height: max-content;