@visitwonders/assembly 0.16.1 → 0.18.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 (80) 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 +12 -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.css +51 -3
  56. package/dist/data/table.js +14 -71
  57. package/dist/form/calendar.js +29 -57
  58. package/dist/form/checkbox-group.js +3 -23
  59. package/dist/form/checkbox.js +3 -17
  60. package/dist/form/combobox-field.js.map +1 -1
  61. package/dist/form/date-picker-field.js +1 -1
  62. package/dist/form/date-picker-field.js.map +1 -1
  63. package/dist/form/date-picker.js +10 -28
  64. package/dist/form/date-range-picker-field.js +1 -1
  65. package/dist/form/date-range-picker-field.js.map +1 -1
  66. package/dist/form/date-range-picker.js +8 -31
  67. package/dist/form/input.js +1 -6
  68. package/dist/form/money-field.js +23 -22
  69. package/dist/form/multi-combobox-field.js.map +1 -1
  70. package/dist/form/radio.js +3 -4
  71. package/dist/form/search-input.js +6 -9
  72. package/dist/form/text-field.js +3 -22
  73. package/dist/form/textarea.js +2 -17
  74. package/dist/form/toggle-field.js +1 -1
  75. package/dist/form/toggle.js +4 -18
  76. package/dist/overlay/drawer.js +6 -26
  77. package/dist/overlay/modal.js +5 -29
  78. package/dist/overlay/popover.js +15 -16
  79. package/dist/overlay/tooltip.js +14 -16
  80. package/package.json +16 -17
@@ -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,
@@ -1 +1 @@
1
- {"version":3,"file":"date-range-picker-field.js","sources":["../../src/form/date-range-picker-field.gts"],"sourcesContent":["import Component from '@glimmer/component';\n\nimport Control from './control.gts';\nimport DateRangePicker, {\n type DateFormat,\n type DateRangePreset,\n type DateRange,\n} from './date-range-picker.gts';\n\nexport type LabelVisibility = 'visible' | 'hidden';\n\nexport interface DateRangePickerFieldSignature {\n Element: HTMLDivElement;\n Args: {\n /** Label text (required) */\n label: string;\n\n /** Current selected date range value */\n value?: DateRange | null;\n\n /** Default value (uncontrolled) */\n defaultValue?: DateRange | null;\n\n /** Placeholder text for start date */\n startPlaceholder?: string;\n\n /** Placeholder text for end date */\n endPlaceholder?: 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 /** Preset date range options */\n presets?: DateRangePreset[];\n\n /** Allow typing in the inputs */\n allowTextInput?: 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 prefix for forms (creates {name}_start and {name}_end) */\n name?: string;\n\n /** Change event handler */\n onChange?: (value: DateRange | 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 DateRangePickerField extends Component<DateRangePickerFieldSignature> {\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 <DateRangePicker\n @id={{ctrl.id}}\n @name={{@name}}\n @value={{@value}}\n @defaultValue={{@defaultValue}}\n @startPlaceholder={{@startPlaceholder}}\n @endPlaceholder={{@endPlaceholder}}\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 @presets={{@presets}}\n @allowTextInput={{@allowTextInput}}\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-range-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":["DateRangePickerField","Component","isInvalid","args","error","isLabelHidden","labelVisibility","getAriaDescribedBy","controlId","parts","helpText","push","length","join","undefined","setComponentTemplate","precompileTemplate","strictMode","scope","Control","DateRangePicker"],"mappings":";;;;;;AA8Ge,MAAMA,6BAA6BC,SAAA,CAAU;EAC1D,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,ogDAAA,EA6DA;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-range-picker-field.js","sources":["../../src/form/date-range-picker-field.gts"],"sourcesContent":["import Component from '@glimmer/component';\n\nimport Control from './control.gts';\nimport DateRangePicker, {\n type DateFormat,\n type DateRangePreset,\n type DateRange,\n} from './date-range-picker.gts';\n\nexport type LabelVisibility = 'visible' | 'hidden';\n\nexport interface DateRangePickerFieldSignature {\n Element: HTMLDivElement;\n Args: {\n /** Label text (required) */\n label: string;\n\n /**\n * Selected date range. The consumer owns it: pair with `onChange` to track\n * edits. Optional because the range can be empty.\n */\n value?: DateRange | null;\n\n /** Placeholder text for start date */\n startPlaceholder?: string;\n\n /** Placeholder text for end date */\n endPlaceholder?: 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 /** Preset date range options */\n presets?: DateRangePreset[];\n\n /** Allow typing in the inputs */\n allowTextInput?: 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 prefix for forms (creates {name}_start and {name}_end) */\n name?: string;\n\n /** Change event handler */\n onChange?: (value: DateRange | 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 DateRangePickerField extends Component<DateRangePickerFieldSignature> {\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 <DateRangePicker\n @id={{ctrl.id}}\n @name={{@name}}\n @value={{@value}}\n @startPlaceholder={{@startPlaceholder}}\n @endPlaceholder={{@endPlaceholder}}\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 @presets={{@presets}}\n @allowTextInput={{@allowTextInput}}\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-range-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":["DateRangePickerField","Component","isInvalid","args","error","isLabelHidden","labelVisibility","getAriaDescribedBy","controlId","parts","helpText","push","length","join","undefined","setComponentTemplate","precompileTemplate","strictMode","scope","Control","DateRangePicker"],"mappings":";;;;;;AA8Ge,MAAMA,6BAA6BC,SAAA,CAAU;EAC1D,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,o+CAAA,EA4DA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,OAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}
@@ -40,7 +40,9 @@ class DateRangePicker extends Component {
40
40
  return '';
41
41
  });
42
42
  }
43
- #startInputValue = (i(this, "startInputValue"), void 0);
43
+ #startInputValue = (i(this, "startInputValue"), void 0); // In-progress typed strings for each input, ephemeral UI state the picker
44
+ // owns. They hold exactly what is being typed until the range parses and
45
+ // commits up via `onChange`.
44
46
  static {
45
47
  g(this.prototype, "endInputValue", [tracked], function () {
46
48
  return '';
@@ -53,18 +55,6 @@ class DateRangePicker extends Component {
53
55
  });
54
56
  }
55
57
  #activeInput = (i(this, "activeInput"), void 0);
56
- static {
57
- g(this.prototype, "internalRangeValue", [tracked], function () {
58
- return null;
59
- });
60
- }
61
- #internalRangeValue = (i(this, "internalRangeValue"), void 0);
62
- static {
63
- g(this.prototype, "hasInternalValueChanged", [tracked], function () {
64
- return false;
65
- });
66
- }
67
- #hasInternalValueChanged = (i(this, "hasInternalValueChanged"), void 0);
68
58
  calendarId = `date-range-picker-calendar-${++dateRangePickerIdCounter}`;
69
59
  startInputElement = null;
70
60
  endInputElement = null;
@@ -101,19 +91,9 @@ class DateRangePicker extends Component {
101
91
  get hasPresets() {
102
92
  return !!this.args.presets && this.args.presets.length > 0;
103
93
  }
104
- /** Check if component is controlled */
105
- get isControlled() {
106
- return this.args.value !== undefined;
107
- }
108
- /** Get current selected range (controlled or uncontrolled) */
94
+ /** The selected range, owned by the consumer via `@value`. */
109
95
  get selectedRange() {
110
- if (this.isControlled) {
111
- return this.args.value ?? null;
112
- }
113
- if (this.args.defaultValue !== undefined && !this.hasInternalValueChanged) {
114
- return this.args.defaultValue ?? null;
115
- }
116
- return this.internalRangeValue;
96
+ return this.args.value ?? null;
117
97
  }
118
98
  /** Value for Calendar component (converts null to undefined) */
119
99
  get calendarRangeValue() {
@@ -379,14 +359,11 @@ class DateRangePicker extends Component {
379
359
  // ============================================================================
380
360
  // Methods
381
361
  // ============================================================================
382
- /** Set the range value (handles both controlled and uncontrolled mode) */
362
+ /** Commit a range up to the consumer and refresh the inputs to match. */
383
363
  setRange(range) {
384
- if (!this.isControlled) {
385
- this.hasInternalValueChanged = true;
386
- this.internalRangeValue = range;
387
- }
388
364
  this.args.onChange?.(range);
389
- // Update input values
365
+ // Reflect the committed range in the inputs straight away, ahead of the
366
+ // parent's @value round-trip.
390
367
  if (range) {
391
368
  this.startInputValue = formatDate(range.start, this.dateFormat);
392
369
  this.endInputValue = formatDate(range.end, this.dateFormat);
@@ -22,18 +22,13 @@ class Input extends Component {
22
22
  get isFullWidth() {
23
23
  return this.args.isFullWidth ?? true;
24
24
  }
25
- get inputValue() {
26
- // Controlled mode: use value prop
27
- // Uncontrolled mode: use defaultValue for initial render
28
- return this.args.value ?? this.args.defaultValue;
29
- }
30
25
  autoFocusModifier = modifier(element => {
31
26
  if (this.args.autoFocus) {
32
27
  element.focus();
33
28
  }
34
29
  });
35
30
  static {
36
- setComponentTemplate(precompileTemplate("<div class=\"input-wrapper_ef268a5e9\" data-size={{this.size}} data-variant={{this.variant}} data-disabled={{if @isDisabled \"true\"}} data-readonly={{if @isReadOnly \"true\"}} data-invalid={{if @isInvalid \"true\"}} data-full-width={{if this.isFullWidth \"true\" \"false\"}} data-test-input-wrapper>\n {{#if (has-block \"prefix\")}}\n <span class=\"input-prefix_ef268a5e9\" data-test-input-prefix>{{yield to=\"prefix\"}}</span>\n {{else if @prefix}}\n <span class=\"input-prefix_ef268a5e9\" data-test-input-prefix>{{@prefix}}</span>\n {{/if}}\n\n <input class=\"input_ef268a5e9\" id={{@id}} type={{this.type}} name={{@name}} value={{this.inputValue}} placeholder={{@placeholder}} disabled={{@isDisabled}} readonly={{@isReadOnly}} required={{@isRequired}} autocomplete={{this.autoComplete}} inputmode={{@inputMode}} min={{@min}} max={{@max}} step={{@step}} pattern={{@pattern}} maxlength={{@maxLength}} aria-invalid={{if @isInvalid \"true\"}} aria-required={{if @isRequired \"true\"}} aria-describedby={{@aria-describedby}} {{this.autoFocusModifier}} data-test-input ...attributes />\n\n {{#if (has-block \"suffix\")}}\n <span class=\"input-suffix_ef268a5e9\" data-test-input-suffix>{{yield to=\"suffix\"}}</span>\n {{else if @suffix}}\n <span class=\"input-suffix_ef268a5e9\" data-test-input-suffix>{{@suffix}}</span>\n {{/if}}\n</div>", {
31
+ setComponentTemplate(precompileTemplate("<div class=\"input-wrapper_ef268a5e9\" data-size={{this.size}} data-variant={{this.variant}} data-disabled={{if @isDisabled \"true\"}} data-readonly={{if @isReadOnly \"true\"}} data-invalid={{if @isInvalid \"true\"}} data-full-width={{if this.isFullWidth \"true\" \"false\"}} data-test-input-wrapper>\n {{#if (has-block \"prefix\")}}\n <span class=\"input-prefix_ef268a5e9\" data-test-input-prefix>{{yield to=\"prefix\"}}</span>\n {{else if @prefix}}\n <span class=\"input-prefix_ef268a5e9\" data-test-input-prefix>{{@prefix}}</span>\n {{/if}}\n\n <input class=\"input_ef268a5e9\" id={{@id}} type={{this.type}} name={{@name}} value={{@value}} placeholder={{@placeholder}} disabled={{@isDisabled}} readonly={{@isReadOnly}} required={{@isRequired}} autocomplete={{this.autoComplete}} inputmode={{@inputMode}} min={{@min}} max={{@max}} step={{@step}} pattern={{@pattern}} maxlength={{@maxLength}} aria-invalid={{if @isInvalid \"true\"}} aria-required={{if @isRequired \"true\"}} aria-describedby={{@aria-describedby}} {{this.autoFocusModifier}} data-test-input ...attributes />\n\n {{#if (has-block \"suffix\")}}\n <span class=\"input-suffix_ef268a5e9\" data-test-input-suffix>{{yield to=\"suffix\"}}</span>\n {{else if @suffix}}\n <span class=\"input-suffix_ef268a5e9\" data-test-input-suffix>{{@suffix}}</span>\n {{/if}}\n</div>", {
37
32
  strictMode: true
38
33
  }), this);
39
34
  }
@@ -40,11 +40,18 @@ const CURRENCY_CONFIG = {
40
40
  };
41
41
  class MoneyField extends Component {
42
42
  static {
43
- g(this.prototype, "internalDisplayValue", [tracked], function () {
44
- return '';
43
+ g(this.prototype, "displayBuffer", [tracked], function () {
44
+ return null;
45
45
  });
46
46
  }
47
- #internalDisplayValue = (i(this, "internalDisplayValue"), void 0);
47
+ #displayBuffer = (i(this, "displayBuffer"), void 0);
48
+ /**
49
+ * In-progress typing buffer: the raw string the user is mid-typing, e.g. "12."
50
+ * before it parses to a number. This is ephemeral UI state the component owns,
51
+ * not the consumer's domain value. It is `null` whenever the field is not being
52
+ * edited, in which case the display formats `@value` instead. It is set on each
53
+ * keystroke and cleared on blur, once the parsed value commits up via `onChange`.
54
+ */
48
55
  get currencyConfig() {
49
56
  return CURRENCY_CONFIG[this.args.currency] ?? {
50
57
  symbol: this.args.currency,
@@ -73,14 +80,11 @@ class MoneyField extends Component {
73
80
  get isInvalid() {
74
81
  return !!this.args.error;
75
82
  }
76
- get isControlled() {
77
- return this.args.value !== undefined;
78
- }
79
83
  get displayValue() {
80
- if (this.isControlled) {
81
- return this.formatMinorToDisplay(this.args.value);
82
- }
83
- return this.internalDisplayValue;
84
+ // While editing, the buffer is authoritative so half-typed strings ("12.")
85
+ // survive keystrokes. Otherwise the display is the consumer's committed
86
+ // value, formatted.
87
+ return this.displayBuffer ?? this.formatMinorToDisplay(this.args.value);
84
88
  }
85
89
  get placeholder() {
86
90
  if (this.args.placeholder) {
@@ -146,9 +150,9 @@ class MoneyField extends Component {
146
150
  return;
147
151
  }
148
152
  }
149
- if (!this.isControlled) {
150
- this.internalDisplayValue = rawValue;
151
- }
153
+ // Hold the raw keystrokes in the ephemeral buffer so the field shows exactly
154
+ // what is being typed; the committed value still flows up through onInput.
155
+ this.displayBuffer = rawValue;
152
156
  const minorValue = this.parseDisplayToMinor(rawValue);
153
157
  this.args.onInput?.(minorValue, event);
154
158
  }
@@ -158,21 +162,18 @@ class MoneyField extends Component {
158
162
  handleBlur(event) {
159
163
  const input = event.target;
160
164
  const minorValue = this.parseDisplayToMinor(input.value);
165
+ // Editing is over, so drop the typing buffer: from here the display formats
166
+ // @value. The parent updates @value in response to onChange, which the
167
+ // displayValue getter then reflects on the next render.
168
+ this.displayBuffer = null;
161
169
  if (minorValue !== null) {
162
170
  const clampedValue = this.clampValue(minorValue);
163
171
  const formattedDisplay = this.formatMinorToDisplay(clampedValue);
164
- if (!this.isControlled) {
165
- this.internalDisplayValue = formattedDisplay;
166
- }
167
- // Set DOM value directly for immediate visual feedback of formatted value.
168
- // In controlled mode, the parent should update @value in response to onChange,
169
- // which will sync the displayValue getter on the next render.
172
+ // Set the DOM value directly for immediate feedback of the formatted value,
173
+ // ahead of the parent's @value round-trip.
170
174
  input.value = formattedDisplay;
171
175
  this.args.onChange?.(clampedValue, event);
172
176
  } else {
173
- if (!this.isControlled) {
174
- this.internalDisplayValue = '';
175
- }
176
177
  input.value = '';
177
178
  this.args.onChange?.(null, event);
178
179
  }
@@ -1 +1 @@
1
- {"version":3,"file":"multi-combobox-field.js","sources":["../../src/form/multi-combobox-field.gts"],"sourcesContent":["import Component from '@glimmer/component';\n\nimport Control from './control.gts';\nimport MultiComboBox from './multi-combobox.gts';\nimport type { ComboBoxItems, ComboBoxOption } from './combobox-shared.ts';\n\nexport type LabelVisibility = 'visible' | 'hidden';\n\nexport interface MultiComboBoxFieldSignature {\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 values (controlled). */\n values?: string[];\n\n /** Fires when the selection changes. */\n onChange?: (values: string[], options: ComboBoxOption[]) => void;\n\n /** Async search. */\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 when no values selected. */\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 /** Chips shown before collapsing to \"+N more\". */\n maxVisibleChips?: number;\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-all button when values are 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 — one hidden input is rendered per selected value. */\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 MultiComboBoxField extends Component<MultiComboBoxFieldSignature> {\n get isInvalid(): boolean {\n return !!this.args.error;\n }\n\n get isLabelHidden(): boolean {\n return this.args.labelVisibility === 'hidden';\n }\n\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 <MultiComboBox\n @id={{ctrl.id}}\n @name={{@name}}\n @items={{@items}}\n @values={{@values}}\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 @maxVisibleChips={{@maxVisibleChips}}\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-multi-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":["MultiComboBoxField","Component","isInvalid","args","error","isLabelHidden","labelVisibility","getAriaDescribedBy","controlId","parts","helpText","push","length","join","undefined","setComponentTemplate","precompileTemplate","strictMode","scope","Control","MultiComboBox"],"mappings":";;;;;;AAqGe,MAAMA,2BAA2BC,SAAA,CAAU;EACxD,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;EAEAC,kBAAA,GAAsBC,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,i5CAAA,EA0DA;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":"multi-combobox-field.js","sources":["../../src/form/multi-combobox-field.gts"],"sourcesContent":["import Component from '@glimmer/component';\n\nimport Control from './control.gts';\nimport MultiComboBox from './multi-combobox.gts';\nimport type { ComboBoxItems, ComboBoxOption } from './combobox-shared.ts';\n\nexport type LabelVisibility = 'visible' | 'hidden';\n\nexport interface MultiComboBoxFieldSignature {\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 values. The consumer owns them; pair with `onChange`. */\n values?: string[];\n\n /** Fires when the selection changes. */\n onChange?: (values: string[], options: ComboBoxOption[]) => void;\n\n /** Async search. */\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 when no values selected. */\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 /** Chips shown before collapsing to \"+N more\". */\n maxVisibleChips?: number;\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-all button when values are 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 — one hidden input is rendered per selected value. */\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 MultiComboBoxField extends Component<MultiComboBoxFieldSignature> {\n get isInvalid(): boolean {\n return !!this.args.error;\n }\n\n get isLabelHidden(): boolean {\n return this.args.labelVisibility === 'hidden';\n }\n\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 <MultiComboBox\n @id={{ctrl.id}}\n @name={{@name}}\n @items={{@items}}\n @values={{@values}}\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 @maxVisibleChips={{@maxVisibleChips}}\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-multi-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":["MultiComboBoxField","Component","isInvalid","args","error","isLabelHidden","labelVisibility","getAriaDescribedBy","controlId","parts","helpText","push","length","join","undefined","setComponentTemplate","precompileTemplate","strictMode","scope","Control","MultiComboBox"],"mappings":";;;;;;AAqGe,MAAMA,2BAA2BC,SAAA,CAAU;EACxD,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;EAEAC,kBAAA,GAAsBC,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,i5CAAA,EA0DA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,OAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}
@@ -20,16 +20,15 @@ class Radio extends Component {
20
20
  return this.args.size ?? 'md';
21
21
  }
22
22
  get checkedValue() {
23
- // Controlled mode takes precedence
23
+ // An explicit isChecked takes precedence over the group's selection.
24
24
  if (this.args.isChecked !== undefined) {
25
25
  return this.args.isChecked;
26
26
  }
27
- // Group value mode (from RadioGroup)
27
+ // Otherwise the radio is checked when its value matches the group's.
28
28
  if (this.args.groupValue !== undefined) {
29
29
  return this.args.value === this.args.groupValue;
30
30
  }
31
- // Uncontrolled mode
32
- return this.args.defaultChecked;
31
+ return false;
33
32
  }
34
33
  handleChange() {
35
34
  this.args.onChange?.(this.args.value);
@@ -18,13 +18,10 @@ class SearchInput extends Component {
18
18
  return false;
19
19
  });
20
20
  }
21
- #isFocused = (i(this, "isFocused"), void 0);
22
- static {
23
- g(this.prototype, "value", [tracked], function () {
24
- return this.args.value || '';
25
- });
21
+ #isFocused = (i(this, "isFocused"), void 0); // Ephemeral UI state the component owns; the value itself is consumer-owned.
22
+ get currentValue() {
23
+ return this.args.value ?? '';
26
24
  }
27
- #value = (i(this, "value"), void 0);
28
25
  get inputId() {
29
26
  return guidFor(this);
30
27
  }
@@ -36,7 +33,6 @@ class SearchInput extends Component {
36
33
  }
37
34
  handleInput(event) {
38
35
  const target = event.target;
39
- this.value = target.value;
40
36
  this.args.onInput?.(target.value, event);
41
37
  }
42
38
  static {
@@ -65,14 +61,15 @@ class SearchInput extends Component {
65
61
  }
66
62
  handleKeyUp(event) {
67
63
  if (event.key === 'Enter') {
68
- this.args.onSearch?.(this.value);
64
+ const target = event.target;
65
+ this.args.onSearch?.(target.value);
69
66
  }
70
67
  }
71
68
  static {
72
69
  n(this.prototype, "handleKeyUp", [action]);
73
70
  }
74
71
  static {
75
- setComponentTemplate(precompileTemplate("<div class=\"field_e35e7cf3c\" ...attributes>\n {{#if @label}}\n <label for={{this.inputId}} class=\"label_e35e7cf3c\">\n {{@label}}\n {{#if @required}}\n <span class=\"required_e35e7cf3c\">*</span>\n {{/if}}\n </label>\n {{/if}}\n\n <div class=\"input-wrapper_e35e7cf3c\">\n <span class=\"search-icon_e35e7cf3c\">\n <Icon @icon={{Search}} @size={{16}} />\n </span>\n <input id={{this.inputId}} type=\"search\" name={{@name}} value={{this.value}} placeholder={{@placeholder}} disabled={{@disabled}} readonly={{@readonly}} required={{@required}} autocomplete={{@autocomplete}} class=\"input_e35e7cf3c\" data-size={{this.size}} data-error={{if this.hasError \"true\"}} {{on \"input\" this.handleInput}} {{on \"change\" this.handleChange}} {{on \"focus\" this.handleFocus}} {{on \"blur\" this.handleBlur}} {{on \"keyup\" this.handleKeyUp}} />\n </div>\n\n {{#if @helperText}}\n <p class=\"helper-text_e35e7cf3c\">{{@helperText}}</p>\n {{/if}}\n\n {{#if @error}}\n <p class=\"error-text_e35e7cf3c\">{{@error}}</p>\n {{/if}}\n</div>", {
72
+ setComponentTemplate(precompileTemplate("<div class=\"field_e35e7cf3c\" ...attributes>\n {{#if @label}}\n <label for={{this.inputId}} class=\"label_e35e7cf3c\">\n {{@label}}\n {{#if @required}}\n <span class=\"required_e35e7cf3c\">*</span>\n {{/if}}\n </label>\n {{/if}}\n\n <div class=\"input-wrapper_e35e7cf3c\">\n <span class=\"search-icon_e35e7cf3c\">\n <Icon @icon={{Search}} @size={{16}} />\n </span>\n <input id={{this.inputId}} type=\"search\" name={{@name}} value={{this.currentValue}} placeholder={{@placeholder}} disabled={{@disabled}} readonly={{@readonly}} required={{@required}} autocomplete={{@autocomplete}} class=\"input_e35e7cf3c\" data-size={{this.size}} data-error={{if this.hasError \"true\"}} {{on \"input\" this.handleInput}} {{on \"change\" this.handleChange}} {{on \"focus\" this.handleFocus}} {{on \"blur\" this.handleBlur}} {{on \"keyup\" this.handleKeyUp}} />\n </div>\n\n {{#if @helperText}}\n <p class=\"helper-text_e35e7cf3c\">{{@helperText}}</p>\n {{/if}}\n\n {{#if @error}}\n <p class=\"error-text_e35e7cf3c\">{{@error}}</p>\n {{/if}}\n</div>", {
76
73
  strictMode: true,
77
74
  scope: () => ({
78
75
  Icon,
@@ -1,6 +1,5 @@
1
1
  import "./text-field-css-754feeb82dbd9fc9afdc1f00dceb13ad.css"
2
2
  import Component from '@glimmer/component';
3
- import { tracked } from '@glimmer/tracking';
4
3
  import { action } from '@ember/object';
5
4
  import { guidFor } from '@ember/object/internals';
6
5
  import { on } from '@ember/modifier';
@@ -11,20 +10,12 @@ import HelpText from './help-text.js';
11
10
  import ErrorMessage from './error-message.js';
12
11
  import { precompileTemplate } from '@ember/template-compilation';
13
12
  import { setComponentTemplate } from '@ember/component';
14
- import { g, i, n } from 'decorator-transforms/runtime';
13
+ import { n } from 'decorator-transforms/runtime';
15
14
 
16
15
  ;
17
16
 
18
17
  class TextField extends Component {
19
- static {
20
- g(this.prototype, "internalValue", [tracked]);
21
- }
22
- #internalValue = (i(this, "internalValue"), void 0);
23
18
  id = this.args.id ?? guidFor(this);
24
- constructor(owner, args) {
25
- super(owner, args);
26
- this.internalValue = args.value ?? args.defaultValue ?? '';
27
- }
28
19
  get helpTextId() {
29
20
  return `${this.id}-help-text`;
30
21
  }
@@ -59,11 +50,8 @@ class TextField extends Component {
59
50
  get rows() {
60
51
  return this.args.rows ?? 3;
61
52
  }
62
- get isControlled() {
63
- return this.args.value !== undefined;
64
- }
65
53
  get currentValue() {
66
- return this.isControlled ? this.args.value ?? '' : this.internalValue;
54
+ return this.args.value ?? '';
67
55
  }
68
56
  get characterCount() {
69
57
  return this.currentValue.length;
@@ -77,9 +65,6 @@ class TextField extends Component {
77
65
  }
78
66
  // Handler for Textarea (receives value as first arg)
79
67
  handleTextareaInput(value, event) {
80
- if (!this.isControlled) {
81
- this.internalValue = value;
82
- }
83
68
  this.args.onInput?.(value, event);
84
69
  }
85
70
  static {
@@ -94,11 +79,7 @@ class TextField extends Component {
94
79
  }
95
80
  handleInputEvent(event) {
96
81
  const target = event.target;
97
- const value = target.value;
98
- if (!this.isControlled) {
99
- this.internalValue = value;
100
- }
101
- this.args.onInput?.(value, event);
82
+ this.args.onInput?.(target.value, event);
102
83
  }
103
84
  static {
104
85
  n(this.prototype, "handleInputEvent", [action]);
@@ -1,12 +1,11 @@
1
1
  import "./textarea.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
 
@@ -45,19 +44,8 @@ const autoGrow = modifier((element, [enabled, minRows, maxRows]) => {
45
44
  };
46
45
  });
47
46
  class Textarea extends Component {
48
- static {
49
- g(this.prototype, "internalValue", [tracked]);
50
- }
51
- #internalValue = (i(this, "internalValue"), void 0);
52
- constructor(owner, args) {
53
- super(owner, args);
54
- this.internalValue = args.value ?? args.defaultValue ?? '';
55
- }
56
- get isControlled() {
57
- return this.args.value !== undefined;
58
- }
59
47
  get currentValue() {
60
- return this.isControlled ? this.args.value ?? '' : this.internalValue;
48
+ return this.args.value ?? '';
61
49
  }
62
50
  get size() {
63
51
  return this.args.size ?? 'md';
@@ -98,9 +86,6 @@ class Textarea extends Component {
98
86
  }
99
87
  handleInput(event) {
100
88
  const target = event.target;
101
- if (!this.isControlled) {
102
- this.internalValue = target.value;
103
- }
104
89
  this.args.onInput?.(target.value, event);
105
90
  }
106
91
  static {
@@ -26,7 +26,7 @@ class ToggleField extends Component {
26
26
  return undefined;
27
27
  }
28
28
  static {
29
- setComponentTemplate(precompileTemplate("<div class=\"toggle-field_e5c1644c4\" data-invalid={{if this.isInvalid \"true\"}} data-test-toggle-field ...attributes>\n <Toggle @id={{this.fieldId}} @label={{@label}} @description={{@description}} @checked={{@checked}} @defaultChecked={{@defaultChecked}} @onChange={{@onChange}} @labelPosition={{@labelPosition}} @size={{@size}} @isDisabled={{@isDisabled}} @name={{@name}} @value={{@value}} @isRequired={{@isRequired}} @onBlur={{@onBlur}} @onFocus={{@onFocus}} @aria-describedby={{this.ariaDescribedBy}} />\n\n {{#if @error}}\n <ErrorMessage id={{this.errorMessageId}} @isInvalid={{this.isInvalid}}>\n {{@error}}\n </ErrorMessage>\n {{/if}}\n</div>", {
29
+ setComponentTemplate(precompileTemplate("<div class=\"toggle-field_e5c1644c4\" data-invalid={{if this.isInvalid \"true\"}} data-test-toggle-field ...attributes>\n <Toggle @id={{this.fieldId}} @label={{@label}} @description={{@description}} @checked={{@checked}} @onChange={{@onChange}} @labelPosition={{@labelPosition}} @size={{@size}} @isDisabled={{@isDisabled}} @name={{@name}} @value={{@value}} @isRequired={{@isRequired}} @onBlur={{@onBlur}} @onFocus={{@onFocus}} @aria-describedby={{this.ariaDescribedBy}} />\n\n {{#if @error}}\n <ErrorMessage id={{this.errorMessageId}} @isInvalid={{this.isInvalid}}>\n {{@error}}\n </ErrorMessage>\n {{/if}}\n</div>", {
30
30
  strictMode: true,
31
31
  scope: () => ({
32
32
  Toggle,
@@ -12,20 +12,13 @@ import { g, i, n } from 'decorator-transforms/runtime';
12
12
  ;
13
13
 
14
14
  class Toggle extends Component {
15
- static {
16
- g(this.prototype, "internalChecked", [tracked]);
17
- }
18
- #internalChecked = (i(this, "internalChecked"), void 0);
19
15
  static {
20
16
  g(this.prototype, "isFocused", [tracked], function () {
21
17
  return false;
22
18
  });
23
19
  }
24
- #isFocused = (i(this, "isFocused"), void 0);
25
- constructor(owner, args) {
26
- super(owner, args);
27
- this.internalChecked = args.defaultChecked ?? false;
28
- }
20
+ #isFocused = (i(this, "isFocused"), void 0); // Ephemeral UI state — which the component owns. Tracks the focus ring;
21
+ // never the consumer's checked state.
29
22
  guid = guidFor(this);
30
23
  get inputId() {
31
24
  return this.args.id ?? this.guid;
@@ -45,11 +38,8 @@ class Toggle extends Component {
45
38
  get value() {
46
39
  return this.args.value ?? 'on';
47
40
  }
48
- get isControlled() {
49
- return this.args.checked !== undefined;
50
- }
51
41
  get checked() {
52
- return this.isControlled ? this.args.checked ?? false : this.internalChecked;
42
+ return this.args.checked;
53
43
  }
54
44
  get hasLabel() {
55
45
  return !!this.args.label;
@@ -68,11 +58,7 @@ class Toggle extends Component {
68
58
  }
69
59
  handleChange(event) {
70
60
  const target = event.target;
71
- const newChecked = target.checked;
72
- if (!this.isControlled) {
73
- this.internalChecked = newChecked;
74
- }
75
- this.args.onChange?.(newChecked);
61
+ this.args.onChange?.(target.checked, event);
76
62
  }
77
63
  static {
78
64
  n(this.prototype, "handleChange", [action]);