@visitwonders/assembly 0.16.1 → 0.17.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 (79) hide show
  1. package/declarations/action/button-group.d.ts +5 -8
  2. package/declarations/action/button-group.d.ts.map +1 -1
  3. package/declarations/data/pagination.d.ts +3 -5
  4. package/declarations/data/pagination.d.ts.map +1 -1
  5. package/declarations/data/table.d.ts +4 -11
  6. package/declarations/data/table.d.ts.map +1 -1
  7. package/declarations/form/calendar.d.ts +19 -12
  8. package/declarations/form/calendar.d.ts.map +1 -1
  9. package/declarations/form/checkbox-group.d.ts +2 -8
  10. package/declarations/form/checkbox-group.d.ts.map +1 -1
  11. package/declarations/form/checkbox.d.ts +4 -8
  12. package/declarations/form/checkbox.d.ts.map +1 -1
  13. package/declarations/form/combobox-field.d.ts +1 -1
  14. package/declarations/form/combobox-field.d.ts.map +1 -1
  15. package/declarations/form/combobox.d.ts +1 -1
  16. package/declarations/form/combobox.d.ts.map +1 -1
  17. package/declarations/form/date-picker-field.d.ts +4 -3
  18. package/declarations/form/date-picker-field.d.ts.map +1 -1
  19. package/declarations/form/date-picker.d.ts +11 -8
  20. package/declarations/form/date-picker.d.ts.map +1 -1
  21. package/declarations/form/date-range-picker-field.d.ts +4 -3
  22. package/declarations/form/date-range-picker-field.d.ts.map +1 -1
  23. package/declarations/form/date-range-picker.d.ts +6 -9
  24. package/declarations/form/date-range-picker.d.ts.map +1 -1
  25. package/declarations/form/input.d.ts +1 -4
  26. package/declarations/form/input.d.ts.map +1 -1
  27. package/declarations/form/money-field.d.ts +13 -3
  28. package/declarations/form/money-field.d.ts.map +1 -1
  29. package/declarations/form/multi-combobox-field.d.ts +1 -1
  30. package/declarations/form/multi-combobox-field.d.ts.map +1 -1
  31. package/declarations/form/multi-combobox.d.ts +1 -1
  32. package/declarations/form/multi-combobox.d.ts.map +1 -1
  33. package/declarations/form/radio.d.ts +3 -5
  34. package/declarations/form/radio.d.ts.map +1 -1
  35. package/declarations/form/search-input.d.ts +2 -1
  36. package/declarations/form/search-input.d.ts.map +1 -1
  37. package/declarations/form/text-field.d.ts +1 -7
  38. package/declarations/form/text-field.d.ts.map +1 -1
  39. package/declarations/form/textarea.d.ts +1 -7
  40. package/declarations/form/textarea.d.ts.map +1 -1
  41. package/declarations/form/toggle-field.d.ts +2 -4
  42. package/declarations/form/toggle-field.d.ts.map +1 -1
  43. package/declarations/form/toggle.d.ts +3 -9
  44. package/declarations/form/toggle.d.ts.map +1 -1
  45. package/declarations/overlay/drawer.d.ts +10 -9
  46. package/declarations/overlay/drawer.d.ts.map +1 -1
  47. package/declarations/overlay/modal.d.ts +10 -10
  48. package/declarations/overlay/modal.d.ts.map +1 -1
  49. package/declarations/overlay/popover.d.ts +9 -8
  50. package/declarations/overlay/popover.d.ts.map +1 -1
  51. package/declarations/overlay/tooltip.d.ts +9 -8
  52. package/declarations/overlay/tooltip.d.ts.map +1 -1
  53. package/dist/action/button-group.js +2 -25
  54. package/dist/data/pagination.js +10 -24
  55. package/dist/data/table.js +9 -71
  56. package/dist/form/calendar.js +29 -57
  57. package/dist/form/checkbox-group.js +3 -23
  58. package/dist/form/checkbox.js +3 -17
  59. package/dist/form/combobox-field.js.map +1 -1
  60. package/dist/form/date-picker-field.js +1 -1
  61. package/dist/form/date-picker-field.js.map +1 -1
  62. package/dist/form/date-picker.js +10 -28
  63. package/dist/form/date-range-picker-field.js +1 -1
  64. package/dist/form/date-range-picker-field.js.map +1 -1
  65. package/dist/form/date-range-picker.js +8 -31
  66. package/dist/form/input.js +1 -6
  67. package/dist/form/money-field.js +23 -22
  68. package/dist/form/multi-combobox-field.js.map +1 -1
  69. package/dist/form/radio.js +3 -4
  70. package/dist/form/search-input.js +6 -9
  71. package/dist/form/text-field.js +3 -22
  72. package/dist/form/textarea.js +2 -17
  73. package/dist/form/toggle-field.js +1 -1
  74. package/dist/form/toggle.js +4 -18
  75. package/dist/overlay/drawer.js +6 -26
  76. package/dist/overlay/modal.js +5 -29
  77. package/dist/overlay/popover.js +15 -16
  78. package/dist/overlay/tooltip.js +14 -16
  79. package/package.json +1 -1
@@ -1,38 +1,21 @@
1
1
  import "./button-group.css"
2
2
  import Component from '@glimmer/component';
3
- import { tracked } from '@glimmer/tracking';
4
3
  import { action } from '@ember/object';
5
4
  import { on } from '@ember/modifier';
6
5
  import { fn, hash } from '@ember/helper';
7
6
  import Button from './button.js';
8
7
  import { precompileTemplate } from '@ember/template-compilation';
9
8
  import { setComponentTemplate } from '@ember/component';
10
- import { g, i, n } from 'decorator-transforms/runtime';
9
+ import { n } from 'decorator-transforms/runtime';
11
10
 
12
11
  ;
13
12
 
14
13
  class ButtonGroup extends Component {
15
- static {
16
- g(this.prototype, "internalValue", [tracked]);
17
- }
18
- #internalValue = (i(this, "internalValue"), void 0);
19
- constructor(owner, args) {
20
- super(owner, args);
21
- const defaultVal = args.defaultValue;
22
- if (this.selectionType === 'multiple') {
23
- this.internalValue = Array.isArray(defaultVal) ? [...defaultVal] : defaultVal ? [defaultVal] : [];
24
- } else {
25
- this.internalValue = Array.isArray(defaultVal) ? defaultVal[0] ?? '' : defaultVal ?? '';
26
- }
27
- }
28
14
  get selectionType() {
29
15
  return this.args.type ?? 'single';
30
16
  }
31
- get isControlled() {
32
- return this.args.value !== undefined;
33
- }
34
17
  get selectedValue() {
35
- return this.isControlled ? this.args.value ?? '' : this.internalValue;
18
+ return this.args.value;
36
19
  }
37
20
  get variant() {
38
21
  return this.args.variant ?? 'outline';
@@ -74,15 +57,9 @@ class ButtonGroup extends Component {
74
57
  } else {
75
58
  newValues = [...currentArray, value];
76
59
  }
77
- if (!this.isControlled) {
78
- this.internalValue = newValues;
79
- }
80
60
  this.args.onChange?.(newValues);
81
61
  } else {
82
62
  if (this.selectedValue === value) return;
83
- if (!this.isControlled) {
84
- this.internalValue = value;
85
- }
86
63
  this.args.onChange?.(value);
87
64
  }
88
65
  }
@@ -20,13 +20,6 @@ import { g, i } from 'decorator-transforms/runtime';
20
20
  // Component
21
21
  // ============================================================================
22
22
  class Pagination extends Component {
23
- static {
24
- g(this.prototype, "internalCurrentPage", [tracked], function () {
25
- return 1;
26
- });
27
- }
28
- #internalCurrentPage = (i(this, "internalCurrentPage"), void 0); // Uncontrolled-mode state. When the consumer passes `@currentPage`, this
29
- // value is ignored; otherwise it tracks page locally.
30
23
  // Tracked snapshot of the last seen totalPages so the refresh-clamp modifier
31
24
  // can detect a shrink-under-the-user without scheduling its own runloop.
32
25
  previousTotalPages = null;
@@ -86,15 +79,10 @@ class Pagination extends Component {
86
79
  return this.args.showJumpTo ?? false;
87
80
  }
88
81
  // ============================================================================
89
- // Mode inference (mirrors Table's isControlledSelection / isControlledSort).
90
- // Note: unlike Table, @onChange fires in BOTH modes — Pagination models a
91
- // navigation event. See spec §Behaviour > Callback contract.
82
+ // Current page (the consumer owns it)
92
83
  // ============================================================================
93
- get isControlled() {
94
- return this.args.currentPage !== undefined;
95
- }
96
84
  get currentPage() {
97
- return this.args.currentPage ?? this.internalCurrentPage;
85
+ return this.args.currentPage;
98
86
  }
99
87
  // ============================================================================
100
88
  // Derived counts
@@ -182,14 +170,11 @@ class Pagination extends Component {
182
170
  // ============================================================================
183
171
  // Navigation
184
172
  // ============================================================================
185
- // Single update path. Fires @onChange in both modes the callback models a
186
- // navigation event regardless of whether the parent stores currentPage.
187
- // Spec §Behaviour > Callback contract.
173
+ // Single navigation path. Clamps to [1, totalPages] and fires @onChange with
174
+ // the result; the consumer advances @currentPage. The component never writes
175
+ // its own page. Spec §Behaviour > Callback contract.
188
176
  goToPage(page) {
189
177
  const clamped = Math.max(1, Math.min(this.totalPages, page));
190
- if (!this.isControlled) {
191
- this.internalCurrentPage = clamped;
192
- }
193
178
  this.args.onChange(clamped);
194
179
  }
195
180
  handlePageClick = page => {
@@ -253,10 +238,11 @@ class Pagination extends Component {
253
238
  // assumed to be valid.
254
239
  if (previous === null) return;
255
240
  // Only clamp on shrink-past-current. Growth never re-fires.
256
- // The mutation is deferred to a microtask: the modifier reads
257
- // `currentPage` (which in uncontrolled mode is `internalCurrentPage`)
258
- // and `goToPage` writes it. Glimmer rejects same-computation read+write
259
- // on a tracked field, so we punt the write to the next microtask.
241
+ // @onChange is deferred to a microtask: the modifier reads `currentPage`
242
+ // (i.e. @currentPage), and firing @onChange drives the consumer to update
243
+ // that same arg. Glimmer rejects mutating a tracked value read in the
244
+ // same computation, so we punt the fire to the next microtask. Tests
245
+ // flush it with `await settled()`.
260
246
  if (totalPages < previous && currentPage > totalPages) {
261
247
  void Promise.resolve().then(() => this.goToPage(totalPages));
262
248
  }
@@ -54,18 +54,6 @@ class Table extends Component {
54
54
  });
55
55
  }
56
56
  #focusedRowIndex = (i(this, "focusedRowIndex"), void 0);
57
- static {
58
- g(this.prototype, "internalSortState", [tracked], function () {
59
- return null;
60
- });
61
- }
62
- #internalSortState = (i(this, "internalSortState"), void 0);
63
- static {
64
- g(this.prototype, "internalSelectedIds", [tracked], function () {
65
- return [];
66
- });
67
- }
68
- #internalSelectedIds = (i(this, "internalSelectedIds"), void 0);
69
57
  static {
70
58
  g(this.prototype, "lastSelectedIndex", [tracked], function () {
71
59
  return null;
@@ -92,60 +80,18 @@ class Table extends Component {
92
80
  get visibleColumns() {
93
81
  return this.args.columns.filter(col => !col.hidden);
94
82
  }
95
- get isControlledSelection() {
96
- return this.args.selectedIds !== undefined;
97
- }
98
83
  get selectedIds() {
99
- return this.isControlledSelection ? this.args.selectedIds ?? [] : this.internalSelectedIds;
100
- }
101
- get isControlledSort() {
102
- return this.args.sortBy !== undefined || this.args.onSortChange !== undefined;
84
+ return this.args.selectedIds ?? [];
103
85
  }
104
86
  get sortState() {
105
- return this.isControlledSort ? this.args.sortBy ?? null : this.internalSortState;
106
- }
107
- get sortedData() {
108
- const data = this.args.data ?? [];
109
- const sort = this.sortState;
110
- if (!sort) return data;
111
- const column = this.args.columns.find(col => col.id === sort.columnId);
112
- if (!column) return data;
113
- const sorted = [...data];
114
- sorted.sort((a, b) => {
115
- // Use custom sort function if provided
116
- if (column.sortFn) {
117
- const result = column.sortFn(a, b);
118
- return sort.direction === 'desc' ? -result : result;
119
- }
120
- // Default sort using accessor
121
- const aVal = this.getCellValue(a, column);
122
- const bVal = this.getCellValue(b, column);
123
- let result = 0;
124
- if (aVal === bVal) {
125
- result = 0;
126
- } else if (aVal === null || aVal === undefined) {
127
- result = 1;
128
- } else if (bVal === null || bVal === undefined) {
129
- result = -1;
130
- } else if (typeof aVal === 'string' && typeof bVal === 'string') {
131
- result = aVal.localeCompare(bVal);
132
- } else if (typeof aVal === 'number' && typeof bVal === 'number') {
133
- result = aVal - bVal;
134
- } else {
135
- // Handle other types by converting to string safely
136
- const aStr = stringifyValue(aVal);
137
- const bStr = stringifyValue(bVal);
138
- result = aStr.localeCompare(bStr);
139
- }
140
- return sort.direction === 'desc' ? -result : result;
141
- });
142
- return sorted;
87
+ return this.args.sortBy ?? null;
143
88
  }
89
+ // The table renders @data in the order it is given and never sorts it. The
90
+ // consumer owns the sort: @sortBy drives the header indicator and
91
+ // @onSortChange reports a click; the consumer reorders the data.
92
+ // See docs/decisions/0003-sorting-model.md.
144
93
  get displayData() {
145
- if (this.args.isLoading) {
146
- return [];
147
- }
148
- return this.sortedData;
94
+ return this.args.isLoading ? [] : this.args.data ?? [];
149
95
  }
150
96
  get isEmpty() {
151
97
  return !this.args.isLoading && this.displayData.length === 0;
@@ -229,18 +175,10 @@ class Table extends Component {
229
175
  return 'none';
230
176
  };
231
177
  updateSelection(newIds) {
232
- if (this.isControlledSelection) {
233
- this.args.onSelectionChange?.(newIds);
234
- } else {
235
- this.internalSelectedIds = newIds;
236
- }
178
+ this.args.onSelectionChange?.(newIds);
237
179
  }
238
180
  updateSort(newSort) {
239
- if (this.isControlledSort) {
240
- this.args.onSortChange?.(newSort);
241
- } else {
242
- this.internalSortState = newSort;
243
- }
181
+ this.args.onSortChange?.(newSort);
244
182
  }
245
183
  moveFocus(direction) {
246
184
  const rows = this.displayData;
@@ -18,35 +18,26 @@ import { g, i } from 'decorator-transforms/runtime';
18
18
  // ============================================================================
19
19
  class Calendar extends Component {
20
20
  static {
21
- g(this.prototype, "internalMonth", [tracked], function () {
21
+ g(this.prototype, "_navigationMonth", [tracked], function () {
22
22
  return new Date();
23
23
  });
24
24
  }
25
- #internalMonth = (i(this, "internalMonth"), void 0);
25
+ #_navigationMonth = (i(this, "_navigationMonth"), void 0);
26
+ /**
27
+ * The month the calendar is currently showing. This is the calendar's own
28
+ * navigation mechanics, not the consumer's domain state: the user pages
29
+ * through months to find a date. It seeds to the current month and the prev
30
+ * and next buttons (and keyboard month paging) drive it. When the consumer
31
+ * passes `@month`, that override wins and this field is bypassed.
32
+ */
26
33
  static {
27
34
  g(this.prototype, "focusedDate", [tracked], function () {
28
35
  return null;
29
36
  });
30
37
  }
31
- #focusedDate = (i(this, "focusedDate"), void 0);
32
- static {
33
- g(this.prototype, "internalValue", [tracked], function () {
34
- return null;
35
- });
36
- }
37
- #internalValue = (i(this, "internalValue"), void 0);
38
- static {
39
- g(this.prototype, "internalMultiValue", [tracked], function () {
40
- return [];
41
- });
42
- }
43
- #internalMultiValue = (i(this, "internalMultiValue"), void 0);
44
- static {
45
- g(this.prototype, "internalRangeValue", [tracked], function () {
46
- return null;
47
- });
48
- }
49
- #internalRangeValue = (i(this, "internalRangeValue"), void 0);
38
+ #focusedDate = (i(this, "focusedDate"), void 0); // Ephemeral UI mechanics the calendar owns: which day has keyboard focus,
39
+ // which day is hovered during a range drag, and the first-click anchor of an
40
+ // in-progress range before the second click lands.
50
41
  static {
51
42
  g(this.prototype, "rangeSelectionStart", [tracked], function () {
52
43
  return null;
@@ -78,46 +69,32 @@ class Calendar extends Component {
78
69
  get showToday() {
79
70
  return this.args.showToday ?? true;
80
71
  }
81
- /** Current displayed month (controlled or uncontrolled) */
72
+ /**
73
+ * The month on screen. The `@month` override wins when present; otherwise the
74
+ * calendar's own navigation state drives. Writing it (via the prev and next
75
+ * buttons or keyboard month paging) updates the navigation state and reports
76
+ * the new month up. The override is left untouched — when it is in play the
77
+ * consumer updates it in response to `onMonthChange`, exactly as it owns the
78
+ * selection.
79
+ */
82
80
  get currentMonth() {
83
- if (this.args.month !== undefined) {
84
- return this.args.month;
85
- }
86
- if (this.args.defaultMonth !== undefined && !this.hasInternalMonthChanged) {
87
- return this.args.defaultMonth;
88
- }
89
- return this.internalMonth;
81
+ return this.args.month ?? this._navigationMonth;
90
82
  }
91
- hasInternalMonthChanged = false;
92
- hasInternalValueChanged = false;
93
83
  set currentMonth(value) {
94
- this.hasInternalMonthChanged = true;
95
- this.internalMonth = value;
84
+ this._navigationMonth = value;
96
85
  this.args.onMonthChange?.(value);
97
86
  }
98
- /** Selected value for single mode (controlled or uncontrolled) */
87
+ /** Selected value for single mode, owned by the consumer via `@value`. */
99
88
  get selectedValue() {
100
- if (this.args.value !== undefined) {
101
- return this.args.value;
102
- }
103
- if (this.args.defaultValue !== undefined && !this.hasInternalValueChanged) {
104
- return this.args.defaultValue;
105
- }
106
- return this.internalValue;
89
+ return this.args.value ?? null;
107
90
  }
108
- /** Selected values for multiple mode */
91
+ /** Selected values for multiple mode, owned by the consumer via `@multiValue`. */
109
92
  get selectedMultiValue() {
110
- if (this.args.multiValue !== undefined) {
111
- return this.args.multiValue;
112
- }
113
- return this.internalMultiValue;
93
+ return this.args.multiValue ?? [];
114
94
  }
115
- /** Selected range for range mode */
95
+ /** Selected range for range mode, owned by the consumer via `@rangeValue`. */
116
96
  get selectedRange() {
117
- if (this.args.rangeValue !== undefined) {
118
- return this.args.rangeValue;
119
- }
120
- return this.internalRangeValue;
97
+ return this.args.rangeValue ?? null;
121
98
  }
122
99
  /** Weekday labels starting from weekStartsOn */
123
100
  get weekdayLabels() {
@@ -278,8 +255,6 @@ class Calendar extends Component {
278
255
  selectDate = date => {
279
256
  if (this.isDateDisabled(date)) return;
280
257
  if (this.mode === 'single') {
281
- this.hasInternalValueChanged = true;
282
- this.internalValue = date;
283
258
  this.args.onChange?.(date);
284
259
  } else if (this.mode === 'multiple') {
285
260
  const current = [...this.selectedMultiValue];
@@ -291,13 +266,11 @@ class Calendar extends Component {
291
266
  } else {
292
267
  current.push(date);
293
268
  }
294
- this.internalMultiValue = current;
295
269
  this.args.onMultiChange?.(current);
296
270
  } else if (this.mode === 'range') {
297
271
  if (!this.rangeSelectionStart) {
298
- // First click: set range start and clear any existing range
272
+ // First click: set range start (ephemeral anchor) and clear the range.
299
273
  this.rangeSelectionStart = date;
300
- this.internalRangeValue = null;
301
274
  this.args.onRangeChange?.(null);
302
275
  } else {
303
276
  // Second click: complete range
@@ -312,7 +285,6 @@ class Calendar extends Component {
312
285
  };
313
286
  this.rangeSelectionStart = null;
314
287
  this.hoveredDate = null;
315
- this.internalRangeValue = range;
316
288
  this.args.onRangeChange?.(range);
317
289
  }
318
290
  }
@@ -1,6 +1,5 @@
1
1
  import "./checkbox-group.css"
2
2
  import Component from '@glimmer/component';
3
- import { tracked } from '@glimmer/tracking';
4
3
  import { action } from '@ember/object';
5
4
  import { on } from '@ember/modifier';
6
5
  import { hash } from '@ember/helper';
@@ -9,25 +8,14 @@ import Checkbox from './checkbox.js';
9
8
  import ErrorMessage from './error-message.js';
10
9
  import { precompileTemplate } from '@ember/template-compilation';
11
10
  import { setComponentTemplate } from '@ember/component';
12
- import { g, i, n } from 'decorator-transforms/runtime';
11
+ import { n } from 'decorator-transforms/runtime';
13
12
 
14
13
  ;
15
14
 
16
15
  class CheckboxGroup extends Component {
17
- static {
18
- g(this.prototype, "internalValues", [tracked]);
19
- }
20
- #internalValues = (i(this, "internalValues"), void 0);
21
- constructor(owner, args) {
22
- super(owner, args);
23
- this.internalValues = [...(args.defaultValue ?? [])];
24
- }
25
16
  guid = guidFor(this);
26
- get isControlled() {
27
- return this.args.value !== undefined;
28
- }
29
17
  get selectedValues() {
30
- return this.isControlled ? this.args.value ?? [] : this.internalValues;
18
+ return this.args.value;
31
19
  }
32
20
  get orientation() {
33
21
  return this.args.orientation ?? 'vertical';
@@ -48,15 +36,7 @@ class CheckboxGroup extends Component {
48
36
  return this.selectedValues.includes(value);
49
37
  };
50
38
  handleCheckboxChange(value, isChecked) {
51
- let newValues;
52
- if (isChecked) {
53
- newValues = [...this.selectedValues, value];
54
- } else {
55
- newValues = this.selectedValues.filter(v => v !== value);
56
- }
57
- if (!this.isControlled) {
58
- this.internalValues = newValues;
59
- }
39
+ const newValues = isChecked ? [...this.selectedValues, value] : this.selectedValues.filter(v => v !== value);
60
40
  this.args.onChange?.(newValues);
61
41
  }
62
42
  static {
@@ -1,32 +1,20 @@
1
1
  import "./checkbox.css"
2
2
  import Component from '@glimmer/component';
3
- import { tracked } from '@glimmer/tracking';
4
3
  import { action } from '@ember/object';
5
4
  import { on } from '@ember/modifier';
6
5
  import { modifier } from 'ember-modifier';
7
6
  import { precompileTemplate } from '@ember/template-compilation';
8
7
  import { setComponentTemplate } from '@ember/component';
9
- import { g, i, n } from 'decorator-transforms/runtime';
8
+ import { n } from 'decorator-transforms/runtime';
10
9
 
11
10
  ;
12
11
 
13
12
  class Checkbox extends Component {
14
- static {
15
- g(this.prototype, "internalChecked", [tracked]);
16
- }
17
- #internalChecked = (i(this, "internalChecked"), void 0);
18
- constructor(owner, args) {
19
- super(owner, args);
20
- this.internalChecked = args.defaultChecked ?? false;
21
- }
22
13
  get size() {
23
14
  return this.args.size ?? 'md';
24
15
  }
25
- get isControlled() {
26
- return this.args.isChecked !== undefined;
27
- }
28
16
  get checked() {
29
- return this.isControlled ? this.args.isChecked ?? false : this.internalChecked;
17
+ return this.args.isChecked;
30
18
  }
31
19
  get ariaChecked() {
32
20
  if (this.args.isIndeterminate) {
@@ -49,9 +37,7 @@ class Checkbox extends Component {
49
37
  });
50
38
  handleChange(event) {
51
39
  const target = event.target;
52
- if (!this.isControlled) {
53
- this.internalChecked = target.checked;
54
- }
40
+ this.args.onChange?.(target.checked, event);
55
41
  }
56
42
  static {
57
43
  n(this.prototype, "handleChange", [action]);
@@ -1 +1 @@
1
- {"version":3,"file":"combobox-field.js","sources":["../../src/form/combobox-field.gts"],"sourcesContent":["import Component from '@glimmer/component';\n\nimport Control from './control.gts';\nimport ComboBox from './combobox.gts';\nimport type { ComboBoxItems, ComboBoxOption } from './combobox-shared.ts';\n\nexport type LabelVisibility = 'visible' | 'hidden';\n\nexport interface ComboBoxFieldSignature {\n Element: HTMLDivElement;\n Args: {\n /** Label text (required). */\n label: string;\n\n /** Available options. Flat or pre-grouped. */\n items: ComboBoxItems;\n\n /** Current selected value (controlled). */\n value?: string | null;\n\n /** Fires when the selection changes. */\n onChange?: (value: string | null, option: ComboBoxOption | null) => void;\n\n /** Async search (passed through to ComboBox). */\n onSearch?: (query: string) => Promise<ComboBoxItems>;\n\n /** Debounce for `onSearch` in ms. */\n searchDebounceMs?: number;\n\n /** Force the loading state. */\n isLoading?: boolean;\n\n /** Called when an `onSearch` promise rejects. */\n onSearchError?: (error: unknown) => void;\n\n /** Loading row text. */\n loadingText?: string;\n\n /** Input placeholder. */\n placeholder?: string;\n\n /** Empty state text. */\n noResultsText?: string;\n\n /** Allow creating new options from a non-matching query. */\n isCreatable?: boolean;\n\n /** Fires when the user activates the create row. */\n onCreate?: (query: string) => void;\n\n /** Build the create-row label. */\n createLabel?: (query: string) => string;\n\n /** Help text displayed below the control. */\n helpText?: string;\n\n /** Error message (also sets invalid state). */\n error?: string;\n\n /** Field is required. */\n isRequired?: boolean;\n\n /** Field is disabled. */\n isDisabled?: boolean;\n\n /** Show the clear button when a value is set. */\n isClearable?: boolean;\n\n /** Info tooltip text shown next to the label. */\n labelInfo?: string;\n\n /** Optional indicator - shows \"optional\" or custom text next to the label. */\n optionalIndicator?: boolean | string;\n\n /** Label visibility. */\n labelVisibility?: LabelVisibility;\n\n /** Input name for forms. */\n name?: string;\n\n /** Blur event handler. */\n onBlur?: (event: FocusEvent) => void;\n\n /** Focus event handler. */\n onFocus?: (event: FocusEvent) => void;\n\n /** Fires when the dropdown opens. */\n onOpen?: () => void;\n\n /** Fires when the dropdown closes. */\n onClose?: () => void;\n };\n Blocks: {\n /** Rich tooltip content shown next to the label. */\n info: [];\n };\n}\n\nexport default class ComboBoxField extends Component<ComboBoxFieldSignature> {\n get isInvalid(): boolean {\n return !!this.args.error;\n }\n\n get isLabelHidden(): boolean {\n return this.args.labelVisibility === 'hidden';\n }\n\n /** Build aria-describedby from the control's help text and error IDs. */\n getAriaDescribedBy = (controlId: string): string | undefined => {\n const parts: string[] = [];\n if (this.args.helpText) {\n parts.push(`${controlId}-help-text`);\n }\n if (this.args.error) {\n parts.push(`${controlId}-error-message`);\n }\n return parts.length > 0 ? parts.join(' ') : undefined;\n };\n\n <template>\n <Control\n @isInvalid={{this.isInvalid}}\n @isDisabled={{@isDisabled}}\n @isRequired={{@isRequired}}\n @labelInfo={{@labelInfo}}\n @optionalIndicator={{@optionalIndicator}}\n ...attributes\n as |ctrl|\n >\n {{#if (has-block \"info\")}}\n <ctrl.Label @isVisuallyHidden={{this.isLabelHidden}}>\n <:default>{{@label}}</:default>\n <:info>{{yield to=\"info\"}}</:info>\n </ctrl.Label>\n {{else}}\n <ctrl.Label @isVisuallyHidden={{this.isLabelHidden}}>\n {{@label}}\n </ctrl.Label>\n {{/if}}\n\n <ComboBox\n @id={{ctrl.id}}\n @name={{@name}}\n @items={{@items}}\n @value={{@value}}\n @placeholder={{@placeholder}}\n @noResultsText={{@noResultsText}}\n @onSearch={{@onSearch}}\n @searchDebounceMs={{@searchDebounceMs}}\n @isLoading={{@isLoading}}\n @onSearchError={{@onSearchError}}\n @loadingText={{@loadingText}}\n @isCreatable={{@isCreatable}}\n @onCreate={{@onCreate}}\n @createLabel={{@createLabel}}\n @isDisabled={{@isDisabled}}\n @isInvalid={{this.isInvalid}}\n @isRequired={{@isRequired}}\n @isClearable={{@isClearable}}\n @aria-describedby={{this.getAriaDescribedBy ctrl.id}}\n @onChange={{@onChange}}\n @onBlur={{@onBlur}}\n @onFocus={{@onFocus}}\n @onOpen={{@onOpen}}\n @onClose={{@onClose}}\n data-test-combobox-field\n />\n\n {{#if @helpText}}\n <ctrl.HelpText>{{@helpText}}</ctrl.HelpText>\n {{/if}}\n\n {{#if @error}}\n <ctrl.ErrorMessage>{{@error}}</ctrl.ErrorMessage>\n {{/if}}\n </Control>\n </template>\n}\n"],"names":["ComboBoxField","Component","isInvalid","args","error","isLabelHidden","labelVisibility","getAriaDescribedBy","controlId","parts","helpText","push","length","join","undefined","setComponentTemplate","precompileTemplate","strictMode","scope","Control","ComboBox"],"mappings":";;;;;;AAkGe,MAAMA,sBAAsBC,SAAA,CAAU;EACnD,IAAIC,SAAAA,GAAqB;AACvB,IAAA,OAAO,CAAC,CAAC,IAAI,CAACC,IAAI,CAACC,KAAK;AAC1B,EAAA;EAEA,IAAIC,aAAAA,GAAyB;AAC3B,IAAA,OAAO,IAAI,CAACF,IAAI,CAACG,eAAe,KAAK,QAAA;AACvC,EAAA;AAEA;EACAC,qBAAsBC,SAAiB,IAAqB;IAC1D,MAAMC,KAAa,GAAK,EAAE;AAC1B,IAAA,IAAI,IAAI,CAACN,IAAI,CAACO,QAAQ,EAAE;AACtBD,MAAAA,KAAA,CAAME,IAAI,CAAC,CAAA,EAAGH,SAAA,YAAqB,CAAA;AACrC,IAAA;AACA,IAAA,IAAI,IAAI,CAACL,IAAI,CAACC,KAAK,EAAE;AACnBK,MAAAA,KAAA,CAAME,IAAI,CAAC,CAAA,EAAGH,SAAA,gBAAyB,CAAA;AACzC,IAAA;AACA,IAAA,OAAOC,MAAMG,MAAM,GAAG,IAAIH,KAAA,CAAMI,IAAI,CAAC,GAAA,CAAA,GAAOC,SAAA;EAC9C,CAAA;AAEA,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,81CAAA,EAyDA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,OAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}
1
+ {"version":3,"file":"combobox-field.js","sources":["../../src/form/combobox-field.gts"],"sourcesContent":["import Component from '@glimmer/component';\n\nimport Control from './control.gts';\nimport ComboBox from './combobox.gts';\nimport type { ComboBoxItems, ComboBoxOption } from './combobox-shared.ts';\n\nexport type LabelVisibility = 'visible' | 'hidden';\n\nexport interface ComboBoxFieldSignature {\n Element: HTMLDivElement;\n Args: {\n /** Label text (required). */\n label: string;\n\n /** Available options. Flat or pre-grouped. */\n items: ComboBoxItems;\n\n /** The selected value. The consumer owns it; pair with `onChange`. */\n value?: string | null;\n\n /** Fires when the selection changes. */\n onChange?: (value: string | null, option: ComboBoxOption | null) => void;\n\n /** Async search (passed through to ComboBox). */\n onSearch?: (query: string) => Promise<ComboBoxItems>;\n\n /** Debounce for `onSearch` in ms. */\n searchDebounceMs?: number;\n\n /** Force the loading state. */\n isLoading?: boolean;\n\n /** Called when an `onSearch` promise rejects. */\n onSearchError?: (error: unknown) => void;\n\n /** Loading row text. */\n loadingText?: string;\n\n /** Input placeholder. */\n placeholder?: string;\n\n /** Empty state text. */\n noResultsText?: string;\n\n /** Allow creating new options from a non-matching query. */\n isCreatable?: boolean;\n\n /** Fires when the user activates the create row. */\n onCreate?: (query: string) => void;\n\n /** Build the create-row label. */\n createLabel?: (query: string) => string;\n\n /** Help text displayed below the control. */\n helpText?: string;\n\n /** Error message (also sets invalid state). */\n error?: string;\n\n /** Field is required. */\n isRequired?: boolean;\n\n /** Field is disabled. */\n isDisabled?: boolean;\n\n /** Show the clear button when a value is set. */\n isClearable?: boolean;\n\n /** Info tooltip text shown next to the label. */\n labelInfo?: string;\n\n /** Optional indicator - shows \"optional\" or custom text next to the label. */\n optionalIndicator?: boolean | string;\n\n /** Label visibility. */\n labelVisibility?: LabelVisibility;\n\n /** Input name for forms. */\n name?: string;\n\n /** Blur event handler. */\n onBlur?: (event: FocusEvent) => void;\n\n /** Focus event handler. */\n onFocus?: (event: FocusEvent) => void;\n\n /** Fires when the dropdown opens. */\n onOpen?: () => void;\n\n /** Fires when the dropdown closes. */\n onClose?: () => void;\n };\n Blocks: {\n /** Rich tooltip content shown next to the label. */\n info: [];\n };\n}\n\nexport default class ComboBoxField extends Component<ComboBoxFieldSignature> {\n get isInvalid(): boolean {\n return !!this.args.error;\n }\n\n get isLabelHidden(): boolean {\n return this.args.labelVisibility === 'hidden';\n }\n\n /** Build aria-describedby from the control's help text and error IDs. */\n getAriaDescribedBy = (controlId: string): string | undefined => {\n const parts: string[] = [];\n if (this.args.helpText) {\n parts.push(`${controlId}-help-text`);\n }\n if (this.args.error) {\n parts.push(`${controlId}-error-message`);\n }\n return parts.length > 0 ? parts.join(' ') : undefined;\n };\n\n <template>\n <Control\n @isInvalid={{this.isInvalid}}\n @isDisabled={{@isDisabled}}\n @isRequired={{@isRequired}}\n @labelInfo={{@labelInfo}}\n @optionalIndicator={{@optionalIndicator}}\n ...attributes\n as |ctrl|\n >\n {{#if (has-block \"info\")}}\n <ctrl.Label @isVisuallyHidden={{this.isLabelHidden}}>\n <:default>{{@label}}</:default>\n <:info>{{yield to=\"info\"}}</:info>\n </ctrl.Label>\n {{else}}\n <ctrl.Label @isVisuallyHidden={{this.isLabelHidden}}>\n {{@label}}\n </ctrl.Label>\n {{/if}}\n\n <ComboBox\n @id={{ctrl.id}}\n @name={{@name}}\n @items={{@items}}\n @value={{@value}}\n @placeholder={{@placeholder}}\n @noResultsText={{@noResultsText}}\n @onSearch={{@onSearch}}\n @searchDebounceMs={{@searchDebounceMs}}\n @isLoading={{@isLoading}}\n @onSearchError={{@onSearchError}}\n @loadingText={{@loadingText}}\n @isCreatable={{@isCreatable}}\n @onCreate={{@onCreate}}\n @createLabel={{@createLabel}}\n @isDisabled={{@isDisabled}}\n @isInvalid={{this.isInvalid}}\n @isRequired={{@isRequired}}\n @isClearable={{@isClearable}}\n @aria-describedby={{this.getAriaDescribedBy ctrl.id}}\n @onChange={{@onChange}}\n @onBlur={{@onBlur}}\n @onFocus={{@onFocus}}\n @onOpen={{@onOpen}}\n @onClose={{@onClose}}\n data-test-combobox-field\n />\n\n {{#if @helpText}}\n <ctrl.HelpText>{{@helpText}}</ctrl.HelpText>\n {{/if}}\n\n {{#if @error}}\n <ctrl.ErrorMessage>{{@error}}</ctrl.ErrorMessage>\n {{/if}}\n </Control>\n </template>\n}\n"],"names":["ComboBoxField","Component","isInvalid","args","error","isLabelHidden","labelVisibility","getAriaDescribedBy","controlId","parts","helpText","push","length","join","undefined","setComponentTemplate","precompileTemplate","strictMode","scope","Control","ComboBox"],"mappings":";;;;;;AAkGe,MAAMA,sBAAsBC,SAAA,CAAU;EACnD,IAAIC,SAAAA,GAAqB;AACvB,IAAA,OAAO,CAAC,CAAC,IAAI,CAACC,IAAI,CAACC,KAAK;AAC1B,EAAA;EAEA,IAAIC,aAAAA,GAAyB;AAC3B,IAAA,OAAO,IAAI,CAACF,IAAI,CAACG,eAAe,KAAK,QAAA;AACvC,EAAA;AAEA;EACAC,qBAAsBC,SAAiB,IAAqB;IAC1D,MAAMC,KAAa,GAAK,EAAE;AAC1B,IAAA,IAAI,IAAI,CAACN,IAAI,CAACO,QAAQ,EAAE;AACtBD,MAAAA,KAAA,CAAME,IAAI,CAAC,CAAA,EAAGH,SAAA,YAAqB,CAAA;AACrC,IAAA;AACA,IAAA,IAAI,IAAI,CAACL,IAAI,CAACC,KAAK,EAAE;AACnBK,MAAAA,KAAA,CAAME,IAAI,CAAC,CAAA,EAAGH,SAAA,gBAAyB,CAAA;AACzC,IAAA;AACA,IAAA,OAAOC,MAAMG,MAAM,GAAG,IAAIH,KAAA,CAAMI,IAAI,CAAC,GAAA,CAAA,GAAOC,SAAA;EAC9C,CAAA;AAEA,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,81CAAA,EAyDA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,OAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}
@@ -23,7 +23,7 @@ class DatePickerField extends Component {
23
23
  return parts.length > 0 ? parts.join(' ') : undefined;
24
24
  };
25
25
  static {
26
- setComponentTemplate(precompileTemplate("<Control @isInvalid={{this.isInvalid}} @isDisabled={{@isDisabled}} @isRequired={{@isRequired}} @isReadOnly={{@isReadOnly}} @labelInfo={{@labelInfo}} @optionalIndicator={{@optionalIndicator}} ...attributes as |ctrl|>\n {{#if (has-block \"info\")}}\n <ctrl.Label @isVisuallyHidden={{this.isLabelHidden}}>\n <:default>{{@label}}</:default>\n <:info>{{yield to=\"info\"}}</:info>\n </ctrl.Label>\n {{else}}\n <ctrl.Label @isVisuallyHidden={{this.isLabelHidden}}>\n {{@label}}\n </ctrl.Label>\n {{/if}}\n\n <DatePicker @id={{ctrl.id}} @name={{@name}} @value={{@value}} @defaultValue={{@defaultValue}} @placeholder={{@placeholder}} @format={{@format}} @minDate={{@minDate}} @maxDate={{@maxDate}} @disabledDates={{@disabledDates}} @disabledDaysOfWeek={{@disabledDaysOfWeek}} @weekStartsOn={{@weekStartsOn}} @showOutsideDays={{@showOutsideDays}} @showToday={{@showToday}} @allowTextInput={{@allowTextInput}} @closeOnSelect={{@closeOnSelect}} @showIcon={{@showIcon}} @clearable={{@clearable}} @isDisabled={{@isDisabled}} @isInvalid={{this.isInvalid}} @isRequired={{@isRequired}} @isReadOnly={{@isReadOnly}} @aria-describedby={{this.getAriaDescribedBy ctrl.id}} @onChange={{@onChange}} @onBlur={{@onBlur}} @onFocus={{@onFocus}} @onOpenChange={{@onOpenChange}} data-test-date-picker-field />\n\n {{#if @helpText}}\n <ctrl.HelpText>{{@helpText}}</ctrl.HelpText>\n {{/if}}\n\n {{#if @error}}\n <ctrl.ErrorMessage>{{@error}}</ctrl.ErrorMessage>\n {{/if}}\n</Control>", {
26
+ setComponentTemplate(precompileTemplate("<Control @isInvalid={{this.isInvalid}} @isDisabled={{@isDisabled}} @isRequired={{@isRequired}} @isReadOnly={{@isReadOnly}} @labelInfo={{@labelInfo}} @optionalIndicator={{@optionalIndicator}} ...attributes as |ctrl|>\n {{#if (has-block \"info\")}}\n <ctrl.Label @isVisuallyHidden={{this.isLabelHidden}}>\n <:default>{{@label}}</:default>\n <:info>{{yield to=\"info\"}}</:info>\n </ctrl.Label>\n {{else}}\n <ctrl.Label @isVisuallyHidden={{this.isLabelHidden}}>\n {{@label}}\n </ctrl.Label>\n {{/if}}\n\n <DatePicker @id={{ctrl.id}} @name={{@name}} @value={{@value}} @placeholder={{@placeholder}} @format={{@format}} @minDate={{@minDate}} @maxDate={{@maxDate}} @disabledDates={{@disabledDates}} @disabledDaysOfWeek={{@disabledDaysOfWeek}} @weekStartsOn={{@weekStartsOn}} @showOutsideDays={{@showOutsideDays}} @showToday={{@showToday}} @allowTextInput={{@allowTextInput}} @closeOnSelect={{@closeOnSelect}} @showIcon={{@showIcon}} @clearable={{@clearable}} @isDisabled={{@isDisabled}} @isInvalid={{this.isInvalid}} @isRequired={{@isRequired}} @isReadOnly={{@isReadOnly}} @aria-describedby={{this.getAriaDescribedBy ctrl.id}} @onChange={{@onChange}} @onBlur={{@onBlur}} @onFocus={{@onFocus}} @onOpenChange={{@onOpenChange}} data-test-date-picker-field />\n\n {{#if @helpText}}\n <ctrl.HelpText>{{@helpText}}</ctrl.HelpText>\n {{/if}}\n\n {{#if @error}}\n <ctrl.ErrorMessage>{{@error}}</ctrl.ErrorMessage>\n {{/if}}\n</Control>", {
27
27
  strictMode: true,
28
28
  scope: () => ({
29
29
  Control,
@@ -1 +1 @@
1
- {"version":3,"file":"date-picker-field.js","sources":["../../src/form/date-picker-field.gts"],"sourcesContent":["import Component from '@glimmer/component';\n\nimport Control from './control.gts';\nimport DatePicker, { type DateFormat } from './date-picker.gts';\n\nexport type LabelVisibility = 'visible' | 'hidden';\n\nexport interface DatePickerFieldSignature {\n Element: HTMLDivElement;\n Args: {\n /** Label text (required) */\n label: string;\n\n /** Current selected date value */\n value?: Date | null;\n\n /** Default value (uncontrolled) */\n defaultValue?: Date | null;\n\n /** Placeholder text shown when no value is selected */\n placeholder?: string;\n\n /** Date format for display and parsing (date-fns format) */\n format?: DateFormat;\n\n /** Minimum allowed date */\n minDate?: Date;\n\n /** Maximum allowed date */\n maxDate?: Date;\n\n /** Array of disabled dates or a function to check if a date is disabled */\n disabledDates?: Date[] | ((date: Date) => boolean);\n\n /** Array of day numbers (0-6, Sunday-Saturday) to disable */\n disabledDaysOfWeek?: number[];\n\n /** Day to start the week on (0 = Sunday, 1 = Monday, etc.) */\n weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;\n\n /** Show days from adjacent months */\n showOutsideDays?: boolean;\n\n /** Show today indicator */\n showToday?: boolean;\n\n /** Allow typing in the input */\n allowTextInput?: boolean;\n\n /** Close dropdown when a date is selected */\n closeOnSelect?: boolean;\n\n /** Show calendar icon */\n showIcon?: boolean;\n\n /** Show clear button when value is selected */\n clearable?: boolean;\n\n /** Help text displayed below the input */\n helpText?: string;\n\n /** Error message (also sets invalid state) */\n error?: string;\n\n /** Field is required */\n isRequired?: boolean;\n\n /** Field is disabled */\n isDisabled?: boolean;\n\n /** Field is read-only */\n isReadOnly?: boolean;\n\n /** Info tooltip text shown next to the label */\n labelInfo?: string;\n\n /** Optional indicator - shows \"optional\" or custom text next to the label */\n optionalIndicator?: boolean | string;\n\n /** Label visibility */\n labelVisibility?: LabelVisibility;\n\n /** Input name for forms */\n name?: string;\n\n /** Change event handler */\n onChange?: (value: Date | null) => void;\n\n /** Blur event handler */\n onBlur?: (event: FocusEvent) => void;\n\n /** Focus event handler */\n onFocus?: (event: FocusEvent) => void;\n\n /** Open state change handler */\n onOpenChange?: (open: boolean) => void;\n };\n Blocks: {\n /** Rich tooltip content shown next to the label */\n info: [];\n };\n}\n\nexport default class DatePickerField extends Component<DatePickerFieldSignature> {\n get isInvalid(): boolean {\n return !!this.args.error;\n }\n\n get isLabelHidden(): boolean {\n return this.args.labelVisibility === 'hidden';\n }\n\n /** Build aria-describedby from the control's help text and error message IDs */\n getAriaDescribedBy = (controlId: string): string | undefined => {\n const parts: string[] = [];\n if (this.args.helpText) {\n parts.push(`${controlId}-help-text`);\n }\n if (this.args.error) {\n parts.push(`${controlId}-error-message`);\n }\n return parts.length > 0 ? parts.join(' ') : undefined;\n };\n\n <template>\n <Control\n @isInvalid={{this.isInvalid}}\n @isDisabled={{@isDisabled}}\n @isRequired={{@isRequired}}\n @isReadOnly={{@isReadOnly}}\n @labelInfo={{@labelInfo}}\n @optionalIndicator={{@optionalIndicator}}\n ...attributes\n as |ctrl|\n >\n {{#if (has-block \"info\")}}\n <ctrl.Label @isVisuallyHidden={{this.isLabelHidden}}>\n <:default>{{@label}}</:default>\n <:info>{{yield to=\"info\"}}</:info>\n </ctrl.Label>\n {{else}}\n <ctrl.Label @isVisuallyHidden={{this.isLabelHidden}}>\n {{@label}}\n </ctrl.Label>\n {{/if}}\n\n <DatePicker\n @id={{ctrl.id}}\n @name={{@name}}\n @value={{@value}}\n @defaultValue={{@defaultValue}}\n @placeholder={{@placeholder}}\n @format={{@format}}\n @minDate={{@minDate}}\n @maxDate={{@maxDate}}\n @disabledDates={{@disabledDates}}\n @disabledDaysOfWeek={{@disabledDaysOfWeek}}\n @weekStartsOn={{@weekStartsOn}}\n @showOutsideDays={{@showOutsideDays}}\n @showToday={{@showToday}}\n @allowTextInput={{@allowTextInput}}\n @closeOnSelect={{@closeOnSelect}}\n @showIcon={{@showIcon}}\n @clearable={{@clearable}}\n @isDisabled={{@isDisabled}}\n @isInvalid={{this.isInvalid}}\n @isRequired={{@isRequired}}\n @isReadOnly={{@isReadOnly}}\n @aria-describedby={{this.getAriaDescribedBy ctrl.id}}\n @onChange={{@onChange}}\n @onBlur={{@onBlur}}\n @onFocus={{@onFocus}}\n @onOpenChange={{@onOpenChange}}\n data-test-date-picker-field\n />\n\n {{#if @helpText}}\n <ctrl.HelpText>{{@helpText}}</ctrl.HelpText>\n {{/if}}\n\n {{#if @error}}\n <ctrl.ErrorMessage>{{@error}}</ctrl.ErrorMessage>\n {{/if}}\n </Control>\n </template>\n}\n"],"names":["DatePickerField","Component","isInvalid","args","error","isLabelHidden","labelVisibility","getAriaDescribedBy","controlId","parts","helpText","push","length","join","undefined","setComponentTemplate","precompileTemplate","strictMode","scope","Control","DatePicker"],"mappings":";;;;;;AAuGe,MAAMA,wBAAwBC,SAAA,CAAU;EACrD,IAAIC,SAAAA,GAAqB;AACvB,IAAA,OAAO,CAAC,CAAC,IAAI,CAACC,IAAI,CAACC,KAAK;AAC1B,EAAA;EAEA,IAAIC,aAAAA,GAAyB;AAC3B,IAAA,OAAO,IAAI,CAACF,IAAI,CAACG,eAAe,KAAK,QAAA;AACvC,EAAA;AAEA;EACAC,qBAAsBC,SAAiB,IAAqB;IAC1D,MAAMC,KAAa,GAAK,EAAE;AAC1B,IAAA,IAAI,IAAI,CAACN,IAAI,CAACO,QAAQ,EAAE;AACtBD,MAAAA,KAAA,CAAME,IAAI,CAAC,CAAA,EAAGH,SAAA,YAAqB,CAAA;AACrC,IAAA;AACA,IAAA,IAAI,IAAI,CAACL,IAAI,CAACC,KAAK,EAAE;AACnBK,MAAAA,KAAA,CAAME,IAAI,CAAC,CAAA,EAAGH,SAAA,gBAAyB,CAAA;AACzC,IAAA;AACA,IAAA,OAAOC,MAAMG,MAAM,GAAG,IAAIH,KAAA,CAAMI,IAAI,CAAC,GAAA,CAAA,GAAOC,SAAA;EAC9C,CAAA;AAEA,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,u9CAAA,EA4DA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,OAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}
1
+ {"version":3,"file":"date-picker-field.js","sources":["../../src/form/date-picker-field.gts"],"sourcesContent":["import Component from '@glimmer/component';\n\nimport Control from './control.gts';\nimport DatePicker, { type DateFormat } from './date-picker.gts';\n\nexport type LabelVisibility = 'visible' | 'hidden';\n\nexport interface DatePickerFieldSignature {\n Element: HTMLDivElement;\n Args: {\n /** Label text (required) */\n label: string;\n\n /**\n * Selected date. The consumer owns it: pair with `onChange` to track edits.\n * Optional because a date picker can be empty.\n */\n value?: Date | null;\n\n /** Placeholder text shown when no value is selected */\n placeholder?: string;\n\n /** Date format for display and parsing (date-fns format) */\n format?: DateFormat;\n\n /** Minimum allowed date */\n minDate?: Date;\n\n /** Maximum allowed date */\n maxDate?: Date;\n\n /** Array of disabled dates or a function to check if a date is disabled */\n disabledDates?: Date[] | ((date: Date) => boolean);\n\n /** Array of day numbers (0-6, Sunday-Saturday) to disable */\n disabledDaysOfWeek?: number[];\n\n /** Day to start the week on (0 = Sunday, 1 = Monday, etc.) */\n weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;\n\n /** Show days from adjacent months */\n showOutsideDays?: boolean;\n\n /** Show today indicator */\n showToday?: boolean;\n\n /** Allow typing in the input */\n allowTextInput?: boolean;\n\n /** Close dropdown when a date is selected */\n closeOnSelect?: boolean;\n\n /** Show calendar icon */\n showIcon?: boolean;\n\n /** Show clear button when value is selected */\n clearable?: boolean;\n\n /** Help text displayed below the input */\n helpText?: string;\n\n /** Error message (also sets invalid state) */\n error?: string;\n\n /** Field is required */\n isRequired?: boolean;\n\n /** Field is disabled */\n isDisabled?: boolean;\n\n /** Field is read-only */\n isReadOnly?: boolean;\n\n /** Info tooltip text shown next to the label */\n labelInfo?: string;\n\n /** Optional indicator - shows \"optional\" or custom text next to the label */\n optionalIndicator?: boolean | string;\n\n /** Label visibility */\n labelVisibility?: LabelVisibility;\n\n /** Input name for forms */\n name?: string;\n\n /** Change event handler */\n onChange?: (value: Date | null) => void;\n\n /** Blur event handler */\n onBlur?: (event: FocusEvent) => void;\n\n /** Focus event handler */\n onFocus?: (event: FocusEvent) => void;\n\n /** Open state change handler */\n onOpenChange?: (open: boolean) => void;\n };\n Blocks: {\n /** Rich tooltip content shown next to the label */\n info: [];\n };\n}\n\nexport default class DatePickerField extends Component<DatePickerFieldSignature> {\n get isInvalid(): boolean {\n return !!this.args.error;\n }\n\n get isLabelHidden(): boolean {\n return this.args.labelVisibility === 'hidden';\n }\n\n /** Build aria-describedby from the control's help text and error message IDs */\n getAriaDescribedBy = (controlId: string): string | undefined => {\n const parts: string[] = [];\n if (this.args.helpText) {\n parts.push(`${controlId}-help-text`);\n }\n if (this.args.error) {\n parts.push(`${controlId}-error-message`);\n }\n return parts.length > 0 ? parts.join(' ') : undefined;\n };\n\n <template>\n <Control\n @isInvalid={{this.isInvalid}}\n @isDisabled={{@isDisabled}}\n @isRequired={{@isRequired}}\n @isReadOnly={{@isReadOnly}}\n @labelInfo={{@labelInfo}}\n @optionalIndicator={{@optionalIndicator}}\n ...attributes\n as |ctrl|\n >\n {{#if (has-block \"info\")}}\n <ctrl.Label @isVisuallyHidden={{this.isLabelHidden}}>\n <:default>{{@label}}</:default>\n <:info>{{yield to=\"info\"}}</:info>\n </ctrl.Label>\n {{else}}\n <ctrl.Label @isVisuallyHidden={{this.isLabelHidden}}>\n {{@label}}\n </ctrl.Label>\n {{/if}}\n\n <DatePicker\n @id={{ctrl.id}}\n @name={{@name}}\n @value={{@value}}\n @placeholder={{@placeholder}}\n @format={{@format}}\n @minDate={{@minDate}}\n @maxDate={{@maxDate}}\n @disabledDates={{@disabledDates}}\n @disabledDaysOfWeek={{@disabledDaysOfWeek}}\n @weekStartsOn={{@weekStartsOn}}\n @showOutsideDays={{@showOutsideDays}}\n @showToday={{@showToday}}\n @allowTextInput={{@allowTextInput}}\n @closeOnSelect={{@closeOnSelect}}\n @showIcon={{@showIcon}}\n @clearable={{@clearable}}\n @isDisabled={{@isDisabled}}\n @isInvalid={{this.isInvalid}}\n @isRequired={{@isRequired}}\n @isReadOnly={{@isReadOnly}}\n @aria-describedby={{this.getAriaDescribedBy ctrl.id}}\n @onChange={{@onChange}}\n @onBlur={{@onBlur}}\n @onFocus={{@onFocus}}\n @onOpenChange={{@onOpenChange}}\n data-test-date-picker-field\n />\n\n {{#if @helpText}}\n <ctrl.HelpText>{{@helpText}}</ctrl.HelpText>\n {{/if}}\n\n {{#if @error}}\n <ctrl.ErrorMessage>{{@error}}</ctrl.ErrorMessage>\n {{/if}}\n </Control>\n </template>\n}\n"],"names":["DatePickerField","Component","isInvalid","args","error","isLabelHidden","labelVisibility","getAriaDescribedBy","controlId","parts","helpText","push","length","join","undefined","setComponentTemplate","precompileTemplate","strictMode","scope","Control","DatePicker"],"mappings":";;;;;;AAuGe,MAAMA,wBAAwBC,SAAA,CAAU;EACrD,IAAIC,SAAAA,GAAqB;AACvB,IAAA,OAAO,CAAC,CAAC,IAAI,CAACC,IAAI,CAACC,KAAK;AAC1B,EAAA;EAEA,IAAIC,aAAAA,GAAyB;AAC3B,IAAA,OAAO,IAAI,CAACF,IAAI,CAACG,eAAe,KAAK,QAAA;AACvC,EAAA;AAEA;EACAC,qBAAsBC,SAAiB,IAAqB;IAC1D,MAAMC,KAAa,GAAK,EAAE;AAC1B,IAAA,IAAI,IAAI,CAACN,IAAI,CAACO,QAAQ,EAAE;AACtBD,MAAAA,KAAA,CAAME,IAAI,CAAC,CAAA,EAAGH,SAAA,YAAqB,CAAA;AACrC,IAAA;AACA,IAAA,IAAI,IAAI,CAACL,IAAI,CAACC,KAAK,EAAE;AACnBK,MAAAA,KAAA,CAAME,IAAI,CAAC,CAAA,EAAGH,SAAA,gBAAyB,CAAA;AACzC,IAAA;AACA,IAAA,OAAOC,MAAMG,MAAM,GAAG,IAAIH,KAAA,CAAMI,IAAI,CAAC,GAAA,CAAA,GAAOC,SAAA;EAC9C,CAAA;AAEA,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,u7CAAA,EA2DA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,OAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}
@@ -70,18 +70,12 @@ class DatePicker extends Component {
70
70
  });
71
71
  }
72
72
  #inputValue = (i(this, "inputValue"), void 0);
73
- static {
74
- g(this.prototype, "internalValue", [tracked], function () {
75
- return null;
76
- });
77
- }
78
- #internalValue = (i(this, "internalValue"), void 0);
79
- static {
80
- g(this.prototype, "hasInternalValueChanged", [tracked], function () {
81
- return false;
82
- });
83
- }
84
- #hasInternalValueChanged = (i(this, "hasInternalValueChanged"), void 0);
73
+ /**
74
+ * In-progress typed date string (e.g. "15/06" before it parses to a Date).
75
+ * This is ephemeral UI state the picker owns, not the consumer's selection:
76
+ * it holds exactly what is being typed until the value parses and commits up
77
+ * via `onChange`.
78
+ */
85
79
  static {
86
80
  g(this.prototype, "skipInputReset", [tracked], function () {
87
81
  return false;
@@ -118,19 +112,9 @@ class DatePicker extends Component {
118
112
  get showToday() {
119
113
  return this.args.showToday ?? true;
120
114
  }
121
- /** Check if component is controlled */
122
- get isControlled() {
123
- return this.args.value !== undefined;
124
- }
125
- /** Get current selected value (controlled or uncontrolled) */
115
+ /** The selected date, owned by the consumer via `@value`. */
126
116
  get selectedValue() {
127
- if (this.isControlled) {
128
- return this.args.value ?? null;
129
- }
130
- if (this.args.defaultValue !== undefined && !this.hasInternalValueChanged) {
131
- return this.args.defaultValue;
132
- }
133
- return this.internalValue;
117
+ return this.args.value ?? null;
134
118
  }
135
119
  /** Value for Calendar component (converts null to undefined) */
136
120
  get calendarValue() {
@@ -307,11 +291,9 @@ class DatePicker extends Component {
307
291
  // Methods
308
292
  // ============================================================================
309
293
  setValue(date) {
310
- if (!this.isControlled) {
311
- this.hasInternalValueChanged = true;
312
- this.internalValue = date;
313
- }
314
294
  this.args.onChange?.(date);
295
+ // Reflect the committed value in the input straight away, ahead of the
296
+ // parent's @value round-trip.
315
297
  this.inputValue = formatDate(date, this.dateFormat);
316
298
  }
317
299
  openDropdown = () => {
@@ -23,7 +23,7 @@ class DateRangePickerField extends Component {
23
23
  return parts.length > 0 ? parts.join(' ') : undefined;
24
24
  };
25
25
  static {
26
- setComponentTemplate(precompileTemplate("<Control @isInvalid={{this.isInvalid}} @isDisabled={{@isDisabled}} @isRequired={{@isRequired}} @isReadOnly={{@isReadOnly}} @labelInfo={{@labelInfo}} @optionalIndicator={{@optionalIndicator}} ...attributes as |ctrl|>\n {{#if (has-block \"info\")}}\n <ctrl.Label @isVisuallyHidden={{this.isLabelHidden}}>\n <:default>{{@label}}</:default>\n <:info>{{yield to=\"info\"}}</:info>\n </ctrl.Label>\n {{else}}\n <ctrl.Label @isVisuallyHidden={{this.isLabelHidden}}>\n {{@label}}\n </ctrl.Label>\n {{/if}}\n\n <DateRangePicker @id={{ctrl.id}} @name={{@name}} @value={{@value}} @defaultValue={{@defaultValue}} @startPlaceholder={{@startPlaceholder}} @endPlaceholder={{@endPlaceholder}} @format={{@format}} @minDate={{@minDate}} @maxDate={{@maxDate}} @disabledDates={{@disabledDates}} @disabledDaysOfWeek={{@disabledDaysOfWeek}} @weekStartsOn={{@weekStartsOn}} @showOutsideDays={{@showOutsideDays}} @showToday={{@showToday}} @presets={{@presets}} @allowTextInput={{@allowTextInput}} @showIcon={{@showIcon}} @clearable={{@clearable}} @isDisabled={{@isDisabled}} @isInvalid={{this.isInvalid}} @isRequired={{@isRequired}} @isReadOnly={{@isReadOnly}} @aria-describedby={{this.getAriaDescribedBy ctrl.id}} @onChange={{@onChange}} @onBlur={{@onBlur}} @onFocus={{@onFocus}} @onOpenChange={{@onOpenChange}} data-test-date-range-picker-field />\n\n {{#if @helpText}}\n <ctrl.HelpText>{{@helpText}}</ctrl.HelpText>\n {{/if}}\n\n {{#if @error}}\n <ctrl.ErrorMessage>{{@error}}</ctrl.ErrorMessage>\n {{/if}}\n</Control>", {
26
+ setComponentTemplate(precompileTemplate("<Control @isInvalid={{this.isInvalid}} @isDisabled={{@isDisabled}} @isRequired={{@isRequired}} @isReadOnly={{@isReadOnly}} @labelInfo={{@labelInfo}} @optionalIndicator={{@optionalIndicator}} ...attributes as |ctrl|>\n {{#if (has-block \"info\")}}\n <ctrl.Label @isVisuallyHidden={{this.isLabelHidden}}>\n <:default>{{@label}}</:default>\n <:info>{{yield to=\"info\"}}</:info>\n </ctrl.Label>\n {{else}}\n <ctrl.Label @isVisuallyHidden={{this.isLabelHidden}}>\n {{@label}}\n </ctrl.Label>\n {{/if}}\n\n <DateRangePicker @id={{ctrl.id}} @name={{@name}} @value={{@value}} @startPlaceholder={{@startPlaceholder}} @endPlaceholder={{@endPlaceholder}} @format={{@format}} @minDate={{@minDate}} @maxDate={{@maxDate}} @disabledDates={{@disabledDates}} @disabledDaysOfWeek={{@disabledDaysOfWeek}} @weekStartsOn={{@weekStartsOn}} @showOutsideDays={{@showOutsideDays}} @showToday={{@showToday}} @presets={{@presets}} @allowTextInput={{@allowTextInput}} @showIcon={{@showIcon}} @clearable={{@clearable}} @isDisabled={{@isDisabled}} @isInvalid={{this.isInvalid}} @isRequired={{@isRequired}} @isReadOnly={{@isReadOnly}} @aria-describedby={{this.getAriaDescribedBy ctrl.id}} @onChange={{@onChange}} @onBlur={{@onBlur}} @onFocus={{@onFocus}} @onOpenChange={{@onOpenChange}} data-test-date-range-picker-field />\n\n {{#if @helpText}}\n <ctrl.HelpText>{{@helpText}}</ctrl.HelpText>\n {{/if}}\n\n {{#if @error}}\n <ctrl.ErrorMessage>{{@error}}</ctrl.ErrorMessage>\n {{/if}}\n</Control>", {
27
27
  strictMode: true,
28
28
  scope: () => ({
29
29
  Control,