@bagelink/vue 1.12.16 → 1.12.23

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 (35) hide show
  1. package/dist/components/calendar/CalendarTypes.d.ts.map +1 -1
  2. package/dist/components/calendar/Index.vue.d.ts.map +1 -1
  3. package/dist/components/calendar/views/WeekView.vue.d.ts.map +1 -1
  4. package/dist/components/form/inputs/ArrayInput.vue.d.ts.map +1 -1
  5. package/dist/dialog/DialogForm.vue.d.ts.map +1 -1
  6. package/dist/form-flow/FormFlow.vue.d.ts.map +1 -1
  7. package/dist/form-flow/form-flow.d.ts.map +1 -1
  8. package/dist/i18n/index.d.ts.map +1 -1
  9. package/dist/index.cjs +107 -107
  10. package/dist/index.mjs +12048 -11787
  11. package/dist/plugins/bagel.d.ts.map +1 -1
  12. package/dist/style.css +1 -1
  13. package/dist/types/index.d.ts.map +1 -1
  14. package/package.json +2 -2
  15. package/src/components/Pagination.vue +1 -1
  16. package/src/components/calendar/CalendarTypes.ts +11 -0
  17. package/src/components/calendar/Index.vue +25 -4
  18. package/src/components/calendar/views/WeekView.vue +260 -16
  19. package/src/components/form/inputs/ArrayInput.vue +4 -3
  20. package/src/components/form/inputs/RichText/utils/formatting.ts +4 -4
  21. package/src/dialog/DialogForm.vue +7 -9
  22. package/src/form-flow/FormFlow.vue +14 -1
  23. package/src/form-flow/MultiStepForm.vue +1 -1
  24. package/src/form-flow/form-flow.ts +2 -2
  25. package/src/i18n/locales/en.json +47 -0
  26. package/src/i18n/locales/es.json +1 -0
  27. package/src/i18n/locales/fr.json +1 -0
  28. package/src/i18n/locales/he.json +47 -0
  29. package/src/i18n/locales/it.json +1 -0
  30. package/src/i18n/locales/ru.json +1 -0
  31. package/src/plugins/bagel.ts +2 -0
  32. package/src/plugins/useModal.ts +2 -2
  33. package/src/styles/buttons.css +6 -0
  34. package/src/styles/text.css +1759 -1755
  35. package/src/types/index.ts +4 -3
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/vue",
3
3
  "type": "module",
4
- "version": "1.12.16",
4
+ "version": "1.12.23",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
@@ -90,7 +90,7 @@
90
90
  "signature_pad": "^5.0.9",
91
91
  "vue-i18n": "^11.2.8",
92
92
  "vue-toastification": "^2.0.0-rc.5",
93
- "@bagelink/utils": "1.12.16"
93
+ "@bagelink/utils": "1.12.23"
94
94
  },
95
95
  "scripts": {
96
96
  "dev": "tsx watch src/index.ts",
@@ -231,7 +231,7 @@ const renderPageButtons = computed(() => {
231
231
  height: 30px;
232
232
  background-color: var(--bgl-primary);
233
233
  transition: all 0.3s ease;
234
- z-index: -1;
234
+ /* z-index: -1; */
235
235
  }
236
236
 
237
237
  .selected {
@@ -14,11 +14,22 @@ export type CalendarView = 'Week' | 'Month' | 'Day' | 'Agenda'
14
14
 
15
15
  export type WeekStart = 'Sunday' | 'Monday'
16
16
 
17
+ export interface AvailabilitySlot {
18
+ start_time: Date
19
+ end_time: Date
20
+ }
21
+
17
22
  export interface CalendarProps {
18
23
  events?: CalendarEvent[]
19
24
  startDate?: Date
20
25
  view?: CalendarView
21
26
  weekStart?: WeekStart
27
+ /** When true, renders availability blocks on the week grid */
28
+ availabilityMode?: boolean
29
+ /** When true, allows drag-to-paint editing of availability (requires availabilityMode) */
30
+ availabilityEditable?: boolean
31
+ /** Existing availability slots to display as painted blocks */
32
+ availabilitySlots?: AvailabilitySlot[]
22
33
  }
23
34
 
24
35
  export interface CalendarViewState {
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import type { Component } from 'vue'
3
- import type { CalendarEvent, CalendarProps, CalendarView, CalendarViewState } from './CalendarTypes'
3
+ import type { AvailabilitySlot, CalendarEvent, CalendarView, CalendarViewState, WeekStart } from './CalendarTypes'
4
4
  import { timeDelta, Btn, ListItem, Dropdown, formatDate } from '@bagelink/vue'
5
5
  import { ref, computed, onMounted } from 'vue'
6
6
  import CalendarPopover from './CalendarPopover.vue'
@@ -9,11 +9,22 @@ import DayView from './views/DayView.vue'
9
9
  import MonthView from './views/MonthView.vue'
10
10
  import WeekView from './views/WeekView.vue'
11
11
 
12
- const props = withDefaults(defineProps<CalendarProps>(), {
12
+ const props = withDefaults(defineProps<{
13
+ events?: CalendarEvent[]
14
+ startDate?: Date
15
+ view?: CalendarView
16
+ weekStart?: WeekStart
17
+ availabilityMode?: boolean
18
+ availabilityEditable?: boolean
19
+ availabilitySlots?: AvailabilitySlot[]
20
+ }>(), {
13
21
  startDate: () => new Date(),
14
22
  view: 'Week',
15
23
  weekStart: 'Sunday',
16
- events: () => []
24
+ events: () => [],
25
+ availabilityMode: false,
26
+ availabilityEditable: false,
27
+ availabilitySlots: () => []
17
28
  })
18
29
 
19
30
  const emit = defineEmits<{
@@ -24,6 +35,7 @@ const emit = defineEmits<{
24
35
  (e: 'dateChange', changeEvent: CalendarViewState): void
25
36
  (e: 'viewChange', changeEvent: CalendarViewState): void
26
37
  (e: 'ready', changeEvent: CalendarViewState): void
38
+ (e: 'availabilityChange', slots: AvailabilitySlot[]): void
27
39
  }>()
28
40
 
29
41
  const currentDate = ref(new Date(props.startDate))
@@ -117,7 +129,14 @@ defineExpose({
117
129
  closePopover
118
130
  })
119
131
 
120
- onMounted(() => { emit('ready', state.value) })
132
+ onMounted(() => {
133
+ console.log('[Calendar] props:', {
134
+ availabilityMode: props.availabilityMode,
135
+ availabilityEditable: props.availabilityEditable,
136
+ availabilitySlots: props.availabilitySlots?.length
137
+ })
138
+ emit('ready', state.value)
139
+ })
121
140
  </script>
122
141
 
123
142
  <template>
@@ -144,8 +163,10 @@ onMounted(() => { emit('ready', state.value) })
144
163
  </div>
145
164
  <component
146
165
  :is="views[currentView]" :events="events" :start-date="currentDate" :week-start="weekStart"
166
+ :availability-mode="props.availabilityMode" :availability-editable="props.availabilityEditable" :availability-slots="props.availabilitySlots"
147
167
  @event-click="handleEventClick" @event-create="handleEventCreate" @event-update="handleEventUpdate"
148
168
  @event-delete="handleEventDelete" @date-change="handleDateChange" @open-popover="openPopover"
169
+ @availability-change="(slots: AvailabilitySlot[]) => emit('availabilityChange', slots)"
149
170
  >
150
171
  <template #eventContent="{ event, close }">
151
172
  <slot name="eventContent" :event="event" :close="close" />
@@ -1,8 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  import type { SetupContext, ComponentPublicInstance } from 'vue'
3
- import type { CalendarEvent, WeekStart } from '../CalendarTypes'
3
+ import type { AvailabilitySlot, CalendarEvent, WeekStart } from '../CalendarTypes'
4
4
  import { formatDate } from '@bagelink/vue'
5
- import { ref, computed, onMounted, onUnmounted, useSlots, nextTick } from 'vue'
5
+ import { ref, computed, onMounted, onUnmounted, useSlots, nextTick, watch } from 'vue'
6
6
 
7
7
  interface WeekViewEvent extends CalendarEvent {
8
8
  top: number
@@ -17,16 +17,23 @@ const props = withDefaults(defineProps<{
17
17
  events?: CalendarEvent[]
18
18
  startDate?: Date
19
19
  weekStart?: WeekStart
20
+ availabilityMode?: boolean
21
+ availabilityEditable?: boolean
22
+ availabilitySlots?: AvailabilitySlot[]
20
23
  }>(), {
21
24
  startDate: () => new Date(),
22
25
  weekStart: 'Sunday',
23
- events: () => []
26
+ events: () => [],
27
+ availabilityMode: false,
28
+ availabilityEditable: false,
29
+ availabilitySlots: () => []
24
30
  })
25
31
 
26
32
  const emit = defineEmits<{
27
33
  (e: 'eventClick', event: CalendarEvent): void
28
34
  (e: 'eventCreate', event: { start_time: Date, end_time: Date }): void
29
35
  (e: 'openPopover', event: CalendarEvent, position?: { top: number, left: number }): void
36
+ (e: 'availabilityChange', slots: AvailabilitySlot[]): void
30
37
  }>()
31
38
 
32
39
  const slots: SetupContext['slots'] = useSlots()
@@ -52,16 +59,24 @@ const dragState = ref({
52
59
  endTime: undefined as Date | undefined
53
60
  })
54
61
 
55
- // Time indicators
56
- const currentTimeTop = ref(0)
57
- const isToday = ref(false)
58
- const currentTimeInterval = ref<any>()
62
+ // ===== AVAILABILITY MODE =====
63
+ const AVAILABILITY_SNAP_MINUTES = 30
64
+ const localAvailability = ref<AvailabilitySlot[]>([])
65
+ const availDragState = ref({
66
+ isDragging: false,
67
+ mode: 'add' as 'add' | 'remove',
68
+ dayIndex: -1,
69
+ startMinute: 0,
70
+ currentMinute: 0
71
+ })
59
72
 
60
- const timeSlotsContainer = ref<HTMLElement>()
61
- const calendarGrid = ref<HTMLElement>()
62
- const dayColumns = ref<HTMLElement[]>([])
63
- const dayColumnsContainer = ref<HTMLElement>()
64
- const scrollableContainer = ref<HTMLElement>()
73
+ // Sync external availability slots to local state
74
+ watch(() => props.availabilitySlots, (slots) => {
75
+ localAvailability.value = slots.map(s => ({
76
+ start_time: new Date(s.start_time),
77
+ end_time: new Date(s.end_time)
78
+ }))
79
+ }, { immediate: true })
65
80
 
66
81
  // Calculate week days based on start date and week start preference
67
82
  const weekDays = computed(() => {
@@ -83,19 +98,197 @@ const weekDays = computed(() => {
83
98
  return days
84
99
  })
85
100
 
101
+ function snapMinutes(minutes: number): number {
102
+ return Math.round(minutes / AVAILABILITY_SNAP_MINUTES) * AVAILABILITY_SNAP_MINUTES
103
+ }
104
+
105
+ function getAvailabilitySlotsForDay(day: Date) {
106
+ return localAvailability.value.filter((slot) => {
107
+ const slotDate = new Date(slot.start_time)
108
+ return slotDate.getFullYear() === day.getFullYear()
109
+ && slotDate.getMonth() === day.getMonth()
110
+ && slotDate.getDate() === day.getDate()
111
+ }).map((slot) => {
112
+ const startMinutes = new Date(slot.start_time).getHours() * 60 + new Date(slot.start_time).getMinutes()
113
+ const endMinutes = new Date(slot.end_time).getHours() * 60 + new Date(slot.end_time).getMinutes()
114
+ return {
115
+ top: (startMinutes / slotDuration) * slotHeight,
116
+ height: ((endMinutes - startMinutes) / slotDuration) * slotHeight,
117
+ slot
118
+ }
119
+ })
120
+ }
121
+
122
+ function availabilityPreviewForDay(dayIndex: number) {
123
+ if (!availDragState.value.isDragging || availDragState.value.dayIndex !== dayIndex) return null
124
+ const minM = Math.min(availDragState.value.startMinute, availDragState.value.currentMinute)
125
+ const maxM = Math.max(availDragState.value.startMinute, availDragState.value.currentMinute)
126
+ return {
127
+ top: (minM / slotDuration) * slotHeight,
128
+ height: ((maxM - minM) / slotDuration) * slotHeight,
129
+ mode: availDragState.value.mode
130
+ }
131
+ }
132
+
133
+ function isMinuteInAvailability(day: Date, minute: number): boolean {
134
+ return localAvailability.value.some((slot) => {
135
+ const slotDate = new Date(slot.start_time)
136
+ if (slotDate.getFullYear() !== day.getFullYear()
137
+ || slotDate.getMonth() !== day.getMonth()
138
+ || slotDate.getDate() !== day.getDate()) { return false
139
+ }
140
+ const startM = slotDate.getHours() * 60 + slotDate.getMinutes()
141
+ const endM = new Date(slot.end_time).getHours() * 60 + new Date(slot.end_time).getMinutes()
142
+ return minute >= startM && minute < endM
143
+ })
144
+ }
145
+
146
+ function handleAvailMouseDown(e: MouseEvent, dayIndex: number) {
147
+ if (!props.availabilityEditable) return
148
+ e.preventDefault()
149
+ e.stopPropagation()
150
+ const day = weekDays.value[dayIndex]
151
+ const minutes = snapMinutes(getMinutesFromY(e.clientY))
152
+ const isExisting = isMinuteInAvailability(day, minutes)
153
+
154
+ availDragState.value = {
155
+ isDragging: true,
156
+ mode: isExisting ? 'remove' : 'add',
157
+ dayIndex,
158
+ startMinute: minutes,
159
+ currentMinute: minutes + AVAILABILITY_SNAP_MINUTES
160
+ }
161
+
162
+ document.addEventListener('mousemove', handleAvailMouseMove)
163
+ document.addEventListener('mouseup', handleAvailMouseUp)
164
+ }
165
+
166
+ function handleAvailMouseMove(e: MouseEvent) {
167
+ if (!availDragState.value.isDragging) return
168
+ const minutes = snapMinutes(getMinutesFromY(e.clientY))
169
+ availDragState.value.currentMinute = minutes
170
+ }
171
+
172
+ function handleAvailMouseUp() {
173
+ if (!availDragState.value.isDragging) return
174
+ const { dayIndex, startMinute, currentMinute, mode } = availDragState.value
175
+ const day = weekDays.value[dayIndex]
176
+ const minM = Math.min(startMinute, currentMinute)
177
+ const maxM = Math.max(startMinute, currentMinute)
178
+
179
+ if (maxM - minM < AVAILABILITY_SNAP_MINUTES) {
180
+ availDragState.value.isDragging = false
181
+ document.removeEventListener('mousemove', handleAvailMouseMove)
182
+ document.removeEventListener('mouseup', handleAvailMouseUp)
183
+ return
184
+ }
185
+
186
+ const newStart = new Date(day)
187
+ newStart.setHours(Math.floor(minM / 60), minM % 60, 0, 0)
188
+ const newEnd = new Date(day)
189
+ newEnd.setHours(Math.floor(maxM / 60), maxM % 60, 0, 0)
190
+
191
+ if (mode === 'add') {
192
+ // Add and merge overlapping
193
+ const otherDays = localAvailability.value.filter((s) => {
194
+ const d = new Date(s.start_time)
195
+ return d.getFullYear() !== day.getFullYear()
196
+ || d.getMonth() !== day.getMonth()
197
+ || d.getDate() !== day.getDate()
198
+ })
199
+ const sameDaySlots = localAvailability.value.filter((s) => {
200
+ const d = new Date(s.start_time)
201
+ return d.getFullYear() === day.getFullYear()
202
+ && d.getMonth() === day.getMonth()
203
+ && d.getDate() === day.getDate()
204
+ })
205
+ sameDaySlots.push({ start_time: newStart, end_time: newEnd })
206
+ // Sort and merge
207
+ sameDaySlots.sort((a, b) => new Date(a.start_time).getTime() - new Date(b.start_time).getTime())
208
+ const merged: AvailabilitySlot[] = []
209
+ for (const s of sameDaySlots) {
210
+ if (merged.length === 0) {
211
+ merged.push({ start_time: new Date(s.start_time), end_time: new Date(s.end_time) })
212
+ } else {
213
+ const last = merged[merged.length - 1]
214
+ if (new Date(s.start_time).getTime() <= new Date(last.end_time).getTime()) {
215
+ last.end_time = new Date(Math.max(new Date(last.end_time).getTime(), new Date(s.end_time).getTime()))
216
+ } else {
217
+ merged.push({ start_time: new Date(s.start_time), end_time: new Date(s.end_time) })
218
+ }
219
+ }
220
+ }
221
+ localAvailability.value = [...otherDays, ...merged]
222
+ } else {
223
+ // Remove: subtract the range from existing slots
224
+ const result: AvailabilitySlot[] = []
225
+ for (const s of localAvailability.value) {
226
+ const d = new Date(s.start_time)
227
+ if (d.getFullYear() !== day.getFullYear()
228
+ || d.getMonth() !== day.getMonth()
229
+ || d.getDate() !== day.getDate()) {
230
+ result.push(s)
231
+ continue
232
+ }
233
+ const sStart = new Date(s.start_time).getTime()
234
+ const sEnd = new Date(s.end_time).getTime()
235
+ const rStart = newStart.getTime()
236
+ const rEnd = newEnd.getTime()
237
+
238
+ // No overlap
239
+ if (sEnd <= rStart || sStart >= rEnd) {
240
+ result.push(s)
241
+ continue
242
+ }
243
+ // Left remainder
244
+ if (sStart < rStart) {
245
+ result.push({ start_time: new Date(sStart), end_time: new Date(rStart) })
246
+ }
247
+ // Right remainder
248
+ if (sEnd > rEnd) {
249
+ result.push({ start_time: new Date(rEnd), end_time: new Date(sEnd) })
250
+ }
251
+ }
252
+ localAvailability.value = result
253
+ }
254
+
255
+ emit('availabilityChange', localAvailability.value)
256
+ availDragState.value.isDragging = false
257
+ document.removeEventListener('mousemove', handleAvailMouseMove)
258
+ document.removeEventListener('mouseup', handleAvailMouseUp)
259
+ }
260
+
261
+ // Time indicators
262
+ const currentTimeTop = ref(0)
263
+ const isToday = ref(false)
264
+ const currentTimeInterval = ref<any>()
265
+
266
+ const timeSlotsContainer = ref<HTMLElement>()
267
+ const calendarGrid = ref<HTMLElement>()
268
+ const dayColumns = ref<HTMLElement[]>([])
269
+ const dayColumnsContainer = ref<HTMLElement>()
270
+ const scrollableContainer = ref<HTMLElement>()
271
+
272
+ function getMinutesFromY(clientY: number): number {
273
+ if (!dayColumnsContainer.value) return 0
274
+ const rect = dayColumnsContainer.value.getBoundingClientRect()
275
+ const relativeY = clientY - rect.top
276
+ return Math.max(0, Math.min((relativeY / slotHeight) * slotDuration, 24 * 60))
277
+ }
278
+
86
279
  // Generate hourly time slots
87
280
  const timeSlots = computed(() => {
88
- const slots = []
281
+ const items = []
89
282
  for (let hour = timeRange.start; hour < timeRange.end; hour++) {
90
283
  for (let minute = 0; minute < 60; minute += slotDuration) {
91
- slots.push({
284
+ items.push({
92
285
  hour,
93
286
  minute,
94
287
  time: `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`
95
288
  })
96
289
  }
97
290
  }
98
- return slots
291
+ return items
99
292
  })
100
293
 
101
294
  // Calculate drag preview positioning using the same formula as events
@@ -410,6 +603,11 @@ function scrollToTime(time: number) {
410
603
 
411
604
  // Lifecycle hooks
412
605
  onMounted(() => {
606
+ console.log('[WeekView] mounted with props:', {
607
+ availabilityMode: props.availabilityMode,
608
+ availabilityEditable: props.availabilityEditable,
609
+ availabilitySlotsCount: props.availabilitySlots?.length
610
+ })
413
611
  updateCurrentTimeIndicator()
414
612
  currentTimeInterval.value = setInterval(updateCurrentTimeIndicator, 60000)
415
613
 
@@ -420,6 +618,8 @@ onMounted(() => {
420
618
  onUnmounted(() => {
421
619
  document.removeEventListener('mousemove', handleMouseMove)
422
620
  document.removeEventListener('mouseup', handleMouseUp)
621
+ document.removeEventListener('mousemove', handleAvailMouseMove)
622
+ document.removeEventListener('mouseup', handleAvailMouseUp)
423
623
 
424
624
  if (currentTimeInterval.value) {
425
625
  clearInterval(currentTimeInterval.value)
@@ -466,8 +666,29 @@ onUnmounted(() => {
466
666
 
467
667
  <div
468
668
  v-for="(day, index) in weekDays" :key="day.toISOString()" :ref="el => setDayColumnRef(el, index)"
469
- class="day-column top bottom border-start relative" @mousedown="handleMouseDown($event, day)"
669
+ class="day-column top bottom border-start relative"
670
+ @mousedown="props.availabilityEditable ? handleAvailMouseDown($event, index) : handleMouseDown($event, day)"
470
671
  >
672
+ <!-- Availability painted blocks (behind events) -->
673
+ <template v-if="props.availabilityMode">
674
+ <div
675
+ v-for="(block, bi) in getAvailabilitySlotsForDay(day)" :key="`avail-${bi}`"
676
+ class="availability-block absolute start end"
677
+ :class="{ 'availability-editable': props.availabilityEditable }"
678
+ :style="{ top: `${block.top}px`, height: `${block.height}px` }"
679
+ />
680
+ <!-- Availability drag preview -->
681
+ <div
682
+ v-if="availabilityPreviewForDay(index)"
683
+ class="availability-preview absolute start end pointer-events-none"
684
+ :class="{ 'avail-remove': availabilityPreviewForDay(index)!.mode === 'remove' }"
685
+ :style="{
686
+ top: `${availabilityPreviewForDay(index)!.top}px`,
687
+ height: `${availabilityPreviewForDay(index)!.height}px`,
688
+ }"
689
+ />
690
+ </template>
691
+
471
692
  <template
472
693
  v-for="event in processedEvents.filter(e =>
473
694
  new Date(e.start_time).toDateString() === day.toDateString())" :key="event.id"
@@ -587,6 +808,29 @@ onUnmounted(() => {
587
808
  justify-content: center; */
588
809
  }
589
810
 
811
+ .availability-block {
812
+ background-color: rgba(76, 175, 80, 0.18);
813
+ border-inline-start: 3px solid rgba(76, 175, 80, 0.5);
814
+ z-index: 0;
815
+ pointer-events: none;
816
+ }
817
+
818
+ .availability-block.availability-editable {
819
+ background-color: rgba(33, 150, 243, 0.18);
820
+ border-inline-start: 3px solid rgba(33, 150, 243, 0.5);
821
+ }
822
+
823
+ .availability-preview {
824
+ background-color: rgba(76, 175, 80, 0.25);
825
+ border: 1px dashed rgba(76, 175, 80, 0.6);
826
+ z-index: 0;
827
+ }
828
+
829
+ .availability-preview.avail-remove {
830
+ background-color: rgba(244, 67, 54, 0.15);
831
+ border-color: rgba(244, 67, 54, 0.5);
832
+ }
833
+
590
834
  @media (max-width: 910px) {
591
835
  .weekGrid {
592
836
  display: grid;
@@ -136,7 +136,7 @@ function getItemRef(i: number) {
136
136
  {{ helpText }}
137
137
  </p>
138
138
  <template v-if="!collapsible">
139
- <div v-for="(_, i) in items" :key="i" class="array-input-row grid align-items-center">
139
+ <div v-for="(_, i) in items" :key="i" class="array-input-row grid align-items-center pb-1">
140
140
  <slot :item="getItemRef(i)" :index="i" />
141
141
  <div v-if="allowDelete" class="ms-075 my-1 border-start h-100p ps-025">
142
142
  <Btn
@@ -149,13 +149,14 @@ function getItemRef(i: number) {
149
149
  <Draggable v-if="collapsible" :model-value="items" handle=".grab" :disabled="!allowReorder" class="grid gap-05 mb-05" @end="onDraggableEnd">
150
150
  <template #default="{ index: i }">
151
151
  <div class="border radius-1 overflow-hidden">
152
- <div class="grid gap-05 grid-array-line align-items-center pe-025 hover">
152
+ <div class="grid gap-0 grid-array-line align-items-center pe-025 hover">
153
153
  <Btn
154
154
  v-if="allowReorder" v-tooltip="resolveI18n('Drag to reorder')" flat thin icon="drag_indicator"
155
155
  class="grab"
156
156
  />
157
+ <div v-else />
157
158
  <Btn
158
- full-width align-txt="start" class="px-05 bg-transparent color-inherit" :ripple="false"
159
+ full-width align-txt="start" class="px-025 bg-transparent color-inherit" :ripple="false"
159
160
  :icon="minimizedItems[i] ? 'expand_more' : 'expand_less'" @click="toggleItem(i)"
160
161
  >
161
162
  <p class="ellipsis-1">
@@ -178,10 +178,10 @@ export function formatting(state: EditorState) {
178
178
 
179
179
  const clear = () => {
180
180
  console.log('[Clear Format] Starting clear format process', state)
181
- console.assert(state, '[Clear Format] State must exist')
182
- console.assert(state.doc, '[Clear Format] Document must exist')
183
- console.assert(state.range, '[Clear Format] Range must exist')
184
- console.assert(state.selection, '[Clear Format] Selection must exist')
181
+ console.assert(!!state, '[Clear Format] State must exist')
182
+ console.assert(!!state.doc, '[Clear Format] Document must exist')
183
+ console.assert(!!state.range, '[Clear Format] Range must exist')
184
+ console.assert(!!state.selection, '[Clear Format] Selection must exist')
185
185
 
186
186
  if (!state.doc || !state.range || !state.selection) {
187
187
  console.log('[Clear Format] No document or selection')
@@ -108,15 +108,13 @@ function handleClose() {
108
108
  :open="open" :title="title" :width="width" :position="position" :dismissable="dismissable"
109
109
  @update:open="$emit('update:open', $event)" @close="handleClose"
110
110
  >
111
- <form @submit.prevent="handleSubmit">
112
- <!-- Error message -->
113
- <div v-if="error" class="dialog-form-error bg-red-30 color-red radius-1 mb-1 txt14 px-1 py-075">
114
- {{ error }}
115
- </div>
116
-
117
- <!-- Form fields -->
118
- <FormFlow ref="formFlowRef" v-model="formData" :schema="schema" />
119
- </form>
111
+ <!-- Error message -->
112
+ <div v-if="error" class="dialog-form-error bg-red-30 color-red radius-1 mb-1 txt14 px-1 py-075">
113
+ {{ error }}
114
+ </div>
115
+
116
+ <!-- Form fields -->
117
+ <FormFlow ref="formFlowRef" v-model="formData" :schema="schema" @submit="handleSubmit" />
120
118
 
121
119
  <template #footer>
122
120
  <!-- Delete button (left side) -->
@@ -348,7 +348,7 @@ defineExpose({
348
348
  </script>
349
349
 
350
350
  <template>
351
- <form ref="formElement" :class="schema._class" @submit="handleSubmit">
351
+ <form ref="formElement" class="grid-form" :class="schema._class" @submit="handleSubmit">
352
352
  <template v-if="formState === 'success'">
353
353
  <slot name="success" :form-data="formData" />
354
354
  </template>
@@ -376,6 +376,19 @@ defineExpose({
376
376
  </ArrayInput>
377
377
  </template>
378
378
 
379
+ <!-- Array field with primitive string item type e.g. $.array('Label', 'text') -->
380
+ <template v-else-if="field._type === 'array' && typeof field._config.schema === 'string'">
381
+ <ArrayInput v-bind="getFieldProps(field, key)">
382
+ <template #default="{ item }">
383
+ <component
384
+ :is="getFieldComponent(field._config.schema as string)"
385
+ :model-value="(item.value as string | number | undefined)"
386
+ @update:model-value="(val: any) => { item.value = val }"
387
+ />
388
+ </template>
389
+ </ArrayInput>
390
+ </template>
391
+
379
392
  <!-- Array field with custom component -->
380
393
  <template v-else-if="field._type === 'array' && field._config.schema">
381
394
  <ArrayInput v-bind="getFieldProps(field, key)">
@@ -360,7 +360,7 @@ defineExpose({
360
360
  <!-- Default: Use FormFlow -->
361
361
  <FormFlow v-model="formData" :schema="currentStepSchema" :components="components" :errors="stepErrors">
362
362
  <!-- Pass through field slots -->
363
- <template v-for="(_, name) in $slots" #[name]="slotProps">
363
+ <template v-for="(_, name) in ($slots as Record<string, unknown>)" #[name]="slotProps">
364
364
  <slot :name="name" v-bind="slotProps" />
365
365
  </template>
366
366
  </FormFlow>
@@ -285,8 +285,8 @@ export const $ = {
285
285
  },
286
286
 
287
287
  richtext(
288
- labelOrConfig?: string | (BaseFieldConfig & { autoheight?: boolean, basic?: boolean }),
289
- config?: BaseFieldConfig & { autoheight?: boolean, basic?: boolean }
288
+ labelOrConfig?: string | (BaseFieldConfig & { autoheight?: boolean, basic?: boolean, simple?: boolean }),
289
+ config?: BaseFieldConfig & { autoheight?: boolean, basic?: boolean, simple?: boolean }
290
290
  ): FieldBuilder<string> {
291
291
  return new Field('richtext', parseArgs(labelOrConfig, config))
292
292
  },
@@ -182,6 +182,7 @@
182
182
  "closeEditor": "Close Editor (ESC)",
183
183
  "blockNotEditable": "This block's content comes from a data source",
184
184
  "blockNotEditableDesc": "and cannot be edited here.",
185
+ "dataSourceHint": "Some content in this block may come from a data source. To change it, edit the data directly.",
185
186
  "screenTooSmall": "The screen is too small for this editor. Try reducing zoom or using a larger screen.",
186
187
  "cancel": "Cancel",
187
188
  "publish": "Publish",
@@ -210,6 +211,52 @@
210
211
  "production": "Production",
211
212
  "publishFailed": "Publish failed"
212
213
  },
214
+ "collection": {
215
+ "loadError": "Loading failed",
216
+ "newItemTitle": "New item — {label}",
217
+ "createSubmit": "Create",
218
+ "editTitle": "Edit",
219
+ "saveSubmit": "Save",
220
+ "importTitle": "Import JSON — {label}",
221
+ "importSubmit": "Import",
222
+ "importJsonLabel": "JSON content",
223
+ "importJsonHint": "Array of objects [ {...}, {...} ] or a single object",
224
+ "importSummary": "{imported} imported · {failed} failed",
225
+ "deleteItemTitle": "Delete item",
226
+ "deleteItemMessage": "Delete this item?",
227
+ "deleteConfirm": "Delete",
228
+ "batchDeleteTitle": "Batch delete",
229
+ "batchDeleteMessage": "Delete {count} items?",
230
+ "batchDeleteSummary": "{deleted} deleted · {failed} failed",
231
+ "batchUpdateTitle": "Batch update — {count} items",
232
+ "updateAllSubmit": "Update all",
233
+ "batchUpdateSummary": "Updated · {failed} failed",
234
+ "selectedCount": "{count} selected",
235
+ "updateBtn": "Update",
236
+ "deleteBtn": "Delete",
237
+ "searchPlaceholder": "Search...",
238
+ "refreshTooltip": "Refresh",
239
+ "importTooltip": "Import JSON",
240
+ "newItemBtn": "New item",
241
+ "deleteTooltip": "Delete",
242
+ "reorderTooltip": "Reorder",
243
+ "reorderSaving": "Saving order..."
244
+ },
245
+ "datastores": {
246
+ "fieldName": "Name",
247
+ "fieldStore": "Store",
248
+ "fieldStoreHint": "e.g.: default",
249
+ "fieldCollection": "Collection",
250
+ "fieldCollectionHint": "e.g.: my_collection",
251
+ "fieldDescription": "Description",
252
+ "createTitle": "Create new Datastore",
253
+ "createSubmit": "Create",
254
+ "editTitle": "Edit {name}",
255
+ "saveSubmit": "Save",
256
+ "addBtn": "Add Datastore",
257
+ "empty": "No Datastores yet",
258
+ "deleteConfirm": "Delete \"{name}\"?"
259
+ },
213
260
  "admin": {
214
261
  "content": "Content",
215
262
  "blox": "Blox",