@bagelink/vue 0.0.562 → 0.0.566

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 (51) hide show
  1. package/dist/components/DataPreview.vue.d.ts.map +1 -1
  2. package/dist/components/MaterialIcon.vue.d.ts +2 -0
  3. package/dist/components/MaterialIcon.vue.d.ts.map +1 -1
  4. package/dist/components/TableSchema.vue.d.ts +34 -21
  5. package/dist/components/TableSchema.vue.d.ts.map +1 -1
  6. package/dist/components/form/BglForm.vue.d.ts.map +1 -1
  7. package/dist/components/form/inputs/CheckInput.vue.d.ts +21 -11
  8. package/dist/components/form/inputs/CheckInput.vue.d.ts.map +1 -1
  9. package/dist/components/form/inputs/DateInput.vue.d.ts +2 -0
  10. package/dist/components/form/inputs/DateInput.vue.d.ts.map +1 -1
  11. package/dist/components/form/inputs/FileUpload.vue.d.ts +31 -41
  12. package/dist/components/form/inputs/FileUpload.vue.d.ts.map +1 -1
  13. package/dist/components/form/inputs/RadioPillsInput.vue.d.ts +1 -1
  14. package/dist/components/form/inputs/RadioPillsInput.vue.d.ts.map +1 -1
  15. package/dist/components/form/inputs/TelInput.vue.d.ts +11 -11
  16. package/dist/components/form/inputs/TelInput.vue.d.ts.map +1 -1
  17. package/dist/components/form/inputs/ToggleInput.vue.d.ts +18 -3
  18. package/dist/components/form/inputs/ToggleInput.vue.d.ts.map +1 -1
  19. package/dist/components/layout/TabbedLayout.vue.d.ts.map +1 -1
  20. package/dist/composables/index.d.ts +10 -0
  21. package/dist/composables/index.d.ts.map +1 -0
  22. package/dist/index.cjs +620 -201
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.mjs +621 -202
  26. package/dist/style.css +262 -119
  27. package/dist/types/BagelForm.d.ts +1 -0
  28. package/dist/types/BagelForm.d.ts.map +1 -1
  29. package/dist/utils/index.d.ts +4 -2
  30. package/dist/utils/index.d.ts.map +1 -1
  31. package/package.json +15 -14
  32. package/src/components/Btn.vue +1 -1
  33. package/src/components/DataPreview.vue +12 -13
  34. package/src/components/MapEmbed.vue +2 -2
  35. package/src/components/MaterialIcon.vue +7 -3
  36. package/src/components/TableSchema.vue +294 -108
  37. package/src/components/form/BglFieldSet.vue +0 -2
  38. package/src/components/form/BglForm.vue +2 -4
  39. package/src/components/form/inputs/CheckInput.vue +13 -6
  40. package/src/components/form/inputs/DateInput.vue +2 -0
  41. package/src/components/form/inputs/FileUpload.vue +63 -47
  42. package/src/components/form/inputs/ToggleInput.vue +22 -5
  43. package/src/components/layout/TabbedLayout.vue +1 -1
  44. package/src/composables/index.ts +24 -0
  45. package/src/index.ts +1 -0
  46. package/src/styles/layout.css +24 -0
  47. package/src/styles/mobilLayout.css +23 -0
  48. package/src/styles/text.css +0 -2
  49. package/src/styles/theme.css +2 -1
  50. package/src/types/BagelForm.ts +2 -0
  51. package/src/utils/index.ts +24 -2
@@ -3,9 +3,12 @@ import {
3
3
  BglField,
4
4
  type BglFormSchemaT,
5
5
  MaterialIcon,
6
+ isDate,
6
7
  keyToLabel,
8
+ useBglSchema,
7
9
  } from '@bagelink/vue'
8
- import { useSlots } from 'vue'
10
+ import { computed, useSlots, watch } from 'vue'
11
+ import { useVirtualList } from '@vueuse/core'
9
12
 
10
13
  const props = defineProps<{
11
14
  selectedItems?: string[]
@@ -13,51 +16,29 @@ const props = defineProps<{
13
16
  schema?: BglFormSchemaT | (() => BglFormSchemaT)
14
17
  showFields?: string[]
15
18
  }>()
19
+
16
20
  const emit = defineEmits(['update:selectedItems', 'orderBy', 'select'])
17
21
  const slots = useSlots()
18
- const loading = $ref(true)
19
-
20
- let selectedItems = $ref<string[]>(props.selectedItems || [])
21
- let selected = $computed({
22
- get: () => selectedItems,
23
- set: (value: string[]) => {
24
- selectedItems = value
25
- emit('update:selectedItems', value)
26
- },
27
- })
22
+ const loading = defineModel('loading', { default: false })
28
23
 
29
- function selectElement(item: Record<string, any>) { emit('select', item) }
24
+ const itemHeight = defineModel('itemHeight', { default: 50 })
25
+ const computedItemHiehgt = $computed(() => `${itemHeight.value}px`)
30
26
 
31
- let sortDirection = $ref('ASC')
32
27
  let sortField = $ref('')
28
+ let sortDirection = $ref('ASC')
29
+ let selectedItems = $ref<string[]>(props.selectedItems || [])
30
+
31
+ const isSelectable = $computed(() => Array.isArray(props.selectedItems))
33
32
 
34
33
  const computedSortField = $computed(() => `_transformed_${sortField}`)
35
34
 
36
- function getFallbackSchema() {
37
- const keys = [
38
- ...new Set((props.data || []).map(Object.keys).flat()),
39
- ] as string[]
40
- const schema: BglFormSchemaT = keys.map(id => ({
41
- id,
42
- label: keyToLabel(id),
43
- }))
44
- return props.showFields
45
- ? schema.filter(f => props.showFields?.includes(f.id as string) ?? !f.id)
46
- : schema
47
- }
48
-
49
- const computedSchema = $computed(() => {
50
- let _schema = props.schema
51
- if (typeof _schema === 'function') {
52
- _schema = _schema()
53
- }
54
- if (_schema) {
55
- return props.showFields
56
- ? _schema.filter(f => props.showFields?.includes(f.id as string))
57
- : _schema
58
- }
59
- return getFallbackSchema()
60
- })
35
+ const computedSchema = $computed(
36
+ () => useBglSchema({
37
+ schema: props.schema,
38
+ showFields: props.showFields,
39
+ data: props.data,
40
+ })
41
+ )
61
42
 
62
43
  function transform(rowData: any) {
63
44
  const obj: Record<string, any> = { ...rowData }
@@ -70,21 +51,27 @@ function transform(rowData: any) {
70
51
 
71
52
  Object.assign(obj, {
72
53
  [`${field.id}`]: fieldData,
73
- [`_transformed_${field.id}`]: newFieldVal
54
+ [`_transformed_${field.id}`]: newFieldVal,
74
55
  })
75
56
  }
76
57
  return obj
77
58
  }
78
59
 
79
- const computedData = $computed(() => {
60
+ const computedData = computed(() => {
80
61
  const dta = [...props.data].map(transform)
81
62
  if (sortField as string) {
82
63
  return dta.sort((a, b) => {
83
- const aValue = a[computedSortField] ?? a[sortField] ?? ''
84
- const bValue = b[computedSortField] ?? b[sortField] ?? ''
64
+ let aValue = a[computedSortField] ?? a[sortField] ?? ''
65
+ let bValue = b[computedSortField] ?? b[sortField] ?? ''
85
66
 
86
- const numAValue = Number.parseInt(aValue.replace(/[^\d.-]/g, ''), 10)
87
- const numBValue = Number.parseInt(bValue.replace(/[^\d.-]/g, ''), 10)
67
+ if (isDate(aValue) && isDate(bValue)) {
68
+ // ? now we can sort by as number
69
+ aValue = new Date(aValue).getTime()
70
+ bValue = new Date(bValue).getTime()
71
+ }
72
+
73
+ const numAValue = Number.parseInt(`${aValue}`.replace(/[^\d.-]/g, ''), 10)
74
+ const numBValue = Number.parseInt(`${bValue}`.replace(/[^\d.-]/g, ''), 10)
88
75
 
89
76
  if (!Number.isNaN(numAValue) && !Number.isNaN(numBValue)) {
90
77
  if (sortDirection === 'ASC') return numAValue - numBValue
@@ -103,15 +90,6 @@ const computedData = $computed(() => {
103
90
  return dta
104
91
  })
105
92
 
106
- function _unused_SelectAll(event: PointerEvent) {
107
- const value = (event.target as HTMLInputElement).checked
108
- if (!value) {
109
- selected = []
110
- return
111
- }
112
- selected.push(...computedData.map(d => d.id))
113
- }
114
-
115
93
  function sort(fieldname: string) {
116
94
  if (sortField === fieldname) {
117
95
  if (sortDirection === 'ASC') sortDirection = 'DESC'
@@ -122,67 +100,193 @@ function sort(fieldname: string) {
122
100
  }
123
101
  emit('orderBy', `${fieldname} ${sortDirection}`.trim())
124
102
  }
103
+
104
+ // #region ? VIRTUAL LIST
105
+ const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
106
+ computedData,
107
+ {
108
+ itemHeight: itemHeight.value,
109
+ overscan: 10,
110
+ }
111
+ )
112
+
113
+ watch(
114
+ () => computedData.value.length,
115
+ (newLength, oldLength) => {
116
+ if (newLength === oldLength) return
117
+ scrollTo(0)
118
+ }
119
+ )
120
+ // #endregion ? VIRTUAL LIST
121
+
122
+ // #region ? SELECT COLUMN
123
+ const allSelector = $ref<HTMLInputElement | null>(null)
124
+ let computedSelectedItems = $computed({
125
+ get: () => selectedItems,
126
+ set: (value: string[]) => {
127
+ selectedItems = value
128
+ emit('update:selectedItems', value)
129
+ updateAllSelectorState()
130
+ },
131
+ })
132
+
133
+ function updateAllSelectorState() {
134
+ if (!allSelector) return
135
+ const allSelected
136
+ = computedData.value.length === computedSelectedItems.length
137
+ && computedData.value.every((s: any) => computedSelectedItems.includes(s.id))
138
+ allSelector.checked = allSelected
139
+ allSelector.indeterminate = !allSelected && computedSelectedItems.length > 0
140
+ }
141
+
142
+ function toggleSelectItem(item: Record<string, any>) {
143
+ if (!computedSelectedItems.length) {
144
+ emit('select', item)
145
+ return
146
+ }
147
+ const index = computedSelectedItems.indexOf(item.id)
148
+ if (index > -1) {
149
+ computedSelectedItems.splice(index, 1)
150
+ } else {
151
+ computedSelectedItems.push(item.id)
152
+ }
153
+ }
154
+
155
+ function toggleSelectAll(event: Event) {
156
+ const value = (event.target as HTMLInputElement).checked
157
+ computedSelectedItems = value ? computedData.value.map((d: any) => d.id) : []
158
+ }
159
+ // #endregion ? SELECT COLUMN
125
160
  </script>
126
161
 
127
162
  <template>
128
- <div class="table-list-wrap h-100">
129
- <table class="infinite-wrapper">
130
- <thead class="row first-row">
131
- <th
132
- v-for="field in computedSchema"
133
- :key="field.id"
134
- class="col"
135
- @click="sort(field?.id || '')"
136
- >
137
- <div class="flex">
138
- {{ field.label || keyToLabel(field.id) }}
139
- <div
140
- class="list-arrows"
141
- :class="{ sorted: sortField === field.id }"
163
+ <div
164
+ class="table-list-wrap h-100"
165
+ v-bind="containerProps"
166
+ :class="{ 'loading-table': loading }"
167
+ >
168
+ <div
169
+ v-if="loading"
170
+ class="loading-table-wrapper z-99 h-100 w-100 absolute inset"
171
+ >
172
+ <div class="loading-table-animation absolute oval" />
173
+ </div>
174
+ <div
175
+ v-else
176
+ v-bind="wrapperProps"
177
+ >
178
+ <table class="infinite-wrapper">
179
+ <thead class="row first-row">
180
+ <th v-if="isSelectable">
181
+ <input
182
+ ref="allSelector"
183
+ type="checkbox"
184
+ @click.stop
185
+ @change="toggleSelectAll"
142
186
  >
143
- <MaterialIcon
144
- :class="{ desc: sortDirection === 'DESC' }"
145
- icon="keyboard_arrow_up"
146
- />
147
- </div>
148
- </div>
149
- </th>
150
- </thead>
151
- <!-- eslint-disable-next-line vue/no-unused-refs TODO: Can we remove the unused ref ??? -->
152
- <tbody ref="infinite" class="rows infinite" :class="{ loading }">
153
- <tr
154
- v-for="row in computedData"
155
- :key="row.id"
156
- class="row row-item position-relative"
157
- @click="selectElement(row)"
158
- >
159
- <td
187
+ </th>
188
+ <th
160
189
  v-for="field in computedSchema"
161
- :key="`${field.id}-${row.id}`"
190
+ :key="field.id"
162
191
  class="col"
192
+ @click="sort(field?.id || '')"
163
193
  >
164
- <slot
165
- v-if="field.id && slots[field.id]"
166
- :name="field.id"
167
- :row
168
- :field
169
- />
170
- <div v-else>
171
- <BglField
172
- class="embedded-field"
194
+ <div class="flex">
195
+ {{ field.label || keyToLabel(field.id) }}
196
+ <div
197
+ class="list-arrows"
198
+ :class="{ sorted: sortField === field.id }"
199
+ >
200
+ <MaterialIcon
201
+ :class="{ desc: sortDirection === 'DESC' }"
202
+ icon="keyboard_arrow_up"
203
+ />
204
+ </div>
205
+ </div>
206
+ </th>
207
+ </thead>
208
+ <tbody>
209
+ <tr
210
+ v-for="{ data: row } in list"
211
+ :key="row.id"
212
+ class="row row-item position-relative"
213
+ :class="{ selected: computedSelectedItems.includes(row.id) }"
214
+ @click="toggleSelectItem(row)"
215
+ >
216
+ <td v-if="isSelectable">
217
+ <div @click.stop>
218
+ <input
219
+ v-model="computedSelectedItems"
220
+ type="checkbox"
221
+ :value="row.id"
222
+ >
223
+ </div>
224
+ </td>
225
+ <td
226
+ v-for="field in computedSchema"
227
+ :key="`${field.id}-${row.id}`"
228
+ class="col"
229
+ >
230
+ <slot
231
+ v-if="field.id && slots[field.id]"
232
+ :name="field.id"
233
+ :row
173
234
  :field
174
- :modelValue="row"
175
- label=""
176
235
  />
177
- </div>
178
- </td>
179
- </tr>
180
- </tbody>
181
- </table>
236
+ <div v-else>
237
+ <BglField
238
+ class="embedded-field"
239
+ :field
240
+ :modelValue="row"
241
+ label=""
242
+ />
243
+ </div>
244
+ </td>
245
+ </tr>
246
+ </tbody>
247
+ </table>
248
+ </div>
182
249
  </div>
183
250
  </template>
184
251
 
185
252
  <style scoped>
253
+ .selected {
254
+ background: var(--bgl-primary-tint);
255
+ }
256
+
257
+ tbody tr.selected:hover {
258
+ background: var(--bgl-primary-light);
259
+ }
260
+
261
+ .loading-table {
262
+ position: relative;
263
+ }
264
+
265
+ .inset {
266
+ inset: 0;
267
+ }
268
+
269
+ .loading-table-animation {
270
+ --size: 60px;
271
+ top: 30vh;
272
+ inset-inline-start: calc(50% - var(--size));
273
+ border: 3px solid var(--bgl-gray-20);
274
+ border-top: 4px solid var(--bgl-primary);
275
+ width: var(--size);
276
+ height: var(--size);
277
+ animation: loading-table 1s linear infinite;
278
+ }
279
+
280
+ @keyframes loading-table {
281
+ 0% {
282
+ transform: translate(-50%, -50%) rotate(0deg);
283
+ }
284
+
285
+ 100% {
286
+ transform: translate(-50%, -50%) rotate(360deg);
287
+ }
288
+ }
289
+
186
290
  .list-arrows {
187
291
  opacity: 0;
188
292
  }
@@ -210,10 +314,20 @@ table {
210
314
  border-collapse: separate;
211
315
  border-spacing: 0 15px;
212
316
  border-collapse: collapse;
317
+ width: 100%;
213
318
  }
214
319
 
215
320
  th {
321
+ font-size: 0.8rem;
322
+ color: var(--bgl-black-tint);
323
+ position: sticky;
324
+ top: 0;
325
+ z-index: 2;
326
+ background: var(--bgl-white);
327
+ height: v-bind(computedItemHiehgt);
328
+ vertical-align: bottom;
216
329
  font-weight: 400;
330
+ text-align: start;
217
331
  }
218
332
 
219
333
  .embedded-field {
@@ -233,12 +347,8 @@ th {
233
347
  top: 0;
234
348
  z-index: 2;
235
349
  background: var(--bgl-white);
236
- /* height: 50px; */
237
350
  vertical-align: bottom;
238
351
  }
239
- .row.first-row input[type=checkbox]{
240
- margin-bottom: 0.7rem;
241
- }
242
352
 
243
353
  .row.first-row::after {
244
354
  content: '';
@@ -261,11 +371,12 @@ th {
261
371
  line-height: 1;
262
372
  align-items: center;
263
373
  }
374
+
264
375
  .col:has(.bagel-input) {
265
376
  padding: 0rem 0.25rem;
266
377
  }
267
378
 
268
- .col > div {
379
+ .col>div {
269
380
  display: flex;
270
381
  gap: 0.5rem;
271
382
  }
@@ -305,14 +416,15 @@ th {
305
416
  }
306
417
 
307
418
  .row-item {
308
- height: 50px;
419
+ height: v-bind(computedItemHiehgt);
309
420
  transition: all 200ms ease;
310
421
  }
311
422
 
312
423
  .row-item:hover {
313
424
  background: var(--bgl-gray-light);
314
425
  }
315
- .row-item input[type=checkbox]{
426
+
427
+ .row-item input[type='checkbox'] {
316
428
  margin-top: 0.45rem !important;
317
429
  accent-color: var(--bgl-primary);
318
430
  }
@@ -321,4 +433,78 @@ th {
321
433
  overflow-y: auto;
322
434
  width: 100%;
323
435
  }
436
+
437
+ input[type='checkbox'] {
438
+ margin-top: 0.3rem !important;
439
+ accent-color: var(--bgl-primary);
440
+ transform: scale(1.2);
441
+ position: relative;
442
+ cursor: pointer;
443
+ display: flex;
444
+ justify-content: center;
445
+ align-items: center;
446
+ height: 0.85rem;
447
+ width: 0.85rem;
448
+ }
449
+
450
+ input[type='checkbox']::before {
451
+ content: '';
452
+ height: 0.85rem;
453
+ width: 0.85rem;
454
+ background: var(--bgl-primary);
455
+ display: block;
456
+ text-align: center;
457
+ border-radius: 100%;
458
+ opacity: 0;
459
+ transition: all 200ms ease;
460
+ transform: scale(1);
461
+ position: absolute;
462
+ }
463
+
464
+ input[type='checkbox']:hover::before {
465
+ opacity: 0.2;
466
+ transform: scale(2);
467
+ }
468
+
469
+ [lang='he'] [dir='ltr'] {
470
+ text-align: right;
471
+ }
472
+
473
+ th input[type='checkbox'] {
474
+ transform: translateY(0.2rem) scale(1.2);
475
+ accent-color: var(--bgl-primary);
476
+ }
477
+
478
+ th::after {
479
+ content: '';
480
+ border-bottom: 1px solid var(--border-color);
481
+ position: absolute;
482
+ left: 0;
483
+ right: 0;
484
+ bottom: -1px;
485
+ }
486
+
487
+ tr {
488
+ border-bottom: 1px solid var(--border-color);
489
+ cursor: pointer;
490
+ align-items: center;
491
+ }
492
+
493
+ td,
494
+ th {
495
+ white-space: nowrap;
496
+ padding: 0.75rem 0.65rem;
497
+ transition: var(--bgl-transition);
498
+ line-height: 1;
499
+ }
500
+
501
+ tbody tr {
502
+ font-size: 0.88em;
503
+ height: v-bind(computedItemHiehgt);
504
+ transition: all 200ms ease;
505
+ }
506
+
507
+ tbody tr:hover {
508
+ background: var(--bgl-gray-light);
509
+ }
324
510
  </style>
@@ -1,6 +1,4 @@
1
1
  <script lang="ts" setup>
2
- import { defineExpose } from 'vue'
3
-
4
2
  const fieldSet = $ref<HTMLFieldSetElement | null>(null)
5
3
  const isDirty = $ref(false)
6
4
  const validateForm = () => fieldSet?.reportValidity()
@@ -1,8 +1,8 @@
1
1
  <script lang="ts" setup>
2
+ import { BglField, type BglFormSchemaT, Title, useModal } from '@bagelink/vue'
2
3
  import { useSlots, watch } from 'vue'
3
- import { BglField, Title, useModal } from '@bagelink/vue'
4
4
 
5
- import type { BglFormSchemaT } from '@bagelink/vue'
5
+ type FormStatus = 'idle' | 'loading' | 'success' | 'error'
6
6
 
7
7
  const props = withDefaults(
8
8
  defineProps<{
@@ -46,8 +46,6 @@ const validateForm = () => form?.reportValidity()
46
46
 
47
47
  const clearForm = () => (data = {})
48
48
 
49
- type FormStatus = 'idle' | 'loading' | 'success' | 'error'
50
-
51
49
  watch(
52
50
  () => props.status,
53
51
  (status) => {
@@ -1,27 +1,34 @@
1
1
  <script setup lang="ts">
2
- const props = defineProps<{
2
+ import { onMounted } from 'vue'
3
+
4
+ const props = withDefaults(defineProps<{
3
5
  label?: string
4
6
  id?: string
5
7
  title?: string
6
8
  small?: boolean
7
9
  required?: boolean
8
- }>()
10
+ defaultValue?: boolean
11
+ }>(), { defaultValue: false })
12
+
13
+ const inputId = $computed(() => props.id || Math.random().toString(36).substring(7))
9
14
 
10
- const inputId = $ref(props.id || Math.random().toString(36).substring(7))
15
+ const checked = defineModel<boolean | undefined>('modelValue', { default: undefined })
11
16
 
12
- const checked = defineModel<boolean>('modelValue', { default: false })
17
+ onMounted(() => {
18
+ if (checked.value === undefined) checked.value = props.defaultValue
19
+ })
13
20
  </script>
14
21
 
15
22
  <template>
16
23
  <div
17
24
  class="bagel-input bgl-checkbox align-items-center ps-025"
18
- :title="title"
25
+ :title
19
26
  :class="{ small }"
20
27
  >
21
28
  <input
22
29
  :id="inputId"
23
30
  v-model="checked"
24
- :required="required"
31
+ :required
25
32
  type="checkbox"
26
33
  class="me-05"
27
34
  >
@@ -5,6 +5,7 @@ import { onMounted } from 'vue'
5
5
 
6
6
  const props = withDefaults(
7
7
  defineProps<{
8
+ required?: boolean
8
9
  label?: string
9
10
  editMode?: boolean
10
11
  small?: boolean
@@ -61,6 +62,7 @@ onMounted(() => {
61
62
  <VDatepicker
62
63
  ref="datePicker"
63
64
  v-model="date"
65
+ :required="required"
64
66
  :auto-apply="true"
65
67
  :enable-time-picker="enableTime"
66
68
  :allowed-dates="allowedDates"