@cahyo-dimas/freeday 1.54.0 → 2.0.0

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 (143) hide show
  1. package/CHANGELOG.md +625 -545
  2. package/COMPONENTS.md +141 -138
  3. package/README.id.md +46 -45
  4. package/README.md +46 -43
  5. package/USAGE.md +34 -34
  6. package/adapters/blazor/FdyAppShell.razor +2 -2
  7. package/adapters/blazor/FdyAppShell.razor.cs +6 -6
  8. package/adapters/blazor/FdyCfl.razor.cs +1 -1
  9. package/adapters/blazor/FdyChart.razor +1 -1
  10. package/adapters/blazor/FdyChart.razor.cs +3 -3
  11. package/adapters/blazor/FdyChartSeries.cs +1 -1
  12. package/adapters/blazor/FdyCombo.razor.cs +2 -2
  13. package/adapters/blazor/FdyModal.razor +1 -1
  14. package/adapters/blazor/FdyTable.razor.cs +7 -7
  15. package/adapters/blazor/FdyTableFilter.razor +1 -1
  16. package/adapters/blazor/FdyTableFooter.razor +1 -1
  17. package/adapters/blazor/FdyTableFooter.razor.cs +4 -4
  18. package/adapters/blazor/Freeday.Blazor.csproj +1 -1
  19. package/adapters/blazor/FreedayComponentBase.cs +1 -1
  20. package/adapters/blazor/TableModel.cs +3 -3
  21. package/adapters/blazor/TableTypes.cs +3 -3
  22. package/adapters/blazor/freeday-blazor.js +5 -5
  23. package/adapters/core/app-shell.d.ts +1 -1
  24. package/adapters/core/app-shell.js +6 -6
  25. package/adapters/core/table-model.d.ts +2 -2
  26. package/adapters/core/table-model.js +8 -8
  27. package/adapters/react/components/FdyAppShell.tsx +5 -5
  28. package/adapters/react/components/FdyAutocomplete.tsx +1 -1
  29. package/adapters/react/components/FdyCascade.tsx +1 -1
  30. package/adapters/react/components/FdyCfl.tsx +11 -11
  31. package/adapters/react/components/FdyDateRange.tsx +2 -2
  32. package/adapters/react/components/FdyDatepicker.tsx +5 -5
  33. package/adapters/react/components/FdyDrawer.tsx +1 -1
  34. package/adapters/react/components/FdyModal.tsx +2 -2
  35. package/adapters/react/components/FdyTable.tsx +7 -7
  36. package/adapters/react/components/FdyTableFilter.tsx +1 -1
  37. package/adapters/react/components/FdyTableFooter.tsx +5 -5
  38. package/adapters/react/index.d.ts +1 -1
  39. package/adapters/react/useFreeday.js +3 -3
  40. package/adapters/vue/components/FdyAppShell.vue +5 -5
  41. package/adapters/vue/components/FdyAutocomplete.vue +1 -1
  42. package/adapters/vue/components/FdyCascade.vue +1 -1
  43. package/adapters/vue/components/FdyCfl.vue +8 -8
  44. package/adapters/vue/components/FdyChart.vue +2 -2
  45. package/adapters/vue/components/FdyCombo.vue +1 -1
  46. package/adapters/vue/components/FdyDateRange.vue +2 -2
  47. package/adapters/vue/components/FdyDatepicker.vue +5 -5
  48. package/adapters/vue/components/FdyDrawer.vue +3 -3
  49. package/adapters/vue/components/FdyModal.vue +3 -3
  50. package/adapters/vue/components/FdyTable.vue +7 -7
  51. package/adapters/vue/components/FdyTableFilter.vue +1 -1
  52. package/adapters/vue/components/FdyTableFooter.vue +5 -5
  53. package/adapters/vue/index.d.ts +2 -2
  54. package/adapters/vue/useFreeday.js +3 -3
  55. package/dist/asset.d.ts +3 -3
  56. package/dist/freeday-app-shell.js +9 -9
  57. package/dist/freeday-autocomplete.js +2 -2
  58. package/dist/freeday-breakpoint.js +1 -1
  59. package/dist/freeday-carousel.js +5 -5
  60. package/dist/freeday-cascade.js +7 -7
  61. package/dist/freeday-cfl.js +9 -9
  62. package/dist/freeday-chart.js +11 -11
  63. package/dist/freeday-chip.js +3 -3
  64. package/dist/freeday-datepicker.js +11 -7
  65. package/dist/freeday-datetime.js +5 -5
  66. package/dist/freeday-drawer.js +1 -1
  67. package/dist/freeday-form.js +14 -14
  68. package/dist/freeday-mask.js +6 -6
  69. package/dist/freeday-menu.js +1 -1
  70. package/dist/freeday-number.js +3 -3
  71. package/dist/freeday-popover.js +3 -3
  72. package/dist/freeday-rating.js +2 -2
  73. package/dist/freeday-select.js +5 -5
  74. package/dist/freeday-slider.js +2 -2
  75. package/dist/freeday-stepper.js +7 -7
  76. package/dist/freeday-table.js +15 -15
  77. package/dist/freeday-tabs.js +1 -1
  78. package/dist/freeday-timepicker.js +2 -2
  79. package/dist/freeday-toast.js +8 -8
  80. package/dist/freeday-tree.js +3 -3
  81. package/dist/freeday-upload.js +25 -25
  82. package/dist/freeday.bundle.css +176 -176
  83. package/dist/freeday.css +170 -170
  84. package/dist/freeday.d.ts +2 -2
  85. package/dist/freeday.js +162 -158
  86. package/dist/freeday.tokens.css +5 -5
  87. package/docs/agent-onboarding.md +41 -41
  88. package/docs/getting-started.md +35 -35
  89. package/docs/integrations.md +18 -18
  90. package/docs/reference-screen.html +14 -14
  91. package/package.json +4 -3
  92. package/src/base.css +6 -6
  93. package/src/components/accordion.css +4 -4
  94. package/src/components/alert.css +1 -1
  95. package/src/components/app-shell.css +10 -10
  96. package/src/components/appbar.css +3 -3
  97. package/src/components/autocomplete.css +1 -1
  98. package/src/components/avatar.css +3 -3
  99. package/src/components/badge.css +5 -5
  100. package/src/components/breadcrumb.css +1 -1
  101. package/src/components/breakpoints.css +4 -4
  102. package/src/components/button.css +13 -13
  103. package/src/components/card.css +8 -8
  104. package/src/components/carousel.css +2 -2
  105. package/src/components/cascade.css +2 -2
  106. package/src/components/cfl.css +4 -4
  107. package/src/components/chart.css +3 -3
  108. package/src/components/chip.css +2 -2
  109. package/src/components/combo.css +3 -3
  110. package/src/components/composition.css +7 -7
  111. package/src/components/datepicker.css +6 -6
  112. package/src/components/datetimepicker.css +1 -1
  113. package/src/components/description-list.css +1 -1
  114. package/src/components/divider.css +1 -1
  115. package/src/components/drawer.css +1 -1
  116. package/src/components/file-upload.css +5 -5
  117. package/src/components/filterbar.css +7 -7
  118. package/src/components/form-grid.css +2 -2
  119. package/src/components/input-group.css +6 -6
  120. package/src/components/input.css +6 -6
  121. package/src/components/kbd.css +1 -1
  122. package/src/components/list.css +6 -6
  123. package/src/components/menu.css +3 -3
  124. package/src/components/modal.css +1 -1
  125. package/src/components/pagination.css +1 -1
  126. package/src/components/progress.css +1 -1
  127. package/src/components/rating.css +2 -2
  128. package/src/components/selection.css +4 -4
  129. package/src/components/skeleton.css +1 -1
  130. package/src/components/slider.css +1 -1
  131. package/src/components/spinner.css +1 -1
  132. package/src/components/states.css +1 -1
  133. package/src/components/stepper.css +2 -2
  134. package/src/components/table.css +15 -15
  135. package/src/components/tabs.css +3 -3
  136. package/src/components/timeline.css +1 -1
  137. package/src/components/timepicker.css +1 -1
  138. package/src/components/toast.css +3 -3
  139. package/src/components/tooltip.css +1 -1
  140. package/src/components/tree.css +2 -2
  141. package/tokens/breakpoints.d.ts +2 -2
  142. package/tokens/breakpoints.mjs +3 -3
  143. package/tokens/tokens.json +1 -1
@@ -9,7 +9,7 @@ import { computed, nextTick, onBeforeUnmount, ref, useId, type ComputedRef, type
9
9
  // that: a real `v-model:Row|null` over a native <dialog> whose rows come from an async
10
10
  // `fetchPage(query, page)`. It re-implements the enhancer's open/close + search + dense
11
11
  // sticky-header results + single-commit-on-click + keyboard, and adds the pieces a server
12
- // picker needs loading/empty/error states, retry, pagination, an out-of-order guard, and
12
+ // picker needs, loading/empty/error states, retry, pagination, an out-of-order guard, and
13
13
  // an optional in-memory cache. The enhancer is NOT mounted here (it would fight Vue's DOM).
14
14
 
15
15
  interface CflColumn {
@@ -23,7 +23,7 @@ interface CflPage {
23
23
  }
24
24
 
25
25
  const props = defineProps<{
26
- /** Single: `Row | null`. With `multiple`, an array `Row[] | null`, where null and [] both mean
26
+ /** Single: `Row | null`. With `multiple`, an array, `Row[] | null`, where null and [] both mean
27
27
  * nothing picked. The enhancer has had `data-fdy-cfl-multiple` all along; this is the typed
28
28
  * wrappers catching up (#019). */
29
29
  modelValue: Row | Row[] | null;
@@ -31,7 +31,7 @@ const props = defineProps<{
31
31
  columns: ReadonlyArray<CflColumn>;
32
32
  display: (row: Row) => string;
33
33
  rowKey: (row: Row) => string;
34
- /** Advisory only the caller's `fetchPage` owns paging; kept for API documentation. */
34
+ /** Advisory only, the caller's `fetchPage` owns paging; kept for API documentation. */
35
35
  pageSize?: number;
36
36
  /** The dialog's heading. Default 'Choose data', matching the Blazor adapter's `Title`. */
37
37
  title?: string;
@@ -108,7 +108,7 @@ const showClear: ComputedRef<boolean> = computed(
108
108
  );
109
109
  const clearLabelText: ComputedRef<string> = computed((): string => props.clearLabel ?? 'Clear selection');
110
110
  const isInvalid: ComputedRef<boolean> = computed((): boolean => props.invalid === true);
111
- /* `display()` takes one row, so in multi the field states HOW MANY naming one of six would be a
111
+ /* `display()` takes one row, so in multi the field states HOW MANY, naming one of six would be a
112
112
  lie, and naming all six does not fit a control that is 22rem wide. */
113
113
  const currentRows: ComputedRef<Row[]> = computed((): Row[] =>
114
114
  Array.isArray(props.modelValue) ? props.modelValue : props.modelValue !== null ? [props.modelValue as Row] : [],
@@ -119,7 +119,7 @@ const displayValue: ComputedRef<string> = computed((): string => {
119
119
  return props.modelValue !== null ? props.display(props.modelValue as Row) : '';
120
120
  });
121
121
  // The results <table> (owner of `resultsId`) only renders in the rows branch, so gate the
122
- // search input's aria refs on rows existing otherwise they'd dangle during loading/empty/error.
122
+ // search input's aria refs on rows existing, otherwise they'd dangle during loading/empty/error.
123
123
  const hasRows: ComputedRef<boolean> = computed((): boolean => rows.value.length > 0);
124
124
  const controlsId: ComputedRef<string | undefined> = computed((): string | undefined =>
125
125
  hasRows.value ? resultsId : undefined,
@@ -156,7 +156,7 @@ async function loadPage(targetPage: number, append: boolean): Promise<void> {
156
156
  try {
157
157
  const cached: CflPage | undefined = cache.get(key);
158
158
  const res: CflPage = cached ?? (await props.fetchPage(q, targetPage));
159
- if (token !== reqToken) return; // stale a newer request has started
159
+ if (token !== reqToken) return; // stale, a newer request has started
160
160
  if (cached === undefined) cache.set(key, res);
161
161
  const copy: Row[] = res.rows.slice(); // never mutate the caller's array
162
162
  rows.value = append ? rows.value.concat(copy) : copy;
@@ -220,7 +220,7 @@ function togglePick(row: Row): void {
220
220
  else picked.value = picked.value.filter((_: Row, i: number): boolean => i !== at);
221
221
  }
222
222
 
223
- /* A click means "tick this" in multi and "this is my answer" in single the whole difference. */
223
+ /* A click means "tick this" in multi and "this is my answer" in single, the whole difference. */
224
224
  function onRowClick(row: Row): void {
225
225
  if (props.multiple === true) togglePick(row);
226
226
  else commit(row);
@@ -238,7 +238,7 @@ function commit(row: Row | null): void {
238
238
  closeDialog();
239
239
  }
240
240
 
241
- /* Unsetting is not "picking nothing" it must not open or close the dialog, and it must leave focus
241
+ /* Unsetting is not "picking nothing", it must not open or close the dialog, and it must leave focus
242
242
  on a control that still exists, so focus returns to the trigger beside it. */
243
243
  function clearValue(): void {
244
244
  picked.value = [];
@@ -4,7 +4,7 @@ import { computed, onBeforeUnmount, onMounted, ref, watch, nextTick, type Comput
4
4
  // A Vue-native wrapper over freeday's zero-dependency chart renderer (freeday-chart.js).
5
5
  // Unlike the input components, charts are not form controls, so re-implementing them in Vue
6
6
  // would only duplicate the SVG maths. Instead this thin wrapper binds the renderer's data-*
7
- // API reactively and re-invokes window.FreedayChart.update() whenever the data changes giving
7
+ // API reactively and re-invokes window.FreedayChart.update() whenever the data changes, giving
8
8
  // reactive, typed charts (a drop-in replacement for hand-rolled Chart.js wrappers) with no
9
9
  // external dependency. Because the renderer paints with CSS var()/--chart-* tokens, charts also
10
10
  // recolour on data-theme change for free. Requires the freeday enhancers (dist/freeday.js) to be
@@ -16,7 +16,7 @@ interface FdyChartSeries {
16
16
  role?: string;
17
17
  }
18
18
 
19
- // Note: no `ariaLabel` prop pass `aria-label` (and any other native attribute) directly; it
19
+ // Note: no `ariaLabel` prop, pass `aria-label` (and any other native attribute) directly; it
20
20
  // falls through onto the root element. `role="img"` is set below. Give every chart an aria-label.
21
21
  const props = defineProps<{
22
22
  type: 'line' | 'area' | 'bar' | 'sparkline' | 'donut';
@@ -220,7 +220,7 @@ onBeforeUnmount((): void => {
220
220
  <ul :id="listboxId" ref="listboxEl" class="fdy-combo__listbox" role="listbox" popover="manual" :hidden="!open">
221
221
  <!-- @mousedown.prevent keeps focus on the button: the option <li> isn't focusable, so a plain
222
222
  mousedown moves focus out of the combo, fires @focusout, and closes the list before the click
223
- lands mouse-select would silently do nothing. Same pattern as FdyDatepicker/FdyAutocomplete. -->
223
+ lands, mouse-select would silently do nothing. Same pattern as FdyDatepicker/FdyAutocomplete. -->
224
224
  <li
225
225
  v-for="(opt, i) in options"
226
226
  :id="optionId(i)"
@@ -13,7 +13,7 @@ import FdyDatepicker from './FdyDatepicker.vue';
13
13
  // two linked FdyDatepickers where the end can't precede the start (start.max = end, end.min =
14
14
  // start). A real `v-model:DateRangeValue` that composes the single-date picker rather than
15
15
  // re-implementing the calendar. The vanilla enhancer's cross-calendar `.in-range` day shading is
16
- // not reproduced (each picker is independent) the min/max linkage is what keeps the range valid.
16
+ // not reproduced (each picker is independent), the min/max linkage is what keeps the range valid.
17
17
 
18
18
  const props = defineProps<{
19
19
  modelValue: DateRangeValue;
@@ -21,7 +21,7 @@ const props = defineProps<{
21
21
  max?: string;
22
22
  locale?: string;
23
23
  disabled?: boolean;
24
- /** Locked/view mode for both pickers focusable, values shown, but can't be opened or changed. */
24
+ /** Locked/view mode for both pickers, focusable, values shown, but can't be opened or changed. */
25
25
  readonly?: boolean;
26
26
  invalid?: boolean;
27
27
  describedby?: string;
@@ -39,7 +39,7 @@ const props = defineProps<{
39
39
  ariaLabelledby?: string;
40
40
  /** Show a clear (×) button in the trigger when a date is set, so an optional date can be unset. Emits `''` via update:modelValue + change. Off by default. */
41
41
  clearable?: boolean;
42
- /** aria-label for the previous-month nav button. Default 'Previous month' override for non-English UIs (month/weekday names already follow `locale`). */
42
+ /** aria-label for the previous-month nav button. Default 'Previous month', override for non-English UIs (month/weekday names already follow `locale`). */
43
43
  prevMonthLabel?: string;
44
44
  /** aria-label for the next-month nav button. Default 'Next month'. */
45
45
  nextMonthLabel?: string;
@@ -145,7 +145,7 @@ const effectiveFocusDate: ComputedRef<Date> = computed((): Date => {
145
145
  return new Date(view.value.getFullYear(), view.value.getMonth(), Math.min(f.getDate(), daysInMonth));
146
146
  });
147
147
 
148
- /* 'days' | 'months' the calendar drills one level up instead of growing furniture beside the title.
148
+ /* 'days' | 'months', the calendar drills one level up instead of growing furniture beside the title.
149
149
  Before this the only pointer route to another month was one click per month: August 2026 to March
150
150
  2022 is fifty-three of them, and the Shift+PageUp jump had no affordance at all. */
151
151
  const mode: Ref<'days' | 'months' | 'years'> = ref('days');
@@ -212,7 +212,7 @@ interface YearCell {
212
212
  focusable: boolean;
213
213
  }
214
214
 
215
- /** A year is unreachable only when EVERY month in it falls outside min/max the same rule
215
+ /** A year is unreachable only when EVERY month in it falls outside min/max, the same rule
216
216
  * isMonthDisabled applies one level down, and for the same reason. */
217
217
  function isYearDisabled(year: number): boolean {
218
218
  if (minDate.value !== null && startOfDay(new Date(year, 11, 31)).getTime() < startOfDay(minDate.value).getTime()) return true;
@@ -411,7 +411,7 @@ function pick(d: Date): void {
411
411
  closePanel(true);
412
412
  }
413
413
 
414
- // Clear (reset to empty). Emits '' parseISO('') is null, so the placeholder shows again.
414
+ // Clear (reset to empty). Emits '', parseISO('') is null, so the placeholder shows again.
415
415
  function clearValue(): void {
416
416
  emit('update:modelValue', '');
417
417
  emit('change', '');
@@ -496,7 +496,7 @@ function onDocPointerDown(e: MouseEvent): void {
496
496
  }
497
497
 
498
498
  // Close when focus leaves the control entirely (e.g. Shift+Tab off the trigger).
499
- /* A null relatedTarget means focus fell to <body> which is what happens when the element the
499
+ /* A null relatedTarget means focus fell to <body>, which is what happens when the element the
500
500
  * user just pressed is REMOVED by the click it triggered (drilling into the month grid replaces
501
501
  * the grid, and with it the cell that had focus). That is not focus leaving the control, and
502
502
  * closing on it made the panel vanish mid-navigation. A pointer that really lands outside is
@@ -2,15 +2,15 @@
2
2
  import { computed, onMounted, useId, watch, type ComputedRef, type Ref, ref } from 'vue';
3
3
 
4
4
  // A controlled Vue wrapper over freeday's `.fdy-drawer` native <dialog> side panel
5
- // (src/components/drawer.css). Same controlled contract and glue as FdyModal showModal()/close()
5
+ // (src/components/drawer.css). Same controlled contract and glue as FdyModal, showModal()/close()
6
6
  // guarded, @cancel.prevent so Esc routes through app state, backdrop-click via `event.target ===
7
- // dialogEl` applied to a drawer that anchors left (default) or right. Native <dialog> supplies the
7
+ // dialogEl`, applied to a drawer that anchors left (default) or right. Native <dialog> supplies the
8
8
  // focus trap, focus restore, top-layer stacking and inert background; `dismissible` (default true)
9
9
  // gates Esc + backdrop dismissal.
10
10
 
11
11
  // dismissible MUST go through withDefaults: Vue's boolean-cast gives an omitted Boolean prop
12
12
  // `false`, not `undefined`, so a bare `props.dismissible !== false` would make an un-annotated
13
- // drawer non-dismissible (no Esc, no backdrop, no close button) the opposite of the default.
13
+ // drawer non-dismissible (no Esc, no backdrop, no close button), the opposite of the default.
14
14
  const props = withDefaults(defineProps<{
15
15
  open: boolean;
16
16
  title: string;
@@ -8,11 +8,11 @@ import { computed, onMounted, useId, watch, type ComputedRef, type Ref, ref } fr
8
8
  // (showModal() on an open dialog throws), @cancel.prevent so Esc routes through app state instead of
9
9
  // closing the DOM behind its back, and backdrop-click detection via `event.target === dialogEl`.
10
10
  // Native <dialog> already provides the focus trap, focus restore, top-layer stacking and inert
11
- // background the wrapper only avoids breaking them. `dismissible` (default true) gates Esc + backdrop.
11
+ // background, the wrapper only avoids breaking them. `dismissible` (default true) gates Esc + backdrop.
12
12
 
13
13
  // dismissible MUST go through withDefaults: Vue's boolean-cast gives an omitted Boolean prop
14
14
  // `false`, not `undefined`, so a bare `props.dismissible !== false` would make an un-annotated
15
- // modal non-dismissible (no Esc, no backdrop, no close button) the opposite of the default.
15
+ // modal non-dismissible (no Esc, no backdrop, no close button), the opposite of the default.
16
16
  const props = withDefaults(defineProps<{
17
17
  open: boolean;
18
18
  title: string;
@@ -46,7 +46,7 @@ onMounted((): void => sync(props.open));
46
46
  function onCancel(): void {
47
47
  if (dismissible.value) emit('close');
48
48
  }
49
- // The ::backdrop is not a separate element a click whose target is the dialog box itself (not its
49
+ // The ::backdrop is not a separate element, a click whose target is the dialog box itself (not its
50
50
  // content) is a backdrop click.
51
51
  function onClick(e: MouseEvent): void {
52
52
  if (dismissible.value && e.target === dialogEl.value) emit('close');
@@ -45,14 +45,14 @@ const props = withDefaults(defineProps<{
45
45
  /** Client-side page size when `page` is absent; 0/undefined = render all rows (no pager). */
46
46
  pageSize?: number;
47
47
  /**
48
- * Controlled client-side page index (0-based). Provide it with `pageSize`, without `page` to
48
+ * Controlled client-side page index (0-based). Provide it, with `pageSize`, without `page`, to
49
49
  * own the page while the table keeps doing filter/sort/paginate. This is what lets an EXTERNAL
50
50
  * pager drive the table: a responsive screen that hides `.fdy-datatable` below `md` and renders a
51
51
  * card list from the `process` event can render one pager for both breakpoints and point it here.
52
52
  * Omit for the internal index (unchanged default).
53
53
  */
54
54
  /** Withhold the table's own footer (pager + range) so the screen can render one. Server mode had
55
- * no way to do this: the app owns the page there ANYWAY, and was still handed a second control
55
+ * no way to do this: the app owns the page there ANYWAY, and was still handed a second control,
56
56
  * a responsive list that shows a table at one breakpoint and cards at another ended up with the
57
57
  * kit's pager stacked under its own. Client mode's counterpart is `pageIndex`. Default true. */
58
58
  pager?: boolean;
@@ -61,7 +61,7 @@ const props = withDefaults(defineProps<{
61
61
  * (unchanged default). Every back office has one, and a table that renders two thirds of its own
62
62
  * footer forces the app to rebuild all three to add the last (#008).
63
63
  *
64
- * Server mode reports the pick through `update:page` same event as a page click, with a new
64
+ * Server mode reports the pick through `update:page`, same event as a page click, with a new
65
65
  * `size`. Client mode keeps it internally and also emits `update:pageSize`, so the control works
66
66
  * with nothing wired and can still be persisted.
67
67
  */
@@ -86,11 +86,11 @@ const emit = defineEmits<{
86
86
  * a reset to 0 after sort/filter, or a clamp when filtering shrank the set). */
87
87
  'update:pageIndex': [index: number];
88
88
  /** Client mode with `pageSizes`: the reader picked a new rows-per-page. The table has already
89
- * applied it this is for a caller that wants to persist the choice. */
89
+ * applied it, this is for a caller that wants to persist the choice. */
90
90
  'update:pageSize': [size: number];
91
91
  /** A row was activated (click, or Enter/Space while the row itself is focused). */
92
92
  'row-activate': [row: Row];
93
- /** The processed page of rows (after filter/sort/paginate) plus the total row count fires in
93
+ /** The processed page of rows (after filter/sort/paginate) plus the total row count, fires in
94
94
  * BOTH modes whenever they change. Lets a consumer render the SAME processed set elsewhere
95
95
  * (a `< md` card list, a "selected" summary, export-to-CSV) without re-deriving the pipeline. */
96
96
  'process': [result: { rows: Row[]; total: number }];
@@ -100,7 +100,7 @@ const internalSort: Ref<FdySortState | null> = ref(null);
100
100
  const internalFilters: Ref<FdyFilterMap> = ref({});
101
101
  const internalPageIndex: Ref<number> = ref(0);
102
102
  /* Client-mode rows-per-page. `pageSize` is a plain prop with no event, so a footer control that only
103
- * emitted would do nothing in the app that wired nothing the table applies the pick itself and
103
+ * emitted would do nothing in the app that wired nothing, the table applies the pick itself and
104
104
  * reports it. An explicit change to the prop wins back: the parent said something newer. */
105
105
  const internalPageSize: Ref<number | null> = ref(null);
106
106
  watch((): number | undefined => props.pageSize, (): void => { internalPageSize.value = null; });
@@ -250,7 +250,7 @@ function rowClasses(row: Row): Array<string | undefined> {
250
250
  function onRowClick(row: Row): void {
251
251
  if (props.rowActivatable === true) emit('row-activate', row);
252
252
  }
253
- // Enter/Space activate only when the row itself is focused a control inside a cell keeps its own
253
+ // Enter/Space activate only when the row itself is focused, a control inside a cell keeps its own
254
254
  // event (the `event.target !== event.currentTarget` guard). Click relies on inner controls calling
255
255
  // stopPropagation, matching the pattern consumers hand-roll today.
256
256
  function onRowKeydown(e: KeyboardEvent, row: Row): void {
@@ -7,7 +7,7 @@ import type { FdyColumnFilter, FdyColumnFilterType } from '../../core/table-mode
7
7
  // Internal to FdyTable: one column's header funnel button + its type-aware filter popover
8
8
  // (text / enum / number / date) over freeday's `.fdy-table__filterbtn` + `.fdy-filter*` classes.
9
9
  // Reuses usePopover so the panel escapes the table's `overflow:hidden` via the top layer. Purely
10
- // controlled it renders the current `filter` and emits the next one (or null to clear); the
10
+ // controlled, it renders the current `filter` and emits the next one (or null to clear); the
11
11
  // parent owns where that goes (client state or an `update:filters` emit). Not exported.
12
12
 
13
13
  const props = defineProps<{
@@ -8,19 +8,19 @@ import FdyCombo from './FdyCombo.vue';
8
8
  * The band under a table: what you are looking at, how much of it you see, where you are.
9
9
  *
10
10
  * `FdyTable` renders this itself, so most screens never import it. It is exported for the one shape
11
- * that cannot use the table's own a RESPONSIVE list, where a `.fdy-datatable` at `lg` and a
11
+ * that cannot use the table's own, a RESPONSIVE list, where a `.fdy-datatable` at `lg` and a
12
12
  * `.fdy-list` below it are two renderings of ONE page of rows. A footer inside the table is inside
13
13
  * the half that is hidden on a phone, so those screens need to render it once, outside both
14
14
  * (improvement notes #005 and #008, from IDU_EMATE_APPL_WEB).
15
15
  *
16
- * It owns nothing. `page` in, `update:page` out the same contract as the table's server mode, so a
16
+ * It owns nothing. `page` in, `update:page` out, the same contract as the table's server mode, so a
17
17
  * screen already wired for that switches by moving the prop.
18
18
  */
19
19
  const props = defineProps<{
20
20
  /** The page being shown. `size` drives the range AND the rows-per-page control's value. */
21
21
  page: FdyPageState;
22
22
  /**
23
- * Offer a rows-per-page control. Omit for none the footer is then range + pager, exactly as
23
+ * Offer a rows-per-page control. Omit for none; the footer is then range + pager, exactly as
24
24
  * before. Picking a size emits `update:page` carrying the new `size` and the index that still
25
25
  * holds the row you were looking at.
26
26
  */
@@ -29,7 +29,7 @@ const props = defineProps<{
29
29
 
30
30
  const emit = defineEmits<{ 'update:page': [page: FdyPageState] }>();
31
31
 
32
- /* The combo is labelled by the visible word beside it a `<label for>` cannot reach inside a
32
+ /* The combo is labelled by the visible word beside it, a `<label for>` cannot reach inside a
33
33
  * component, and an aria-label would leave that word unattached to anything. */
34
34
  const sizeLabelId: string = `fdy-rows-${useId()}`;
35
35
 
@@ -54,7 +54,7 @@ const sizes: ComputedRef<readonly number[]> = computed((): readonly number[] =>
54
54
  const sizeOptions: ComputedRef<Array<{ value: string; label: string }>> = computed(() =>
55
55
  sizes.value.map((size: number) => ({ value: String(size), label: String(size) })),
56
56
  );
57
- /* One page and no size control means there is nothing here to say the table has always withheld
57
+ /* One page and no size control means there is nothing here to say, the table has always withheld
58
58
  * the whole band in that case, and this is where that decision now lives. */
59
59
  const visible: ComputedRef<boolean> = computed((): boolean => hasPager.value || sizes.value.length > 0);
60
60
 
@@ -14,7 +14,7 @@ export declare function useFreeday(rootRef?: Ref<HTMLElement | null>): UseFreeda
14
14
 
15
15
  /**
16
16
  * Anchor a dropdown panel to its trigger in the top layer, so it escapes any ancestor overflow clip
17
- * `.fdy-card` is `overflow:hidden`, so a panel positioned inside one is otherwise cut at the card's
17
+ *, `.fdy-card` is `overflow:hidden`, so a panel positioned inside one is otherwise cut at the card's
18
18
  * edge. The panel element must carry `popover="manual"`. This is the plumbing every kit dropdown
19
19
  * uses; it is exported so a control the kit does not ship can behave like one that it does, instead
20
20
  * of each app re-implementing it from the description.
@@ -35,7 +35,7 @@ export type { CascadeNode } from './components/FdyCascade.vue';
35
35
  export { default as FdyCfl } from './components/FdyCfl.vue';
36
36
  export { default as FdyChart } from './components/FdyChart.vue';
37
37
  export { default as FdyTable } from './components/FdyTable.vue';
38
- /** The table's own footer, standalone for a responsive list whose table is hidden at some
38
+ /** The table's own footer, standalone, for a responsive list whose table is hidden at some
39
39
  * breakpoints and so cannot host it. */
40
40
  export { default as FdyTableFooter } from './components/FdyTableFooter.vue';
41
41
  export { default as FdyModal } from './components/FdyModal.vue';
@@ -1,4 +1,4 @@
1
- /* Freeday Vue 3 adapter (thin). The enhancers stay the source of truth; this
1
+ /* Freeday. Vue 3 adapter (thin). The enhancers stay the source of truth; this
2
2
  * composable just re-runs every registered window.Freeday*.initAll() over a
3
3
  * subtree after Vue renders it, so [data-fdy-*] markup rendered by Vue gets
4
4
  * enhanced (and re-enhanced after updates). Enhancers are idempotent, so the
@@ -10,12 +10,12 @@
10
10
  * useFreeday(root); // scopes init to this component's subtree
11
11
  * // <div ref="root"> ...[data-fdy-*] markup... </div>
12
12
  *
13
- * The ref may also sit ON the widget itself <div ref="root" data-fdy-menu> which is what you
13
+ * The ref may also sit ON the widget itself, <div ref="root" data-fdy-menu>, which is what you
14
14
  * write when the component's root element IS the widget. initAll() matches its own root as
15
15
  * well as descendants, so both shapes work. (Before 1.23.0 only the wrapping shape did, and
16
16
  * the other failed silently: querySelectorAll never matches its root.)
17
17
  *
18
- * Events come straight through as native bubbling CustomEvents bind them with
18
+ * Events come straight through as native bubbling CustomEvents, bind them with
19
19
  * plain v-on (e.g. @fdy-cascade-change) and read event.detail. See index.d.ts
20
20
  * for the detail types.
21
21
  */
package/dist/asset.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- // Shared type stub for every export imported purely for its side effects the CSS
1
+ // Shared type stub for every export imported purely for its side effects: the CSS
2
2
  // bundles (`./css`, `./css/components`, `./tokens`) and the individual enhancer scripts
3
3
  // (`./enhancers/*`). TypeScript only needs the `types` condition to *resolve* the module;
4
- // there is no value surface to describe. (The root `.` import is different it augments
5
- // window.Freeday so it points at ./freeday.d.ts instead.)
4
+ // there is no value surface to describe. (The root `.` import is different, since it augments
5
+ // window.Freeday, so it points at ./freeday.d.ts instead.)
6
6
  export {};
@@ -1,23 +1,23 @@
1
- /* Freeday app shell enhancer (optional, zero-dependency).
1
+ /* Freeday, app shell enhancer (optional, zero-dependency).
2
2
  * Opt in with <div class="fdy-app" data-fdy-app>; the markup is otherwise unchanged.
3
3
  *
4
4
  * The shell has always shipped the state classes and no behaviour, which left every consumer to
5
- * assemble the toggle, Escape, focus trap, focus restore and `inert` themselves and the two
5
+ * assemble the toggle, Escape, focus trap, focus restore and `inert` themselves, and the two
6
6
  * hand-rolled copies in this repo's own docs already disagreed about which of those exist. Below
7
7
  * 721px the sidebar is an OVERLAY (app-shell.css: position:fixed + translateX(-100%)), so it needs
8
8
  * the same treatment a modal drawer gets; at or above 721px it is a column that merely collapses.
9
9
  *
10
10
  * One rule covers both a hidden nav and a hidden page:
11
11
  * - `__sidebar` is inert whenever the nav is NOT visible. Off-canvas and collapsed panels stay in
12
- * the tab order otherwise translateX(-100%) and width:0 hide a thing from the eye, not from
12
+ * the tab order otherwise, translateX(-100%) and width:0 hide a thing from the eye, not from
13
13
  * the keyboard, which is how a nav nobody can see still swallows Tab.
14
14
  * - `__content` is inert only while the nav is an OPEN OVERLAY, so Tab cannot wander behind the
15
15
  * backdrop.
16
16
  *
17
17
  * Emits a bubbling `fdy-app-nav` CustomEvent (detail {visible}) whenever the nav's visibility
18
18
  * changes, and takes `FreedayAppShell.setVisible(root, visible)` from outside. Those two exist for
19
- * the same reason the other enhancers have them: a host that keeps its own state the Blazor
20
- * wrapper binding @bind-NavOpen, an app persisting the collapsed preference has to be able to
19
+ * the same reason the other enhancers have them: a host that keeps its own state, the Blazor
20
+ * wrapper binding @bind-NavOpen, an app persisting the collapsed preference, has to be able to
21
21
  * hear the change and to drive it, without owning the behaviour twice.
22
22
  * FreedayAppShell.init(root) for late-mounted markup.
23
23
  */
@@ -31,7 +31,7 @@
31
31
  + ' textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
32
32
 
33
33
  /* getClientRects(), not offsetParent: an overlay sidebar is position:fixed, and a fixed element
34
- reports no offsetParent at all filtering on it would call every nav item invisible. */
34
+ reports no offsetParent at all, filtering on it would call every nav item invisible. */
35
35
  function focusables(root) {
36
36
  return Array.prototype.filter.call(root.querySelectorAll(FOCUSABLE), function (el) {
37
37
  return el.getClientRects().length > 0;
@@ -62,7 +62,7 @@
62
62
  function isCollapsed() { return app.classList.contains('fdy-app--nav-collapsed'); }
63
63
  function navVisible() { return mqWide.matches ? !isCollapsed() : isOverlayOpen(); }
64
64
 
65
- /* aria-expanded answers "is the nav showing?" in BOTH modes the two state classes are the
65
+ /* aria-expanded answers "is the nav showing?" in BOTH modes, the two state classes are the
66
66
  kit's business, not the reader's. */
67
67
  function sync() {
68
68
  var visible = navVisible();
@@ -91,7 +91,7 @@
91
91
  }
92
92
  }
93
93
 
94
- /* restoreFocus is false when the viewport closed it rather than the user a resize must not
94
+ /* restoreFocus is false when the viewport closed it rather than the user, a resize must not
95
95
  yank focus out from under whatever the reader was doing. */
96
96
  function close(restoreFocus) {
97
97
  if (!isOverlayOpen()) return;
@@ -118,7 +118,7 @@
118
118
 
119
119
  if (backdrop) backdrop.addEventListener('click', function () { close(); });
120
120
 
121
- /* Following a link inside an overlay nav means "take me there" leaving the panel open over
121
+ /* Following a link inside an overlay nav means "take me there", leaving the panel open over
122
122
  the page you just asked for is the one thing every hand-rolled copy in this repo disagreed on. */
123
123
  sidebar.addEventListener('click', function (e) {
124
124
  if (!isOverlayOpen()) return;
@@ -1,7 +1,7 @@
1
- /* Freeday autocomplete enhancer (optional, zero-dependency).
1
+ /* Freeday, autocomplete enhancer (optional, zero-dependency).
2
2
  * Editable combobox (WAI-ARIA APG): a text input filters a role="listbox" of role="option"
3
3
  * items as you type; select fills the input. Auto-inits [data-fdy-autocomplete]. Options are
4
- * static markup filtered client-side in an app, fetch/filter server-side and re-render options.
4
+ * static markup filtered client-side, in an app, fetch/filter server-side and re-render options.
5
5
  *
6
6
  * Markup: <div data-fdy-autocomplete class="fdy-autocomplete">
7
7
  * <input class="fdy-input" role="combobox" aria-expanded="false" aria-autocomplete="list"
@@ -1,4 +1,4 @@
1
- /* Freeday breakpoint provider (optional, zero-dependency).
1
+ /* Freeday, breakpoint provider (optional, zero-dependency).
2
2
  * The MudBreakpointProvider equivalent for JS consumers: stamps the current breakpoint on
3
3
  * <html data-breakpoint="xs|sm|md|lg|xl"> and emits a bubbling "fdy-breakpoint-change"
4
4
  * CustomEvent (detail {breakpoint}) whenever it changes. Also fills any element with
@@ -1,4 +1,4 @@
1
- /* Freeday carousel enhancer (optional, zero-dependency).
1
+ /* Freeday, carousel enhancer (optional, zero-dependency).
2
2
  * Scroll-snap slider with prev/next arrows, generated dot indicators, keyboard (←/→),
3
3
  * and optional autoplay (data-fdy-autoplay="4000", pauses on hover/focus). Auto-inits
4
4
  * [data-fdy-carousel].
@@ -18,16 +18,16 @@
18
18
  'use strict';
19
19
 
20
20
 
21
- /* User-facing strings. Indonesian by default documented and deliberate for the raw enhancer
22
- * path and every one overridable per element, so a host that speaks another language (the
21
+ /* User-facing strings. Indonesian by default, documented and deliberate for the raw enhancer
22
+ * path, and every one overridable per element, so a host that speaks another language (the
23
23
  * Blazor adapters, an English app on the raw path) supplies its own without forking this file.
24
24
  * Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
25
25
  var TEXT = {
26
- position: '{n} dari {total}',
26
+ position: '{n} of {total}',
27
27
  slide: 'Slide {n}'
28
28
  };
29
29
  /* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
30
- as `data-fdy-text-filtertext` while the kebab form anybody would reach for,
30
+ as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
31
31
  `data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
32
32
  override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
33
33
  still resolves, for markup written against 1.39.0. */
@@ -1,4 +1,4 @@
1
- /* Freeday cascade select enhancer (optional, zero-dependency).
1
+ /* Freeday, cascade select enhancer (optional, zero-dependency).
2
2
  * Turns a [data-fdy-cascade] wrapper into a hierarchical drill-down picker. The
3
3
  * data model is a hidden nested <ul> inside the wrapper: a <li> with a child <ul>
4
4
  * is a branch (drills in), a <li> without is a leaf (selects). Each <li> carries
@@ -54,16 +54,16 @@
54
54
  }
55
55
 
56
56
 
57
- /* User-facing strings. Indonesian by default documented and deliberate for the raw enhancer
58
- * path and every one overridable per element, so a host that speaks another language (the
57
+ /* User-facing strings. Indonesian by default, documented and deliberate for the raw enhancer
58
+ * path, and every one overridable per element, so a host that speaks another language (the
59
59
  * Blazor adapters, an English app on the raw path) supplies its own without forking this file.
60
60
  * Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
61
61
  var TEXT = {
62
- back: 'Kembali satu tingkat',
62
+ back: 'Back one level',
63
63
  submenu: '{label}, submenu'
64
64
  };
65
65
  /* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
66
- as `data-fdy-text-filtertext` while the kebab form anybody would reach for,
66
+ as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
67
67
  `data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
68
68
  override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
69
69
  still resolves, for markup written against 1.39.0. */
@@ -88,8 +88,8 @@
88
88
  var root = sourceUl ? parse(sourceUl) : [];
89
89
  if (sourceUl) sourceUl.remove();
90
90
 
91
- var label = wrap.getAttribute('data-label') || 'Pilih';
92
- var placeholder = wrap.getAttribute('data-placeholder') || 'Pilih…';
91
+ var label = wrap.getAttribute('data-label') || 'Select';
92
+ var placeholder = wrap.getAttribute('data-placeholder') || 'Select…';
93
93
  var sep = wrap.getAttribute('data-separator') || ' / ';
94
94
 
95
95
  var trigger = document.createElement('button');
@@ -1,21 +1,21 @@
1
- /* Freeday choose-from-list enhancer (optional, zero-dependency).
1
+ /* Freeday, choose-from-list enhancer (optional, zero-dependency).
2
2
  * Wires a searchable master-data picker dialog to read-only fields.
3
3
  * Progressive enhancement over a native <dialog>; in a framework app, drive the
4
4
  * dialog as a controlled component (fetchPage callback + server cache), not a store.
5
5
  *
6
6
  * Markup contract (see docs):
7
- * - Field group: [data-fdy-cfl="<dialogId>"] usually a `.fdy-input-group` holding a
7
+ * - Field group: [data-fdy-cfl="<dialogId>"], usually a `.fdy-input-group` holding a
8
8
  * read-only `.fdy-input` and a trigger `<button class="fdy-input-group__btn">`.
9
9
  * Optional `data-fdy-cfl-display="<key>"` picks which row dataset key fills the input
10
10
  * (default "value"); optional `data-fdy-cfl-summary="{n} dipilih"` formats the multi label.
11
- * - Standalone opener: [data-fdy-cfl-open="<dialogId>"] opens the dialog with no bound field.
11
+ * - Standalone opener: [data-fdy-cfl-open="<dialogId>"], opens the dialog with no bound field.
12
12
  * - Dialog: <dialog id="<dialogId>" class="fdy-modal fdy-modal--cfl"> containing
13
13
  * a `[data-fdy-cfl-search]` input, `.fdy-cfl__row` <tr> rows carrying `data-*` fields,
14
14
  * a `[data-fdy-cfl-empty]` element, and (multi only) `[data-fdy-cfl-multiple]` on the
15
15
  * dialog plus `[data-fdy-cfl-count]` and `[data-fdy-cfl-confirm]` in the footer.
16
16
  *
17
17
  * Emits a bubbling "fdy-cfl-select" CustomEvent on the field group (or the dialog when
18
- * opened standalone): detail {row} for single-select, {rows} for multi-select each a plain
18
+ * opened standalone): detail {row} for single-select, {rows} for multi-select, each a plain
19
19
  * object copy of the row's dataset. Consumers fill sibling fields (code → name) from it.
20
20
  */
21
21
  (function () {
@@ -34,15 +34,15 @@
34
34
  }
35
35
 
36
36
 
37
- /* User-facing strings. Indonesian by default documented and deliberate for the raw enhancer
38
- * path and every one overridable per element, so a host that speaks another language (the
37
+ /* User-facing strings. Indonesian by default, documented and deliberate for the raw enhancer
38
+ * path, and every one overridable per element, so a host that speaks another language (the
39
39
  * Blazor adapters, an English app on the raw path) supplies its own without forking this file.
40
40
  * Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
41
41
  var TEXT = {
42
- selected: '{n} dipilih'
42
+ selected: '{n} selected'
43
43
  };
44
44
  /* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
45
- as `data-fdy-text-filtertext` while the kebab form anybody would reach for,
45
+ as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
46
46
  `data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
47
47
  override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
48
48
  still resolves, for markup written against 1.39.0. */
@@ -103,7 +103,7 @@
103
103
  var input = activeGroup.querySelector('.fdy-input');
104
104
  if (input) {
105
105
  if (detail.rows) {
106
- var tpl = activeGroup.getAttribute('data-fdy-cfl-summary') || '{n} dipilih';
106
+ var tpl = activeGroup.getAttribute('data-fdy-cfl-summary') || '{n} selected';
107
107
  input.value = detail.rows.length ? tpl.replace('{n}', String(detail.rows.length)) : '';
108
108
  } else {
109
109
  var key = activeGroup.getAttribute('data-fdy-cfl-display') || 'value';