@bagelink/vue 0.0.564 → 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 +608 -195
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.mjs +609 -196
  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 +3 -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 +283 -104
  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 +22 -2
@@ -5,8 +5,10 @@ import {
5
5
  MaterialIcon,
6
6
  isDate,
7
7
  keyToLabel,
8
+ useBglSchema,
8
9
  } from '@bagelink/vue'
9
- import { useSlots } from 'vue'
10
+ import { computed, useSlots, watch } from 'vue'
11
+ import { useVirtualList } from '@vueuse/core'
10
12
 
11
13
  const props = defineProps<{
12
14
  selectedItems?: string[]
@@ -14,51 +16,29 @@ const props = defineProps<{
14
16
  schema?: BglFormSchemaT | (() => BglFormSchemaT)
15
17
  showFields?: string[]
16
18
  }>()
19
+
17
20
  const emit = defineEmits(['update:selectedItems', 'orderBy', 'select'])
18
21
  const slots = useSlots()
19
- const loading = $ref(true)
20
-
21
- let selectedItems = $ref<string[]>(props.selectedItems || [])
22
- let selected = $computed({
23
- get: () => selectedItems,
24
- set: (value: string[]) => {
25
- selectedItems = value
26
- emit('update:selectedItems', value)
27
- },
28
- })
22
+ const loading = defineModel('loading', { default: false })
29
23
 
30
- function selectElement(item: Record<string, any>) { emit('select', item) }
24
+ const itemHeight = defineModel('itemHeight', { default: 50 })
25
+ const computedItemHiehgt = $computed(() => `${itemHeight.value}px`)
31
26
 
32
- let sortDirection = $ref('ASC')
33
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))
34
32
 
35
33
  const computedSortField = $computed(() => `_transformed_${sortField}`)
36
34
 
37
- function getFallbackSchema() {
38
- const keys = [
39
- ...new Set((props.data || []).map(Object.keys).flat()),
40
- ] as string[]
41
- const schema: BglFormSchemaT = keys.map(id => ({
42
- id,
43
- label: keyToLabel(id),
44
- }))
45
- return props.showFields
46
- ? schema.filter(f => props.showFields?.includes(f.id as string) ?? !f.id)
47
- : schema
48
- }
49
-
50
- const computedSchema = $computed(() => {
51
- let _schema = props.schema
52
- if (typeof _schema === 'function') {
53
- _schema = _schema()
54
- }
55
- if (_schema) {
56
- return props.showFields
57
- ? _schema.filter(f => props.showFields?.includes(f.id as string))
58
- : _schema
59
- }
60
- return getFallbackSchema()
61
- })
35
+ const computedSchema = $computed(
36
+ () => useBglSchema({
37
+ schema: props.schema,
38
+ showFields: props.showFields,
39
+ data: props.data,
40
+ })
41
+ )
62
42
 
63
43
  function transform(rowData: any) {
64
44
  const obj: Record<string, any> = { ...rowData }
@@ -71,13 +51,13 @@ function transform(rowData: any) {
71
51
 
72
52
  Object.assign(obj, {
73
53
  [`${field.id}`]: fieldData,
74
- [`_transformed_${field.id}`]: newFieldVal
54
+ [`_transformed_${field.id}`]: newFieldVal,
75
55
  })
76
56
  }
77
57
  return obj
78
58
  }
79
59
 
80
- const computedData = $computed(() => {
60
+ const computedData = computed(() => {
81
61
  const dta = [...props.data].map(transform)
82
62
  if (sortField as string) {
83
63
  return dta.sort((a, b) => {
@@ -110,15 +90,6 @@ const computedData = $computed(() => {
110
90
  return dta
111
91
  })
112
92
 
113
- function _unused_SelectAll(event: PointerEvent) {
114
- const value = (event.target as HTMLInputElement).checked
115
- if (!value) {
116
- selected = []
117
- return
118
- }
119
- selected.push(...computedData.map(d => d.id))
120
- }
121
-
122
93
  function sort(fieldname: string) {
123
94
  if (sortField === fieldname) {
124
95
  if (sortDirection === 'ASC') sortDirection = 'DESC'
@@ -129,67 +100,193 @@ function sort(fieldname: string) {
129
100
  }
130
101
  emit('orderBy', `${fieldname} ${sortDirection}`.trim())
131
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
132
160
  </script>
133
161
 
134
162
  <template>
135
- <div class="table-list-wrap h-100">
136
- <table class="infinite-wrapper">
137
- <thead class="row first-row">
138
- <th
139
- v-for="field in computedSchema"
140
- :key="field.id"
141
- class="col"
142
- @click="sort(field?.id || '')"
143
- >
144
- <div class="flex">
145
- {{ field.label || keyToLabel(field.id) }}
146
- <div
147
- class="list-arrows"
148
- :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"
149
186
  >
150
- <MaterialIcon
151
- :class="{ desc: sortDirection === 'DESC' }"
152
- icon="keyboard_arrow_up"
153
- />
154
- </div>
155
- </div>
156
- </th>
157
- </thead>
158
- <!-- eslint-disable-next-line vue/no-unused-refs TODO: Can we remove the unused ref ??? -->
159
- <tbody ref="infinite" class="rows infinite" :class="{ loading }">
160
- <tr
161
- v-for="row in computedData"
162
- :key="row.id"
163
- class="row row-item position-relative"
164
- @click="selectElement(row)"
165
- >
166
- <td
187
+ </th>
188
+ <th
167
189
  v-for="field in computedSchema"
168
- :key="`${field.id}-${row.id}`"
190
+ :key="field.id"
169
191
  class="col"
192
+ @click="sort(field?.id || '')"
193
+ >
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)"
170
215
  >
171
- <slot
172
- v-if="field.id && slots[field.id]"
173
- :name="field.id"
174
- :row
175
- :field
176
- />
177
- <div v-else>
178
- <BglField
179
- class="embedded-field"
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
180
234
  :field
181
- :modelValue="row"
182
- label=""
183
235
  />
184
- </div>
185
- </td>
186
- </tr>
187
- </tbody>
188
- </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>
189
249
  </div>
190
250
  </template>
191
251
 
192
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
+
193
290
  .list-arrows {
194
291
  opacity: 0;
195
292
  }
@@ -217,10 +314,20 @@ table {
217
314
  border-collapse: separate;
218
315
  border-spacing: 0 15px;
219
316
  border-collapse: collapse;
317
+ width: 100%;
220
318
  }
221
319
 
222
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;
223
329
  font-weight: 400;
330
+ text-align: start;
224
331
  }
225
332
 
226
333
  .embedded-field {
@@ -240,12 +347,8 @@ th {
240
347
  top: 0;
241
348
  z-index: 2;
242
349
  background: var(--bgl-white);
243
- /* height: 50px; */
244
350
  vertical-align: bottom;
245
351
  }
246
- .row.first-row input[type=checkbox]{
247
- margin-bottom: 0.7rem;
248
- }
249
352
 
250
353
  .row.first-row::after {
251
354
  content: '';
@@ -268,11 +371,12 @@ th {
268
371
  line-height: 1;
269
372
  align-items: center;
270
373
  }
374
+
271
375
  .col:has(.bagel-input) {
272
376
  padding: 0rem 0.25rem;
273
377
  }
274
378
 
275
- .col > div {
379
+ .col>div {
276
380
  display: flex;
277
381
  gap: 0.5rem;
278
382
  }
@@ -312,14 +416,15 @@ th {
312
416
  }
313
417
 
314
418
  .row-item {
315
- height: 50px;
419
+ height: v-bind(computedItemHiehgt);
316
420
  transition: all 200ms ease;
317
421
  }
318
422
 
319
423
  .row-item:hover {
320
424
  background: var(--bgl-gray-light);
321
425
  }
322
- .row-item input[type=checkbox]{
426
+
427
+ .row-item input[type='checkbox'] {
323
428
  margin-top: 0.45rem !important;
324
429
  accent-color: var(--bgl-primary);
325
430
  }
@@ -328,4 +433,78 @@ th {
328
433
  overflow-y: auto;
329
434
  width: 100%;
330
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
+ }
331
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"