@sdcorejs/angular 21.0.0 → 21.0.3

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 (130) hide show
  1. package/README.md +36 -19
  2. package/assets/scss/core/_inline-edit.scss +105 -0
  3. package/assets/scss/core/form.scss +15 -0
  4. package/fesm2022/sdcorejs-angular-components-anchor.mjs +9 -9
  5. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs +111 -24
  6. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
  7. package/fesm2022/sdcorejs-angular-components-avatar.mjs +4 -4
  8. package/fesm2022/sdcorejs-angular-components-avatar.mjs.map +1 -1
  9. package/fesm2022/sdcorejs-angular-components-badge.mjs +3 -3
  10. package/fesm2022/sdcorejs-angular-components-base.mjs +3 -3
  11. package/fesm2022/sdcorejs-angular-components-button.mjs +4 -4
  12. package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
  13. package/fesm2022/sdcorejs-angular-components-chart.mjs +12 -12
  14. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs +3 -3
  15. package/fesm2022/sdcorejs-angular-components-code-editor.mjs +3 -3
  16. package/fesm2022/sdcorejs-angular-components-document-builder.mjs +6 -6
  17. package/fesm2022/sdcorejs-angular-components-editor.mjs +3 -3
  18. package/fesm2022/sdcorejs-angular-components-form-generic.mjs +288 -375
  19. package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
  20. package/fesm2022/sdcorejs-angular-components-history.mjs +7 -7
  21. package/fesm2022/sdcorejs-angular-components-import-excel.mjs +10 -10
  22. package/fesm2022/sdcorejs-angular-components-inform.mjs +6 -6
  23. package/fesm2022/sdcorejs-angular-components-mini-editor.mjs +3 -3
  24. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs +158 -0
  25. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -0
  26. package/fesm2022/sdcorejs-angular-components-modal.mjs +3 -3
  27. package/fesm2022/sdcorejs-angular-components-operator.mjs +6 -4
  28. package/fesm2022/sdcorejs-angular-components-operator.mjs.map +1 -1
  29. package/fesm2022/sdcorejs-angular-components-preview.mjs +8 -8
  30. package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
  31. package/fesm2022/sdcorejs-angular-components-query-bar.mjs +60 -106
  32. package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
  33. package/fesm2022/sdcorejs-angular-components-query-builder.mjs +3 -3
  34. package/fesm2022/sdcorejs-angular-components-quick-action.mjs +3 -3
  35. package/fesm2022/sdcorejs-angular-components-section.mjs +6 -6
  36. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +4 -4
  37. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
  38. package/fesm2022/sdcorejs-angular-components-splitter.mjs +12 -12
  39. package/fesm2022/sdcorejs-angular-components-stepper.mjs +6 -6
  40. package/fesm2022/sdcorejs-angular-components-tab-router.mjs +18 -18
  41. package/fesm2022/sdcorejs-angular-components-tab.mjs +6 -6
  42. package/fesm2022/sdcorejs-angular-components-table.mjs +335 -208
  43. package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
  44. package/fesm2022/sdcorejs-angular-components-upload-file.mjs +16 -16
  45. package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
  46. package/fesm2022/sdcorejs-angular-components-view.mjs +45 -13
  47. package/fesm2022/sdcorejs-angular-components-view.mjs.map +1 -1
  48. package/fesm2022/sdcorejs-angular-components.mjs +1 -0
  49. package/fesm2022/sdcorejs-angular-components.mjs.map +1 -1
  50. package/fesm2022/sdcorejs-angular-directives.mjs +21 -21
  51. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +28 -8
  52. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
  53. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +11 -6
  54. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
  55. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +15 -10
  56. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
  57. package/fesm2022/sdcorejs-angular-forms-chip.mjs +15 -10
  58. package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
  59. package/fesm2022/sdcorejs-angular-forms-date-range.mjs +21 -12
  60. package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
  61. package/fesm2022/sdcorejs-angular-forms-date.mjs +29 -17
  62. package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
  63. package/fesm2022/sdcorejs-angular-forms-datetime.mjs +34 -22
  64. package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
  65. package/fesm2022/sdcorejs-angular-forms-directives.mjs +12 -12
  66. package/fesm2022/sdcorejs-angular-forms-inline-text.mjs +167 -0
  67. package/fesm2022/sdcorejs-angular-forms-inline-text.mjs.map +1 -0
  68. package/fesm2022/sdcorejs-angular-forms-input-color.mjs +7 -5
  69. package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
  70. package/fesm2022/sdcorejs-angular-forms-input-number.mjs +37 -10
  71. package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
  72. package/fesm2022/sdcorejs-angular-forms-input.mjs +38 -12
  73. package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
  74. package/fesm2022/sdcorejs-angular-forms-label.mjs +3 -3
  75. package/fesm2022/sdcorejs-angular-forms-models.mjs +32 -2
  76. package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
  77. package/fesm2022/sdcorejs-angular-forms-radio.mjs +11 -6
  78. package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
  79. package/fesm2022/sdcorejs-angular-forms-select.mjs +36 -12
  80. package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
  81. package/fesm2022/sdcorejs-angular-forms-switch.mjs +11 -6
  82. package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
  83. package/fesm2022/sdcorejs-angular-forms-textarea.mjs +17 -7
  84. package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
  85. package/fesm2022/sdcorejs-angular-forms.mjs +4 -4
  86. package/fesm2022/sdcorejs-angular-handlers.mjs +3 -3
  87. package/fesm2022/sdcorejs-angular-i18n.mjs +26 -6
  88. package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
  89. package/fesm2022/sdcorejs-angular-interceptors.mjs +6 -6
  90. package/fesm2022/sdcorejs-angular-modules-auth.mjs +9 -9
  91. package/fesm2022/sdcorejs-angular-modules-authom.mjs +7 -7
  92. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs +7 -7
  93. package/fesm2022/sdcorejs-angular-modules-layout.mjs +73 -73
  94. package/fesm2022/sdcorejs-angular-modules-permission.mjs +9 -9
  95. package/fesm2022/sdcorejs-angular-pipes.mjs +14 -14
  96. package/fesm2022/sdcorejs-angular-services-api.mjs +10 -10
  97. package/fesm2022/sdcorejs-angular-services-cache.mjs +3 -3
  98. package/fesm2022/sdcorejs-angular-services-confirm.mjs +6 -6
  99. package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
  100. package/fesm2022/sdcorejs-angular-services-docx.mjs +3 -3
  101. package/fesm2022/sdcorejs-angular-services-excel.mjs +3 -3
  102. package/fesm2022/sdcorejs-angular-services-license.mjs +3 -3
  103. package/fesm2022/sdcorejs-angular-services-loading.mjs +3 -3
  104. package/fesm2022/sdcorejs-angular-services-notify.mjs +23 -10
  105. package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
  106. package/fesm2022/sdcorejs-angular-services-storage.mjs +3 -3
  107. package/package.json +10 -1
  108. package/types/sdcorejs-angular-components-autoid-inspector.d.ts +53 -4
  109. package/types/sdcorejs-angular-components-form-generic.d.ts +12 -13
  110. package/types/sdcorejs-angular-components-modal-resizable.d.ts +32 -0
  111. package/types/sdcorejs-angular-components-table.d.ts +99 -10
  112. package/types/sdcorejs-angular-components-view.d.ts +33 -0
  113. package/types/sdcorejs-angular-components.d.ts +1 -0
  114. package/types/sdcorejs-angular-forms-autocomplete.d.ts +16 -3
  115. package/types/sdcorejs-angular-forms-checkbox.d.ts +5 -1
  116. package/types/sdcorejs-angular-forms-chip-calendar.d.ts +5 -2
  117. package/types/sdcorejs-angular-forms-chip.d.ts +5 -2
  118. package/types/sdcorejs-angular-forms-date-range.d.ts +12 -5
  119. package/types/sdcorejs-angular-forms-date.d.ts +14 -5
  120. package/types/sdcorejs-angular-forms-datetime.d.ts +14 -5
  121. package/types/sdcorejs-angular-forms-inline-text.d.ts +80 -0
  122. package/types/sdcorejs-angular-forms-input-color.d.ts +3 -1
  123. package/types/sdcorejs-angular-forms-input-number.d.ts +14 -2
  124. package/types/sdcorejs-angular-forms-input.d.ts +14 -2
  125. package/types/sdcorejs-angular-forms-models.d.ts +40 -2
  126. package/types/sdcorejs-angular-forms-radio.d.ts +5 -2
  127. package/types/sdcorejs-angular-forms-select.d.ts +23 -5
  128. package/types/sdcorejs-angular-forms-switch.d.ts +5 -2
  129. package/types/sdcorejs-angular-forms-textarea.d.ts +9 -2
  130. package/types/sdcorejs-angular-i18n.d.ts +4 -0
@@ -0,0 +1,167 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { model, input, booleanAttribute, output, viewChild, signal, effect, afterNextRender, computed, ChangeDetectionStrategy, Component } from '@angular/core';
4
+ import * as i1 from '@angular/forms';
5
+ import { ReactiveFormsModule } from '@angular/forms';
6
+ import * as i2 from '@angular/material/icon';
7
+ import { MatIconModule } from '@angular/material/icon';
8
+
9
+ /**
10
+ * Borderless, content-hugging text input — the seamless primitive shared by
11
+ * `sd-input` / `sd-input-number` (`viewed='inline'`) and the query-bar / query-builder chips.
12
+ *
13
+ * why: a native `<input>` inside `mat-form-field` cannot hug its content, so inline-edit
14
+ * looked like a full-width input rather than text. This primitive renders a raw `<input>`
15
+ * sized via the native `size` attribute (clamped) so the click/hover target tracks the value.
16
+ *
17
+ * It is intentionally unopinionated about commit/parse/format: it forwards the raw DOM events
18
+ * (focus / blur / keydown / paste / composition / Enter / Escape) so each consumer keeps its own
19
+ * logic (e.g. sd-input-number's vi-VN formatting). Two binding modes:
20
+ * - **uncontrolled** via `[(value)]` — used by the chips' signal drafts;
21
+ * - **controlled** via `[control]` (an external `SdFormControl`) — used by the form controls.
22
+ */
23
+ class SdInlineText {
24
+ /** Uncontrolled value (two-way). Ignored when `[control]` is provided. */
25
+ value = model('', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
26
+ /** Optional external FormControl (controlled mode) — used by sd-input / sd-input-number. */
27
+ control = input(undefined, ...(ngDevMode ? [{ debugName: "control" }] : /* istanbul ignore next */ []));
28
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
29
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
30
+ /** Show a hover/value-gated clear-× (default `true`). */
31
+ clearable = input(true, { ...(ngDevMode ? { debugName: "clearable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
32
+ /** `'auto'` derives state from focus + value; pass an explicit value (e.g. `'error'`) to override. */
33
+ state = input('auto', ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
34
+ chrome = input('standalone', ...(ngDevMode ? [{ debugName: "chrome" }] : /* istanbul ignore next */ []));
35
+ autoId = input(undefined, ...(ngDevMode ? [{ debugName: "autoId" }] : /* istanbul ignore next */ []));
36
+ autofocus = input(false, { ...(ngDevMode ? { debugName: "autofocus" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
37
+ /** Lower bound (chars) so short/empty values keep a clickable target. */
38
+ minSize = input(2, ...(ngDevMode ? [{ debugName: "minSize" }] : /* istanbul ignore next */ []));
39
+ cleared = output();
40
+ keyupEnter = output();
41
+ keydownEscape = output();
42
+ sdFocus = output();
43
+ sdBlur = output();
44
+ sdKeydown = output();
45
+ sdPaste = output();
46
+ sdCompositionStart = output();
47
+ sdCompositionEnd = output();
48
+ inputRef = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
49
+ /** Bumped on input/blur/composition + bound-control changes so size/state methods re-eval under OnPush. */
50
+ tick = signal(0, ...(ngDevMode ? [{ debugName: "tick" }] : /* istanbul ignore next */ []));
51
+ focused = signal(false, ...(ngDevMode ? [{ debugName: "focused" }] : /* istanbul ignore next */ []));
52
+ constructor() {
53
+ // why: a bound control's value can change programmatically (sd-input-number reformats
54
+ // on blur) without a DOM input event — subscribe so the size/state still refresh.
55
+ effect(onCleanup => {
56
+ const c = this.control();
57
+ if (!c)
58
+ return;
59
+ const sub = c.valueChanges.subscribe(() => this.tick.update(n => n + 1));
60
+ onCleanup(() => sub.unsubscribe());
61
+ });
62
+ afterNextRender(() => {
63
+ if (this.autofocus())
64
+ this.focus();
65
+ });
66
+ }
67
+ /**
68
+ * Current text — from the bound control in controlled mode, else the value model.
69
+ * why: `tick` is a tracked dependency so the computed re-runs when a bound control's
70
+ * value changes programmatically (control.value itself is not a signal).
71
+ */
72
+ currentText = computed(() => {
73
+ this.tick();
74
+ const c = this.control();
75
+ const raw = c ? c.value : this.value();
76
+ return raw == null ? '' : String(raw);
77
+ }, ...(ngDevMode ? [{ debugName: "currentText" }] : /* istanbul ignore next */ []));
78
+ hasValue = computed(() => this.currentText().length > 0, ...(ngDevMode ? [{ debugName: "hasValue" }] : /* istanbul ignore next */ []));
79
+ sizeOf = computed(() => {
80
+ const text = this.currentText();
81
+ const base = text.length > 0 ? text.length : this.placeholder().length;
82
+ return Math.max(this.minSize(), base);
83
+ }, ...(ngDevMode ? [{ debugName: "sizeOf" }] : /* istanbul ignore next */ []));
84
+ showClear = computed(() => this.clearable() && this.hasValue() && !this.disabled(), ...(ngDevMode ? [{ debugName: "showClear" }] : /* istanbul ignore next */ []));
85
+ effectiveState = computed(() => {
86
+ const s = this.state();
87
+ if (s !== 'auto')
88
+ return s;
89
+ if (this.focused())
90
+ return 'focus';
91
+ return this.hasValue() ? 'active' : 'pending';
92
+ }, ...(ngDevMode ? [{ debugName: "effectiveState" }] : /* istanbul ignore next */ []));
93
+ // ── event handlers ─────────────────────────────────────────────────────────
94
+ onInput(ev) {
95
+ const el = ev.target;
96
+ // why: only the uncontrolled mode owns the value model; in controlled mode the bound
97
+ // FormControl carries the value natively (ReactiveForms), so we must not double-write.
98
+ if (!this.control())
99
+ this.value.set(el.value);
100
+ this.tick.update(n => n + 1);
101
+ }
102
+ onFocus(ev) {
103
+ this.focused.set(true);
104
+ this.sdFocus.emit(ev);
105
+ }
106
+ onBlur(ev) {
107
+ this.focused.set(false);
108
+ this.tick.update(n => n + 1);
109
+ this.sdBlur.emit(ev);
110
+ }
111
+ onKeyup(ev) {
112
+ if (ev.key === 'Enter')
113
+ this.keyupEnter.emit();
114
+ }
115
+ onKeydown(ev) {
116
+ if (ev.key === 'Escape')
117
+ this.keydownEscape.emit();
118
+ this.sdKeydown.emit(ev);
119
+ }
120
+ onPaste(ev) {
121
+ this.sdPaste.emit(ev);
122
+ }
123
+ onCompositionStart(ev) {
124
+ this.sdCompositionStart.emit(ev);
125
+ }
126
+ onCompositionEnd(ev) {
127
+ this.tick.update(n => n + 1);
128
+ this.sdCompositionEnd.emit(ev);
129
+ }
130
+ clear(ev) {
131
+ ev.stopPropagation();
132
+ const c = this.control();
133
+ if (c)
134
+ c.setValue('');
135
+ else
136
+ this.value.set('');
137
+ this.tick.update(n => n + 1);
138
+ this.cleared.emit();
139
+ }
140
+ // ── public API ───────────────────────────────────────────────────────────
141
+ /** Focus the native input. */
142
+ focus() {
143
+ this.inputRef()?.nativeElement.focus();
144
+ }
145
+ /** Blur the native input. */
146
+ blur() {
147
+ this.inputRef()?.nativeElement.blur();
148
+ }
149
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: SdInlineText, deps: [], target: i0.ɵɵFactoryTarget.Component });
150
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: SdInlineText, isStandalone: true, selector: "sd-inline-text", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, chrome: { classPropertyName: "chrome", publicName: "chrome", isSignal: true, isRequired: false, transformFunction: null }, autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null }, minSize: { classPropertyName: "minSize", publicName: "minSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", cleared: "cleared", keyupEnter: "keyupEnter", keydownEscape: "keydownEscape", sdFocus: "sdFocus", sdBlur: "sdBlur", sdKeydown: "sdKeydown", sdPaste: "sdPaste", sdCompositionStart: "sdCompositionStart", sdCompositionEnd: "sdCompositionEnd" }, host: { properties: { "class.sd-inline-text--standalone": "chrome() === 'standalone'", "class.sd-inline-text--seamless": "chrome() === 'seamless'", "attr.data-state": "effectiveState()" }, classAttribute: "sd-inline-text" }, viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "@let _autoId = autoId();\n@let _placeholder = placeholder();\n@let _size = sizeOf();\n@let _control = control();\n\n@if (_control) {\n <input\n #inputEl\n class=\"sd-inline-text__input\"\n type=\"text\"\n [formControl]=\"_control\"\n [placeholder]=\"_placeholder\"\n [size]=\"_size\"\n [attr.data-autoId]=\"_autoId\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus($event)\"\n (blur)=\"onBlur($event)\"\n (keyup)=\"onKeyup($event)\"\n (keydown)=\"onKeydown($event)\"\n (paste)=\"onPaste($event)\"\n (compositionstart)=\"onCompositionStart($event)\"\n (compositionend)=\"onCompositionEnd($event)\" />\n} @else {\n <input\n #inputEl\n class=\"sd-inline-text__input\"\n type=\"text\"\n [value]=\"currentText()\"\n [disabled]=\"disabled()\"\n [placeholder]=\"_placeholder\"\n [size]=\"_size\"\n [attr.data-autoId]=\"_autoId\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus($event)\"\n (blur)=\"onBlur($event)\"\n (keyup)=\"onKeyup($event)\"\n (keydown)=\"onKeydown($event)\"\n (paste)=\"onPaste($event)\"\n (compositionstart)=\"onCompositionStart($event)\"\n (compositionend)=\"onCompositionEnd($event)\" />\n}\n\n@if (showClear()) {\n <button type=\"button\" class=\"sd-inline-text__clear\" (click)=\"clear($event)\" aria-label=\"Clear\" tabindex=\"-1\">\n <mat-icon>close</mat-icon>\n </button>\n}\n", styles: [":host{display:inline-flex;align-items:center;max-width:100%;font:inherit;color:inherit}.sd-inline-text__input{appearance:none;border:0;outline:0;background:transparent;font:inherit;color:inherit;padding:0;min-width:0;max-width:240px}:host(.sd-inline-text--standalone){border-radius:4px;padding:1px 6px;background-color:#0000000a;transition:background-color .15s ease,box-shadow .15s ease}:host(.sd-inline-text--standalone):hover{background-color:#00000014}:host(.sd-inline-text--standalone)[data-state=focus]{background-color:#0000000f;box-shadow:inset 0 0 0 1px var(--sd-primary, #1f6feb)}:host(.sd-inline-text--standalone)[data-state=error]{box-shadow:inset 0 0 0 1px var(--sd-error, #b32626)}:host(.sd-inline-text--standalone) .sd-inline-text__clear{display:none}:host(.sd-inline-text--standalone):hover .sd-inline-text__clear,:host(.sd-inline-text--standalone)[data-state=focus] .sd-inline-text__clear{display:inline-flex}:host(.sd-inline-text--seamless) .sd-inline-text__input{font-weight:500}:host(.sd-inline-text--seamless) .sd-inline-text__input::placeholder{font-style:italic}:host(.sd-inline-text--seamless)[data-state=active] .sd-inline-text__input{color:var(--sd-primary, #1f6feb)}:host(.sd-inline-text--seamless)[data-state=error] .sd-inline-text__input{color:var(--sd-error, #b32626)}.sd-inline-text__clear{display:inline-flex;align-items:center;margin-left:4px;padding:0;border:0;background:transparent;cursor:pointer;line-height:0;color:var(--sd-text-secondary, #6b7280);transition:color .15s ease}.sd-inline-text__clear mat-icon{font-size:16px;width:16px;height:16px}.sd-inline-text__clear:hover{color:var(--sd-error, #b32626)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
151
+ }
152
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: SdInlineText, decorators: [{
153
+ type: Component,
154
+ args: [{ selector: 'sd-inline-text', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, ReactiveFormsModule, MatIconModule], host: {
155
+ class: 'sd-inline-text',
156
+ '[class.sd-inline-text--standalone]': "chrome() === 'standalone'",
157
+ '[class.sd-inline-text--seamless]': "chrome() === 'seamless'",
158
+ '[attr.data-state]': 'effectiveState()',
159
+ }, template: "@let _autoId = autoId();\n@let _placeholder = placeholder();\n@let _size = sizeOf();\n@let _control = control();\n\n@if (_control) {\n <input\n #inputEl\n class=\"sd-inline-text__input\"\n type=\"text\"\n [formControl]=\"_control\"\n [placeholder]=\"_placeholder\"\n [size]=\"_size\"\n [attr.data-autoId]=\"_autoId\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus($event)\"\n (blur)=\"onBlur($event)\"\n (keyup)=\"onKeyup($event)\"\n (keydown)=\"onKeydown($event)\"\n (paste)=\"onPaste($event)\"\n (compositionstart)=\"onCompositionStart($event)\"\n (compositionend)=\"onCompositionEnd($event)\" />\n} @else {\n <input\n #inputEl\n class=\"sd-inline-text__input\"\n type=\"text\"\n [value]=\"currentText()\"\n [disabled]=\"disabled()\"\n [placeholder]=\"_placeholder\"\n [size]=\"_size\"\n [attr.data-autoId]=\"_autoId\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus($event)\"\n (blur)=\"onBlur($event)\"\n (keyup)=\"onKeyup($event)\"\n (keydown)=\"onKeydown($event)\"\n (paste)=\"onPaste($event)\"\n (compositionstart)=\"onCompositionStart($event)\"\n (compositionend)=\"onCompositionEnd($event)\" />\n}\n\n@if (showClear()) {\n <button type=\"button\" class=\"sd-inline-text__clear\" (click)=\"clear($event)\" aria-label=\"Clear\" tabindex=\"-1\">\n <mat-icon>close</mat-icon>\n </button>\n}\n", styles: [":host{display:inline-flex;align-items:center;max-width:100%;font:inherit;color:inherit}.sd-inline-text__input{appearance:none;border:0;outline:0;background:transparent;font:inherit;color:inherit;padding:0;min-width:0;max-width:240px}:host(.sd-inline-text--standalone){border-radius:4px;padding:1px 6px;background-color:#0000000a;transition:background-color .15s ease,box-shadow .15s ease}:host(.sd-inline-text--standalone):hover{background-color:#00000014}:host(.sd-inline-text--standalone)[data-state=focus]{background-color:#0000000f;box-shadow:inset 0 0 0 1px var(--sd-primary, #1f6feb)}:host(.sd-inline-text--standalone)[data-state=error]{box-shadow:inset 0 0 0 1px var(--sd-error, #b32626)}:host(.sd-inline-text--standalone) .sd-inline-text__clear{display:none}:host(.sd-inline-text--standalone):hover .sd-inline-text__clear,:host(.sd-inline-text--standalone)[data-state=focus] .sd-inline-text__clear{display:inline-flex}:host(.sd-inline-text--seamless) .sd-inline-text__input{font-weight:500}:host(.sd-inline-text--seamless) .sd-inline-text__input::placeholder{font-style:italic}:host(.sd-inline-text--seamless)[data-state=active] .sd-inline-text__input{color:var(--sd-primary, #1f6feb)}:host(.sd-inline-text--seamless)[data-state=error] .sd-inline-text__input{color:var(--sd-error, #b32626)}.sd-inline-text__clear{display:inline-flex;align-items:center;margin-left:4px;padding:0;border:0;background:transparent;cursor:pointer;line-height:0;color:var(--sd-text-secondary, #6b7280);transition:color .15s ease}.sd-inline-text__clear mat-icon{font-size:16px;width:16px;height:16px}.sd-inline-text__clear:hover{color:var(--sd-error, #b32626)}\n"] }]
160
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], chrome: [{ type: i0.Input, args: [{ isSignal: true, alias: "chrome", required: false }] }], autoId: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoId", required: false }] }], autofocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autofocus", required: false }] }], minSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "minSize", required: false }] }], cleared: [{ type: i0.Output, args: ["cleared"] }], keyupEnter: [{ type: i0.Output, args: ["keyupEnter"] }], keydownEscape: [{ type: i0.Output, args: ["keydownEscape"] }], sdFocus: [{ type: i0.Output, args: ["sdFocus"] }], sdBlur: [{ type: i0.Output, args: ["sdBlur"] }], sdKeydown: [{ type: i0.Output, args: ["sdKeydown"] }], sdPaste: [{ type: i0.Output, args: ["sdPaste"] }], sdCompositionStart: [{ type: i0.Output, args: ["sdCompositionStart"] }], sdCompositionEnd: [{ type: i0.Output, args: ["sdCompositionEnd"] }], inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }] } });
161
+
162
+ /**
163
+ * Generated bundle index. Do not edit.
164
+ */
165
+
166
+ export { SdInlineText };
167
+ //# sourceMappingURL=sdcorejs-angular-forms-inline-text.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdcorejs-angular-forms-inline-text.mjs","sources":["../../../projects/sdcorejs-angular/forms/inline-text/src/inline-text.component.ts","../../../projects/sdcorejs-angular/forms/inline-text/src/inline-text.component.html","../../../projects/sdcorejs-angular/forms/inline-text/sdcorejs-angular-forms-inline-text.ts"],"sourcesContent":["import { CommonModule } from '@angular/common';\nimport {\n afterNextRender,\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n ElementRef,\n input,\n model,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { ReactiveFormsModule } from '@angular/forms';\nimport { MatIconModule } from '@angular/material/icon';\nimport { SdFormControl } from '@sdcorejs/angular/forms/models';\n\n/** Visual / `data-state` of the inline input. `'auto'` derives from focus + value. */\nexport type SdInlineTextState = 'auto' | 'pending' | 'active' | 'focus' | 'error';\n/** `'standalone'` draws its own affordance (hover bg + focus ring); `'seamless'` defers all chrome to the host. */\nexport type SdInlineTextChrome = 'standalone' | 'seamless';\n\n/**\n * Borderless, content-hugging text input — the seamless primitive shared by\n * `sd-input` / `sd-input-number` (`viewed='inline'`) and the query-bar / query-builder chips.\n *\n * why: a native `<input>` inside `mat-form-field` cannot hug its content, so inline-edit\n * looked like a full-width input rather than text. This primitive renders a raw `<input>`\n * sized via the native `size` attribute (clamped) so the click/hover target tracks the value.\n *\n * It is intentionally unopinionated about commit/parse/format: it forwards the raw DOM events\n * (focus / blur / keydown / paste / composition / Enter / Escape) so each consumer keeps its own\n * logic (e.g. sd-input-number's vi-VN formatting). Two binding modes:\n * - **uncontrolled** via `[(value)]` — used by the chips' signal drafts;\n * - **controlled** via `[control]` (an external `SdFormControl`) — used by the form controls.\n */\n@Component({\n selector: 'sd-inline-text',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [CommonModule, ReactiveFormsModule, MatIconModule],\n templateUrl: './inline-text.component.html',\n styleUrl: './inline-text.component.scss',\n host: {\n class: 'sd-inline-text',\n '[class.sd-inline-text--standalone]': \"chrome() === 'standalone'\",\n '[class.sd-inline-text--seamless]': \"chrome() === 'seamless'\",\n '[attr.data-state]': 'effectiveState()',\n },\n})\nexport class SdInlineText {\n /** Uncontrolled value (two-way). Ignored when `[control]` is provided. */\n readonly value = model<string>('');\n /** Optional external FormControl (controlled mode) — used by sd-input / sd-input-number. */\n readonly control = input<SdFormControl | undefined>(undefined);\n readonly placeholder = input<string>('');\n readonly disabled = input(false, { transform: booleanAttribute });\n /** Show a hover/value-gated clear-× (default `true`). */\n readonly clearable = input(true, { transform: booleanAttribute });\n /** `'auto'` derives state from focus + value; pass an explicit value (e.g. `'error'`) to override. */\n readonly state = input<SdInlineTextState>('auto');\n readonly chrome = input<SdInlineTextChrome>('standalone');\n readonly autoId = input<string | undefined>(undefined);\n readonly autofocus = input(false, { transform: booleanAttribute });\n /** Lower bound (chars) so short/empty values keep a clickable target. */\n readonly minSize = input(2);\n\n readonly cleared = output<void>();\n readonly keyupEnter = output<void>();\n readonly keydownEscape = output<void>();\n readonly sdFocus = output<FocusEvent>();\n readonly sdBlur = output<FocusEvent>();\n readonly sdKeydown = output<KeyboardEvent>();\n readonly sdPaste = output<ClipboardEvent>();\n readonly sdCompositionStart = output<CompositionEvent>();\n readonly sdCompositionEnd = output<CompositionEvent>();\n\n private readonly inputRef = viewChild<ElementRef<HTMLInputElement>>('inputEl');\n\n /** Bumped on input/blur/composition + bound-control changes so size/state methods re-eval under OnPush. */\n protected readonly tick = signal(0);\n protected readonly focused = signal(false);\n\n constructor() {\n // why: a bound control's value can change programmatically (sd-input-number reformats\n // on blur) without a DOM input event — subscribe so the size/state still refresh.\n effect(onCleanup => {\n const c = this.control();\n if (!c) return;\n const sub = c.valueChanges.subscribe(() => this.tick.update(n => n + 1));\n onCleanup(() => sub.unsubscribe());\n });\n\n afterNextRender(() => {\n if (this.autofocus()) this.focus();\n });\n }\n\n /**\n * Current text — from the bound control in controlled mode, else the value model.\n * why: `tick` is a tracked dependency so the computed re-runs when a bound control's\n * value changes programmatically (control.value itself is not a signal).\n */\n protected readonly currentText = computed<string>(() => {\n this.tick();\n const c = this.control();\n const raw = c ? c.value : this.value();\n return raw == null ? '' : String(raw);\n });\n\n protected readonly hasValue = computed(() => this.currentText().length > 0);\n\n protected readonly sizeOf = computed(() => {\n const text = this.currentText();\n const base = text.length > 0 ? text.length : this.placeholder().length;\n return Math.max(this.minSize(), base);\n });\n\n protected readonly showClear = computed(() => this.clearable() && this.hasValue() && !this.disabled());\n\n protected readonly effectiveState = computed<SdInlineTextState>(() => {\n const s = this.state();\n if (s !== 'auto') return s;\n if (this.focused()) return 'focus';\n return this.hasValue() ? 'active' : 'pending';\n });\n\n // ── event handlers ─────────────────────────────────────────────────────────\n protected onInput(ev: Event): void {\n const el = ev.target as HTMLInputElement;\n // why: only the uncontrolled mode owns the value model; in controlled mode the bound\n // FormControl carries the value natively (ReactiveForms), so we must not double-write.\n if (!this.control()) this.value.set(el.value);\n this.tick.update(n => n + 1);\n }\n\n protected onFocus(ev: FocusEvent): void {\n this.focused.set(true);\n this.sdFocus.emit(ev);\n }\n\n protected onBlur(ev: FocusEvent): void {\n this.focused.set(false);\n this.tick.update(n => n + 1);\n this.sdBlur.emit(ev);\n }\n\n protected onKeyup(ev: KeyboardEvent): void {\n if (ev.key === 'Enter') this.keyupEnter.emit();\n }\n\n protected onKeydown(ev: KeyboardEvent): void {\n if (ev.key === 'Escape') this.keydownEscape.emit();\n this.sdKeydown.emit(ev);\n }\n\n protected onPaste(ev: ClipboardEvent): void {\n this.sdPaste.emit(ev);\n }\n\n protected onCompositionStart(ev: CompositionEvent): void {\n this.sdCompositionStart.emit(ev);\n }\n\n protected onCompositionEnd(ev: CompositionEvent): void {\n this.tick.update(n => n + 1);\n this.sdCompositionEnd.emit(ev);\n }\n\n protected clear(ev: Event): void {\n ev.stopPropagation();\n const c = this.control();\n if (c) c.setValue('');\n else this.value.set('');\n this.tick.update(n => n + 1);\n this.cleared.emit();\n }\n\n // ── public API ───────────────────────────────────────────────────────────\n /** Focus the native input. */\n focus(): void {\n this.inputRef()?.nativeElement.focus();\n }\n\n /** Blur the native input. */\n blur(): void {\n this.inputRef()?.nativeElement.blur();\n }\n}\n","@let _autoId = autoId();\n@let _placeholder = placeholder();\n@let _size = sizeOf();\n@let _control = control();\n\n@if (_control) {\n <input\n #inputEl\n class=\"sd-inline-text__input\"\n type=\"text\"\n [formControl]=\"_control\"\n [placeholder]=\"_placeholder\"\n [size]=\"_size\"\n [attr.data-autoId]=\"_autoId\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus($event)\"\n (blur)=\"onBlur($event)\"\n (keyup)=\"onKeyup($event)\"\n (keydown)=\"onKeydown($event)\"\n (paste)=\"onPaste($event)\"\n (compositionstart)=\"onCompositionStart($event)\"\n (compositionend)=\"onCompositionEnd($event)\" />\n} @else {\n <input\n #inputEl\n class=\"sd-inline-text__input\"\n type=\"text\"\n [value]=\"currentText()\"\n [disabled]=\"disabled()\"\n [placeholder]=\"_placeholder\"\n [size]=\"_size\"\n [attr.data-autoId]=\"_autoId\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus($event)\"\n (blur)=\"onBlur($event)\"\n (keyup)=\"onKeyup($event)\"\n (keydown)=\"onKeydown($event)\"\n (paste)=\"onPaste($event)\"\n (compositionstart)=\"onCompositionStart($event)\"\n (compositionend)=\"onCompositionEnd($event)\" />\n}\n\n@if (showClear()) {\n <button type=\"button\" class=\"sd-inline-text__clear\" (click)=\"clear($event)\" aria-label=\"Clear\" tabindex=\"-1\">\n <mat-icon>close</mat-icon>\n </button>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;AAwBA;;;;;;;;;;;;;AAaG;MAeU,YAAY,CAAA;;AAEd,IAAA,KAAK,GAAG,KAAK,CAAS,EAAE,4EAAC;;AAEzB,IAAA,OAAO,GAAG,KAAK,CAA4B,SAAS,8EAAC;AACrD,IAAA,WAAW,GAAG,KAAK,CAAS,EAAE,kFAAC;IAC/B,QAAQ,GAAG,KAAK,CAAC,KAAK,gFAAI,SAAS,EAAE,gBAAgB,EAAA,CAAG;;IAExD,SAAS,GAAG,KAAK,CAAC,IAAI,iFAAI,SAAS,EAAE,gBAAgB,EAAA,CAAG;;AAExD,IAAA,KAAK,GAAG,KAAK,CAAoB,MAAM,4EAAC;AACxC,IAAA,MAAM,GAAG,KAAK,CAAqB,YAAY,6EAAC;AAChD,IAAA,MAAM,GAAG,KAAK,CAAqB,SAAS,6EAAC;IAC7C,SAAS,GAAG,KAAK,CAAC,KAAK,iFAAI,SAAS,EAAE,gBAAgB,EAAA,CAAG;;AAEzD,IAAA,OAAO,GAAG,KAAK,CAAC,CAAC,8EAAC;IAElB,OAAO,GAAG,MAAM,EAAQ;IACxB,UAAU,GAAG,MAAM,EAAQ;IAC3B,aAAa,GAAG,MAAM,EAAQ;IAC9B,OAAO,GAAG,MAAM,EAAc;IAC9B,MAAM,GAAG,MAAM,EAAc;IAC7B,SAAS,GAAG,MAAM,EAAiB;IACnC,OAAO,GAAG,MAAM,EAAkB;IAClC,kBAAkB,GAAG,MAAM,EAAoB;IAC/C,gBAAgB,GAAG,MAAM,EAAoB;AAErC,IAAA,QAAQ,GAAG,SAAS,CAA+B,SAAS,+EAAC;;AAG3D,IAAA,IAAI,GAAG,MAAM,CAAC,CAAC,2EAAC;AAChB,IAAA,OAAO,GAAG,MAAM,CAAC,KAAK,8EAAC;AAE1C,IAAA,WAAA,GAAA;;;QAGE,MAAM,CAAC,SAAS,IAAG;AACjB,YAAA,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE;AACxB,YAAA,IAAI,CAAC,CAAC;gBAAE;YACR,MAAM,GAAG,GAAG,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YACxE,SAAS,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC;AACpC,QAAA,CAAC,CAAC;QAEF,eAAe,CAAC,MAAK;YACnB,IAAI,IAAI,CAAC,SAAS,EAAE;gBAAE,IAAI,CAAC,KAAK,EAAE;AACpC,QAAA,CAAC,CAAC;IACJ;AAEA;;;;AAIG;AACgB,IAAA,WAAW,GAAG,QAAQ,CAAS,MAAK;QACrD,IAAI,CAAC,IAAI,EAAE;AACX,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE;AACxB,QAAA,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE;AACtC,QAAA,OAAO,GAAG,IAAI,IAAI,GAAG,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC;AACvC,IAAA,CAAC,kFAAC;AAEiB,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,GAAG,CAAC,+EAAC;AAExD,IAAA,MAAM,GAAG,QAAQ,CAAC,MAAK;AACxC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM;QACtE,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC;AACvC,IAAA,CAAC,6EAAC;IAEiB,SAAS,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,SAAS,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,WAAA,EAAA,CAAA,8BAAA,EAAA,CAAA,CAAC;AAEnF,IAAA,cAAc,GAAG,QAAQ,CAAoB,MAAK;AACnE,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE;QACtB,IAAI,CAAC,KAAK,MAAM;AAAE,YAAA,OAAO,CAAC;QAC1B,IAAI,IAAI,CAAC,OAAO,EAAE;AAAE,YAAA,OAAO,OAAO;AAClC,QAAA,OAAO,IAAI,CAAC,QAAQ,EAAE,GAAG,QAAQ,GAAG,SAAS;AAC/C,IAAA,CAAC,qFAAC;;AAGQ,IAAA,OAAO,CAAC,EAAS,EAAA;AACzB,QAAA,MAAM,EAAE,GAAG,EAAE,CAAC,MAA0B;;;AAGxC,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC;AAC7C,QAAA,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9B;AAEU,IAAA,OAAO,CAAC,EAAc,EAAA;AAC9B,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;AACtB,QAAA,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;IACvB;AAEU,IAAA,MAAM,CAAC,EAAc,EAAA;AAC7B,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;AACvB,QAAA,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5B,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;IACtB;AAEU,IAAA,OAAO,CAAC,EAAiB,EAAA;AACjC,QAAA,IAAI,EAAE,CAAC,GAAG,KAAK,OAAO;AAAE,YAAA,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE;IAChD;AAEU,IAAA,SAAS,CAAC,EAAiB,EAAA;AACnC,QAAA,IAAI,EAAE,CAAC,GAAG,KAAK,QAAQ;AAAE,YAAA,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE;AAClD,QAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;IACzB;AAEU,IAAA,OAAO,CAAC,EAAkB,EAAA;AAClC,QAAA,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;IACvB;AAEU,IAAA,kBAAkB,CAAC,EAAoB,EAAA;AAC/C,QAAA,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;IAClC;AAEU,IAAA,gBAAgB,CAAC,EAAoB,EAAA;AAC7C,QAAA,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5B,QAAA,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;IAChC;AAEU,IAAA,KAAK,CAAC,EAAS,EAAA;QACvB,EAAE,CAAC,eAAe,EAAE;AACpB,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE;AACxB,QAAA,IAAI,CAAC;AAAE,YAAA,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;;AAChB,YAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;AACvB,QAAA,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5B,QAAA,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;IACrB;;;IAIA,KAAK,GAAA;QACH,IAAI,CAAC,QAAQ,EAAE,EAAE,aAAa,CAAC,KAAK,EAAE;IACxC;;IAGA,IAAI,GAAA;QACF,IAAI,CAAC,QAAQ,EAAE,EAAE,aAAa,CAAC,IAAI,EAAE;IACvC;wGAzIW,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAZ,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,YAAY,o7DCpDzB,43CA+CA,EAAA,MAAA,EAAA,CAAA,gnDAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDLY,YAAY,EAAA,EAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAE,mBAAmB,ykBAAE,aAAa,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,OAAA,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,QAAA,EAAA,SAAA,EAAA,SAAA,EAAA,UAAA,CAAA,EAAA,QAAA,EAAA,CAAA,SAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAU/C,YAAY,EAAA,UAAA,EAAA,CAAA;kBAdxB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,gBAAgB,EAAA,UAAA,EACd,IAAI,EAAA,eAAA,EACC,uBAAuB,CAAC,MAAM,EAAA,OAAA,EACtC,CAAC,YAAY,EAAE,mBAAmB,EAAE,aAAa,CAAC,EAAA,IAAA,EAGrD;AACJ,wBAAA,KAAK,EAAE,gBAAgB;AACvB,wBAAA,oCAAoC,EAAE,2BAA2B;AACjE,wBAAA,kCAAkC,EAAE,yBAAyB;AAC7D,wBAAA,mBAAmB,EAAE,kBAAkB;AACxC,qBAAA,EAAA,QAAA,EAAA,43CAAA,EAAA,MAAA,EAAA,CAAA,gnDAAA,CAAA,EAAA;qkDA6BmE,SAAS,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AE/E/E;;AAEG;;;;"}
@@ -2,6 +2,7 @@ import * as i0 from '@angular/core';
2
2
  import { input, booleanAttribute, model, output, viewChild, computed, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { SdInput } from '@sdcorejs/angular/forms/input';
4
4
  import { SdSuffixDefDirective } from '@sdcorejs/angular/forms/directives';
5
+ import { sdViewedTransform } from '@sdcorejs/angular/forms/models';
5
6
 
6
7
  // why: matches #RGB, #RRGGBB, #RRGGBBAA. Capital + lowercase hex allowed.
7
8
  // Native <input type="color"> only emits #RRGGBB, so we canonicalize before
@@ -27,7 +28,8 @@ class SdInputColor {
27
28
  required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
28
29
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
29
30
  readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
30
- viewed = input(false, { ...(ngDevMode ? { debugName: "viewed" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
31
+ /** Display mode forwarded to the inner `<sd-input>`. `'inline'` = borderless inline-edit; disabled `'inline'` static. */
32
+ viewed = input(false, { ...(ngDevMode ? { debugName: "viewed" } : /* istanbul ignore next */ {}), transform: sdViewedTransform });
31
33
  // Two-way model — same alias as <sd-input>
32
34
  valueModel = model(undefined, { ...(ngDevMode ? { debugName: "valueModel" } : /* istanbul ignore next */ {}), alias: 'model' });
33
35
  // ==========================================
@@ -101,12 +103,12 @@ class SdInputColor {
101
103
  this.valueModel.set(null);
102
104
  this.sdChange.emit(null);
103
105
  };
104
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: SdInputColor, deps: [], target: i0.ɵɵFactoryTarget.Component });
105
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.15", type: SdInputColor, isStandalone: true, selector: "sd-input-color", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, helperText: { classPropertyName: "helperText", publicName: "helperText", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, viewed: { classPropertyName: "viewed", publicName: "viewed", isSignal: true, isRequired: false, transformFunction: null }, valueModel: { classPropertyName: "valueModel", publicName: "model", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueModel: "modelChange", sdChange: "sdChange" }, viewQueries: [{ propertyName: "picker", first: true, predicate: ["picker"], descendants: true, isSignal: true }], ngImport: i0, template: "<sd-input\n [label]=\"label()\"\n [helperText]=\"helperText()\"\n [placeholder]=\"placeholder()\"\n [size]=\"size()\"\n [appearance]=\"appearance() ?? undefined\"\n [form]=\"form()\"\n [name]=\"name() ?? ''\"\n [autoId]=\"autoId()\"\n [required]=\"required()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [viewed]=\"viewed()\"\n [pattern]=\"hexPattern\"\n patternErrorMessage=\"M\u00E3 m\u00E0u HEX kh\u00F4ng h\u1EE3p l\u1EC7 (vd #1565C0)\"\n [model]=\"valueModel()\"\n (sdChange)=\"onInputChange($event)\">\n\n <ng-template sdSuffixDef>\n <div class=\"sd-input-color__suffix\">\n <!-- why: n\u00FAt clear do <sd-input> t\u1EF1 render (built-in, hover-gated). \u1EDE \u0111\u00E2y\n ch\u1EC9 chi\u1EBFu swatch + native picker. onInputChange coerce '' \u2192 null n\u00EAn\n clear c\u1EE7a sd-input v\u1EABn cho ra model r\u1ED7ng \u0111\u00FAng ngh\u0129a. -->\n <button\n type=\"button\"\n class=\"sd-input-color__swatch\"\n [style.background]=\"swatchColor()\"\n [class.sd-input-color__swatch--empty]=\"swatchColor() === 'transparent'\"\n [disabled]=\"disabled() || readonly() || viewed()\"\n (click)=\"openPicker()\"\n title=\"M\u1EDF b\u1EA3ng ch\u1ECDn m\u00E0u\"\n aria-label=\"M\u1EDF b\u1EA3ng ch\u1ECDn m\u00E0u\">\n </button>\n <input\n #picker\n type=\"color\"\n class=\"sd-input-color__hidden-picker\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [value]=\"pickerSafeValue()\"\n (input)=\"onPickerChange($event)\" />\n </div>\n </ng-template>\n</sd-input>\n", styles: [":host{display:block;--sd-input-color-swatch-size: 22px;--sd-input-color-swatch-border: rgba(0, 0, 0, .12);--sd-input-color-swatch-checker: linear-gradient(45deg, #ddd 25%, transparent 25%), linear-gradient(-45deg, #ddd 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #ddd 75%), linear-gradient(-45deg, transparent 75%, #ddd 75%)}.sd-input-color__suffix{position:relative;display:inline-flex;align-items:center;gap:4px}.sd-input-color__swatch{width:var(--sd-input-color-swatch-size);height:var(--sd-input-color-swatch-size);border-radius:4px;border:1px solid var(--sd-input-color-swatch-border);padding:0;margin:0;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;background:transparent;transition:transform 80ms ease,box-shadow 80ms ease;position:relative;overflow:hidden}.sd-input-color__swatch:hover:not(:disabled){transform:scale(1.06);box-shadow:0 0 0 2px #1565c02e}.sd-input-color__swatch:disabled{cursor:not-allowed;opacity:.5}.sd-input-color__swatch.sd-input-color__swatch--empty{background-color:#fff;background-image:var(--sd-input-color-swatch-checker);background-size:8px 8px;background-position:0 0,0 4px,4px -4px,-4px 0}.sd-input-color__hidden-picker{position:absolute;width:0;height:0;padding:0;border:0;opacity:0;pointer-events:none;right:0;top:50%}\n"], dependencies: [{ kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "directive", type: SdSuffixDefDirective, selector: "[sdSuffixDef]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
106
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: SdInputColor, deps: [], target: i0.ɵɵFactoryTarget.Component });
107
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.16", type: SdInputColor, isStandalone: true, selector: "sd-input-color", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, helperText: { classPropertyName: "helperText", publicName: "helperText", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, viewed: { classPropertyName: "viewed", publicName: "viewed", isSignal: true, isRequired: false, transformFunction: null }, valueModel: { classPropertyName: "valueModel", publicName: "model", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueModel: "modelChange", sdChange: "sdChange" }, viewQueries: [{ propertyName: "picker", first: true, predicate: ["picker"], descendants: true, isSignal: true }], ngImport: i0, template: "<sd-input\n [label]=\"label()\"\n [helperText]=\"helperText()\"\n [placeholder]=\"placeholder()\"\n [size]=\"size()\"\n [appearance]=\"appearance() ?? undefined\"\n [form]=\"form()\"\n [name]=\"name() ?? ''\"\n [autoId]=\"autoId()\"\n [required]=\"required()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [viewed]=\"viewed()\"\n [pattern]=\"hexPattern\"\n patternErrorMessage=\"M\u00E3 m\u00E0u HEX kh\u00F4ng h\u1EE3p l\u1EC7 (vd #1565C0)\"\n [model]=\"valueModel()\"\n (sdChange)=\"onInputChange($event)\">\n\n <ng-template sdSuffixDef>\n <div class=\"sd-input-color__suffix\">\n <!-- why: n\u00FAt clear do <sd-input> t\u1EF1 render (built-in, hover-gated). \u1EDE \u0111\u00E2y\n ch\u1EC9 chi\u1EBFu swatch + native picker. onInputChange coerce '' \u2192 null n\u00EAn\n clear c\u1EE7a sd-input v\u1EABn cho ra model r\u1ED7ng \u0111\u00FAng ngh\u0129a. -->\n <button\n type=\"button\"\n class=\"sd-input-color__swatch\"\n [style.background]=\"swatchColor()\"\n [class.sd-input-color__swatch--empty]=\"swatchColor() === 'transparent'\"\n [disabled]=\"disabled() || readonly() || viewed() === true\"\n (click)=\"openPicker()\"\n title=\"M\u1EDF b\u1EA3ng ch\u1ECDn m\u00E0u\"\n aria-label=\"M\u1EDF b\u1EA3ng ch\u1ECDn m\u00E0u\">\n </button>\n <input\n #picker\n type=\"color\"\n class=\"sd-input-color__hidden-picker\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [value]=\"pickerSafeValue()\"\n (input)=\"onPickerChange($event)\" />\n </div>\n </ng-template>\n</sd-input>\n", styles: [":host{display:block;--sd-input-color-swatch-size: 22px;--sd-input-color-swatch-border: rgba(0, 0, 0, .12);--sd-input-color-swatch-checker: linear-gradient(45deg, #ddd 25%, transparent 25%), linear-gradient(-45deg, #ddd 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #ddd 75%), linear-gradient(-45deg, transparent 75%, #ddd 75%)}.sd-input-color__suffix{position:relative;display:inline-flex;align-items:center;gap:4px}.sd-input-color__swatch{width:var(--sd-input-color-swatch-size);height:var(--sd-input-color-swatch-size);border-radius:4px;border:1px solid var(--sd-input-color-swatch-border);padding:0;margin:0;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;background:transparent;transition:transform 80ms ease,box-shadow 80ms ease;position:relative;overflow:hidden}.sd-input-color__swatch:hover:not(:disabled){transform:scale(1.06);box-shadow:0 0 0 2px #1565c02e}.sd-input-color__swatch:disabled{cursor:not-allowed;opacity:.5}.sd-input-color__swatch.sd-input-color__swatch--empty{background-color:#fff;background-image:var(--sd-input-color-swatch-checker);background-size:8px 8px;background-position:0 0,0 4px,4px -4px,-4px 0}.sd-input-color__hidden-picker{position:absolute;width:0;height:0;padding:0;border:0;opacity:0;pointer-events:none;right:0;top:50%}\n"], dependencies: [{ kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "directive", type: SdSuffixDefDirective, selector: "[sdSuffixDef]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
106
108
  }
107
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: SdInputColor, decorators: [{
109
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: SdInputColor, decorators: [{
108
110
  type: Component,
109
- args: [{ selector: 'sd-input-color', standalone: true, imports: [SdInput, SdSuffixDefDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<sd-input\n [label]=\"label()\"\n [helperText]=\"helperText()\"\n [placeholder]=\"placeholder()\"\n [size]=\"size()\"\n [appearance]=\"appearance() ?? undefined\"\n [form]=\"form()\"\n [name]=\"name() ?? ''\"\n [autoId]=\"autoId()\"\n [required]=\"required()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [viewed]=\"viewed()\"\n [pattern]=\"hexPattern\"\n patternErrorMessage=\"M\u00E3 m\u00E0u HEX kh\u00F4ng h\u1EE3p l\u1EC7 (vd #1565C0)\"\n [model]=\"valueModel()\"\n (sdChange)=\"onInputChange($event)\">\n\n <ng-template sdSuffixDef>\n <div class=\"sd-input-color__suffix\">\n <!-- why: n\u00FAt clear do <sd-input> t\u1EF1 render (built-in, hover-gated). \u1EDE \u0111\u00E2y\n ch\u1EC9 chi\u1EBFu swatch + native picker. onInputChange coerce '' \u2192 null n\u00EAn\n clear c\u1EE7a sd-input v\u1EABn cho ra model r\u1ED7ng \u0111\u00FAng ngh\u0129a. -->\n <button\n type=\"button\"\n class=\"sd-input-color__swatch\"\n [style.background]=\"swatchColor()\"\n [class.sd-input-color__swatch--empty]=\"swatchColor() === 'transparent'\"\n [disabled]=\"disabled() || readonly() || viewed()\"\n (click)=\"openPicker()\"\n title=\"M\u1EDF b\u1EA3ng ch\u1ECDn m\u00E0u\"\n aria-label=\"M\u1EDF b\u1EA3ng ch\u1ECDn m\u00E0u\">\n </button>\n <input\n #picker\n type=\"color\"\n class=\"sd-input-color__hidden-picker\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [value]=\"pickerSafeValue()\"\n (input)=\"onPickerChange($event)\" />\n </div>\n </ng-template>\n</sd-input>\n", styles: [":host{display:block;--sd-input-color-swatch-size: 22px;--sd-input-color-swatch-border: rgba(0, 0, 0, .12);--sd-input-color-swatch-checker: linear-gradient(45deg, #ddd 25%, transparent 25%), linear-gradient(-45deg, #ddd 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #ddd 75%), linear-gradient(-45deg, transparent 75%, #ddd 75%)}.sd-input-color__suffix{position:relative;display:inline-flex;align-items:center;gap:4px}.sd-input-color__swatch{width:var(--sd-input-color-swatch-size);height:var(--sd-input-color-swatch-size);border-radius:4px;border:1px solid var(--sd-input-color-swatch-border);padding:0;margin:0;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;background:transparent;transition:transform 80ms ease,box-shadow 80ms ease;position:relative;overflow:hidden}.sd-input-color__swatch:hover:not(:disabled){transform:scale(1.06);box-shadow:0 0 0 2px #1565c02e}.sd-input-color__swatch:disabled{cursor:not-allowed;opacity:.5}.sd-input-color__swatch.sd-input-color__swatch--empty{background-color:#fff;background-image:var(--sd-input-color-swatch-checker);background-size:8px 8px;background-position:0 0,0 4px,4px -4px,-4px 0}.sd-input-color__hidden-picker{position:absolute;width:0;height:0;padding:0;border:0;opacity:0;pointer-events:none;right:0;top:50%}\n"] }]
111
+ args: [{ selector: 'sd-input-color', standalone: true, imports: [SdInput, SdSuffixDefDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<sd-input\n [label]=\"label()\"\n [helperText]=\"helperText()\"\n [placeholder]=\"placeholder()\"\n [size]=\"size()\"\n [appearance]=\"appearance() ?? undefined\"\n [form]=\"form()\"\n [name]=\"name() ?? ''\"\n [autoId]=\"autoId()\"\n [required]=\"required()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [viewed]=\"viewed()\"\n [pattern]=\"hexPattern\"\n patternErrorMessage=\"M\u00E3 m\u00E0u HEX kh\u00F4ng h\u1EE3p l\u1EC7 (vd #1565C0)\"\n [model]=\"valueModel()\"\n (sdChange)=\"onInputChange($event)\">\n\n <ng-template sdSuffixDef>\n <div class=\"sd-input-color__suffix\">\n <!-- why: n\u00FAt clear do <sd-input> t\u1EF1 render (built-in, hover-gated). \u1EDE \u0111\u00E2y\n ch\u1EC9 chi\u1EBFu swatch + native picker. onInputChange coerce '' \u2192 null n\u00EAn\n clear c\u1EE7a sd-input v\u1EABn cho ra model r\u1ED7ng \u0111\u00FAng ngh\u0129a. -->\n <button\n type=\"button\"\n class=\"sd-input-color__swatch\"\n [style.background]=\"swatchColor()\"\n [class.sd-input-color__swatch--empty]=\"swatchColor() === 'transparent'\"\n [disabled]=\"disabled() || readonly() || viewed() === true\"\n (click)=\"openPicker()\"\n title=\"M\u1EDF b\u1EA3ng ch\u1ECDn m\u00E0u\"\n aria-label=\"M\u1EDF b\u1EA3ng ch\u1ECDn m\u00E0u\">\n </button>\n <input\n #picker\n type=\"color\"\n class=\"sd-input-color__hidden-picker\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [value]=\"pickerSafeValue()\"\n (input)=\"onPickerChange($event)\" />\n </div>\n </ng-template>\n</sd-input>\n", styles: [":host{display:block;--sd-input-color-swatch-size: 22px;--sd-input-color-swatch-border: rgba(0, 0, 0, .12);--sd-input-color-swatch-checker: linear-gradient(45deg, #ddd 25%, transparent 25%), linear-gradient(-45deg, #ddd 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #ddd 75%), linear-gradient(-45deg, transparent 75%, #ddd 75%)}.sd-input-color__suffix{position:relative;display:inline-flex;align-items:center;gap:4px}.sd-input-color__swatch{width:var(--sd-input-color-swatch-size);height:var(--sd-input-color-swatch-size);border-radius:4px;border:1px solid var(--sd-input-color-swatch-border);padding:0;margin:0;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;background:transparent;transition:transform 80ms ease,box-shadow 80ms ease;position:relative;overflow:hidden}.sd-input-color__swatch:hover:not(:disabled){transform:scale(1.06);box-shadow:0 0 0 2px #1565c02e}.sd-input-color__swatch:disabled{cursor:not-allowed;opacity:.5}.sd-input-color__swatch.sd-input-color__swatch--empty{background-color:#fff;background-image:var(--sd-input-color-swatch-checker);background-size:8px 8px;background-position:0 0,0 4px,4px -4px,-4px 0}.sd-input-color__hidden-picker{position:absolute;width:0;height:0;padding:0;border:0;opacity:0;pointer-events:none;right:0;top:50%}\n"] }]
110
112
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], helperText: [{ type: i0.Input, args: [{ isSignal: true, alias: "helperText", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], autoId: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoId", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], viewed: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewed", required: false }] }], valueModel: [{ type: i0.Input, args: [{ isSignal: true, alias: "model", required: false }] }, { type: i0.Output, args: ["modelChange"] }], sdChange: [{ type: i0.Output, args: ["sdChange"] }], picker: [{ type: i0.ViewChild, args: ['picker', { isSignal: true }] }] } });
111
113
 
112
114
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"sdcorejs-angular-forms-input-color.mjs","sources":["../../../projects/sdcorejs-angular/forms/input-color/src/input-color.component.ts","../../../projects/sdcorejs-angular/forms/input-color/src/input-color.component.html","../../../projects/sdcorejs-angular/forms/input-color/sdcorejs-angular-forms-input-color.ts"],"sourcesContent":["import {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n ElementRef,\n input,\n model,\n output,\n viewChild,\n} from '@angular/core';\nimport { MatFormFieldAppearance } from '@angular/material/form-field';\nimport { SdInput } from '@sdcorejs/angular/forms/input';\nimport { SdSuffixDefDirective } from '@sdcorejs/angular/forms/directives';\nimport { Size } from '@sdcorejs/utils/models';\n\n// why: matches #RGB, #RRGGBB, #RRGGBBAA. Capital + lowercase hex allowed.\n// Native <input type=\"color\"> only emits #RRGGBB, so we canonicalize before\n// feeding the picker (expand 3-char, strip alpha) and let the swatch render\n// the raw model value when it's a valid hex.\nexport const SD_INPUT_COLOR_HEX_PATTERN = '^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$';\n\n@Component({\n selector: 'sd-input-color',\n standalone: true,\n imports: [SdInput, SdSuffixDefDirective],\n templateUrl: './input-color.component.html',\n styleUrls: ['./input-color.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SdInputColor {\n readonly hexPattern = SD_INPUT_COLOR_HEX_PATTERN;\n readonly #hexRegex = new RegExp(SD_INPUT_COLOR_HEX_PATTERN);\n\n // ==========================================\n // Inputs (forwarded to <sd-input>)\n // ==========================================\n readonly label = input<string | undefined>();\n readonly helperText = input<string | undefined>();\n readonly placeholder = input<string>('#RRGGBB');\n readonly size = input<Size>('md');\n readonly appearance = input<MatFormFieldAppearance | undefined>(undefined);\n // why: type as `any` — the inner <sd-input> applies the FormGroup|NgForm transform.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n readonly form = input<any>(undefined);\n readonly name = input<string | undefined>();\n readonly autoId = input<string | undefined>();\n\n readonly required = input(false, { transform: booleanAttribute });\n readonly disabled = input(false, { transform: booleanAttribute });\n readonly readonly = input(false, { transform: booleanAttribute });\n readonly viewed = input(false, { transform: booleanAttribute });\n\n // Two-way model — same alias as <sd-input>\n readonly valueModel = model<string | null | undefined>(undefined, { alias: 'model' });\n\n // ==========================================\n // Outputs\n // ==========================================\n readonly sdChange = output<string | null | undefined>();\n\n // ==========================================\n // ViewChild\n // ==========================================\n readonly picker = viewChild<ElementRef<HTMLInputElement>>('picker');\n\n // ==========================================\n // Derived state\n // ==========================================\n // why: <input type=\"color\"> only accepts #RRGGBB. Expand 3-char hex; strip\n // alpha from 8-char. Fallback to #000000 for empty/invalid so the OS picker\n // always opens on something.\n readonly pickerSafeValue = computed<string>(() => {\n const v = this.valueModel();\n if (!v) return '#000000';\n if (/^#[0-9A-Fa-f]{6}$/.test(v)) return v;\n if (/^#[0-9A-Fa-f]{3}$/.test(v)) {\n const r = v[1];\n const g = v[2];\n const b = v[3];\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n if (/^#[0-9A-Fa-f]{8}$/.test(v)) return v.slice(0, 7);\n return '#000000';\n });\n\n // Swatch shows the raw model value when valid; otherwise transparent so the\n // user gets visual feedback that the hex string is malformed.\n readonly swatchColor = computed<string>(() => {\n const v = this.valueModel();\n if (!v) return 'transparent';\n return this.#hexRegex.test(v) ? v : 'transparent';\n });\n\n // ==========================================\n // Handlers\n // ==========================================\n // why: <sd-input> built-in clear bắn null khi clear (thao tác chủ động) → coi\n // như \"không có màu\". Chuỗi rỗng cũng quy về null. Set model = null (không phải\n // undefined): undefined chỉ dành cho trạng thái pristine chưa từng thao tác.\n onInputChange = (v: unknown): void => {\n const str = v == null || v === '' ? null : String(v);\n this.valueModel.set(str);\n this.sdChange.emit(str);\n };\n\n openPicker = (): void => {\n if (this.disabled() || this.readonly() || this.viewed()) return;\n this.picker()?.nativeElement.click();\n };\n\n onPickerChange = (ev: Event): void => {\n const v = (ev.target as HTMLInputElement).value;\n this.valueModel.set(v);\n this.sdChange.emit(v);\n };\n\n // why: nút clear hiển thị giờ do <sd-input> render. Giữ method public này cho\n // consumer gọi tay (programmatic). No-op khi không sửa được hoặc đã rỗng.\n clear = (ev?: Event): void => {\n ev?.stopPropagation();\n if (this.required() || this.disabled() || this.readonly() || this.viewed()) return;\n const v = this.valueModel();\n if (v == null || v === '') return;\n this.valueModel.set(null);\n this.sdChange.emit(null);\n };\n}\n","<sd-input\n [label]=\"label()\"\n [helperText]=\"helperText()\"\n [placeholder]=\"placeholder()\"\n [size]=\"size()\"\n [appearance]=\"appearance() ?? undefined\"\n [form]=\"form()\"\n [name]=\"name() ?? ''\"\n [autoId]=\"autoId()\"\n [required]=\"required()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [viewed]=\"viewed()\"\n [pattern]=\"hexPattern\"\n patternErrorMessage=\"Mã màu HEX không hợp lệ (vd #1565C0)\"\n [model]=\"valueModel()\"\n (sdChange)=\"onInputChange($event)\">\n\n <ng-template sdSuffixDef>\n <div class=\"sd-input-color__suffix\">\n <!-- why: nút clear do <sd-input> tự render (built-in, hover-gated). Ở đây\n chỉ chiếu swatch + native picker. onInputChange coerce '' → null nên\n clear của sd-input vẫn cho ra model rỗng đúng nghĩa. -->\n <button\n type=\"button\"\n class=\"sd-input-color__swatch\"\n [style.background]=\"swatchColor()\"\n [class.sd-input-color__swatch--empty]=\"swatchColor() === 'transparent'\"\n [disabled]=\"disabled() || readonly() || viewed()\"\n (click)=\"openPicker()\"\n title=\"Mở bảng chọn màu\"\n aria-label=\"Mở bảng chọn màu\">\n </button>\n <input\n #picker\n type=\"color\"\n class=\"sd-input-color__hidden-picker\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [value]=\"pickerSafeValue()\"\n (input)=\"onPickerChange($event)\" />\n </div>\n </ng-template>\n</sd-input>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,UAAA,EAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,QAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,QAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EC9BzB,qnDA4CA,EAAA,MAAA,EAAA,CAAA,8xCAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDnBY,OAAO,idAAE,oBAAoB,EAAA,QAAA,EAAA,eAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAK5B,YAAY,EAAA,UAAA,EAAA,CAAA;kBARxB,SAAS;+BACE,gBAAgB,EAAA,UAAA,EACd,IAAI,EAAA,OAAA,EACP,CAAC,OAAO,EAAE,oBAAoB,CAAC,EAAA,eAAA,EAGvB,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,qnDAAA,EAAA,MAAA,EAAA,CAAA,8xCAAA,CAAA,EAAA;42CAoCW,QAAQ,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AEhEpE;;AAEG;;;;"}
1
+ {"version":3,"file":"sdcorejs-angular-forms-input-color.mjs","sources":["../../../projects/sdcorejs-angular/forms/input-color/src/input-color.component.ts","../../../projects/sdcorejs-angular/forms/input-color/src/input-color.component.html","../../../projects/sdcorejs-angular/forms/input-color/sdcorejs-angular-forms-input-color.ts"],"sourcesContent":["import {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n ElementRef,\n input,\n model,\n output,\n viewChild,\n} from '@angular/core';\nimport { MatFormFieldAppearance } from '@angular/material/form-field';\nimport { SdInput } from '@sdcorejs/angular/forms/input';\nimport { SdSuffixDefDirective } from '@sdcorejs/angular/forms/directives';\nimport { SdViewed, SdViewedInput, sdViewedTransform } from '@sdcorejs/angular/forms/models';\nimport { Size } from '@sdcorejs/utils/models';\n\n// why: matches #RGB, #RRGGBB, #RRGGBBAA. Capital + lowercase hex allowed.\n// Native <input type=\"color\"> only emits #RRGGBB, so we canonicalize before\n// feeding the picker (expand 3-char, strip alpha) and let the swatch render\n// the raw model value when it's a valid hex.\nexport const SD_INPUT_COLOR_HEX_PATTERN = '^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$';\n\n@Component({\n selector: 'sd-input-color',\n standalone: true,\n imports: [SdInput, SdSuffixDefDirective],\n templateUrl: './input-color.component.html',\n styleUrls: ['./input-color.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SdInputColor {\n readonly hexPattern = SD_INPUT_COLOR_HEX_PATTERN;\n readonly #hexRegex = new RegExp(SD_INPUT_COLOR_HEX_PATTERN);\n\n // ==========================================\n // Inputs (forwarded to <sd-input>)\n // ==========================================\n readonly label = input<string | undefined>();\n readonly helperText = input<string | undefined>();\n readonly placeholder = input<string>('#RRGGBB');\n readonly size = input<Size>('md');\n readonly appearance = input<MatFormFieldAppearance | undefined>(undefined);\n // why: type as `any` — the inner <sd-input> applies the FormGroup|NgForm transform.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n readonly form = input<any>(undefined);\n readonly name = input<string | undefined>();\n readonly autoId = input<string | undefined>();\n\n readonly required = input(false, { transform: booleanAttribute });\n readonly disabled = input(false, { transform: booleanAttribute });\n readonly readonly = input(false, { transform: booleanAttribute });\n /** Display mode — forwarded to the inner `<sd-input>`. `'inline'` = borderless inline-edit; disabled `'inline'` → static. */\n readonly viewed = input<SdViewed, SdViewedInput>(false, { transform: sdViewedTransform });\n\n // Two-way model — same alias as <sd-input>\n readonly valueModel = model<string | null | undefined>(undefined, { alias: 'model' });\n\n // ==========================================\n // Outputs\n // ==========================================\n readonly sdChange = output<string | null | undefined>();\n\n // ==========================================\n // ViewChild\n // ==========================================\n readonly picker = viewChild<ElementRef<HTMLInputElement>>('picker');\n\n // ==========================================\n // Derived state\n // ==========================================\n // why: <input type=\"color\"> only accepts #RRGGBB. Expand 3-char hex; strip\n // alpha from 8-char. Fallback to #000000 for empty/invalid so the OS picker\n // always opens on something.\n readonly pickerSafeValue = computed<string>(() => {\n const v = this.valueModel();\n if (!v) return '#000000';\n if (/^#[0-9A-Fa-f]{6}$/.test(v)) return v;\n if (/^#[0-9A-Fa-f]{3}$/.test(v)) {\n const r = v[1];\n const g = v[2];\n const b = v[3];\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n if (/^#[0-9A-Fa-f]{8}$/.test(v)) return v.slice(0, 7);\n return '#000000';\n });\n\n // Swatch shows the raw model value when valid; otherwise transparent so the\n // user gets visual feedback that the hex string is malformed.\n readonly swatchColor = computed<string>(() => {\n const v = this.valueModel();\n if (!v) return 'transparent';\n return this.#hexRegex.test(v) ? v : 'transparent';\n });\n\n // ==========================================\n // Handlers\n // ==========================================\n // why: <sd-input> built-in clear bắn null khi clear (thao tác chủ động) → coi\n // như \"không có màu\". Chuỗi rỗng cũng quy về null. Set model = null (không phải\n // undefined): undefined chỉ dành cho trạng thái pristine chưa từng thao tác.\n onInputChange = (v: unknown): void => {\n const str = v == null || v === '' ? null : String(v);\n this.valueModel.set(str);\n this.sdChange.emit(str);\n };\n\n openPicker = (): void => {\n if (this.disabled() || this.readonly() || this.viewed()) return;\n this.picker()?.nativeElement.click();\n };\n\n onPickerChange = (ev: Event): void => {\n const v = (ev.target as HTMLInputElement).value;\n this.valueModel.set(v);\n this.sdChange.emit(v);\n };\n\n // why: nút clear hiển thị giờ do <sd-input> render. Giữ method public này cho\n // consumer gọi tay (programmatic). No-op khi không sửa được hoặc đã rỗng.\n clear = (ev?: Event): void => {\n ev?.stopPropagation();\n if (this.required() || this.disabled() || this.readonly() || this.viewed()) return;\n const v = this.valueModel();\n if (v == null || v === '') return;\n this.valueModel.set(null);\n this.sdChange.emit(null);\n };\n}\n","<sd-input\n [label]=\"label()\"\n [helperText]=\"helperText()\"\n [placeholder]=\"placeholder()\"\n [size]=\"size()\"\n [appearance]=\"appearance() ?? undefined\"\n [form]=\"form()\"\n [name]=\"name() ?? ''\"\n [autoId]=\"autoId()\"\n [required]=\"required()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [viewed]=\"viewed()\"\n [pattern]=\"hexPattern\"\n patternErrorMessage=\"Mã màu HEX không hợp lệ (vd #1565C0)\"\n [model]=\"valueModel()\"\n (sdChange)=\"onInputChange($event)\">\n\n <ng-template sdSuffixDef>\n <div class=\"sd-input-color__suffix\">\n <!-- why: nút clear do <sd-input> tự render (built-in, hover-gated). Ở đây\n chỉ chiếu swatch + native picker. onInputChange coerce '' → null nên\n clear của sd-input vẫn cho ra model rỗng đúng nghĩa. -->\n <button\n type=\"button\"\n class=\"sd-input-color__swatch\"\n [style.background]=\"swatchColor()\"\n [class.sd-input-color__swatch--empty]=\"swatchColor() === 'transparent'\"\n [disabled]=\"disabled() || readonly() || viewed() === true\"\n (click)=\"openPicker()\"\n title=\"Mở bảng chọn màu\"\n aria-label=\"Mở bảng chọn màu\">\n </button>\n <input\n #picker\n type=\"color\"\n class=\"sd-input-color__hidden-picker\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [value]=\"pickerSafeValue()\"\n (input)=\"onPickerChange($event)\" />\n </div>\n </ng-template>\n</sd-input>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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AAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,UAAA,EAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,QAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,QAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EC/BzB,8nDA4CA,EAAA,MAAA,EAAA,CAAA,8xCAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDlBY,OAAO,idAAE,oBAAoB,EAAA,QAAA,EAAA,eAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAK5B,YAAY,EAAA,UAAA,EAAA,CAAA;kBARxB,SAAS;+BACE,gBAAgB,EAAA,UAAA,EACd,IAAI,EAAA,OAAA,EACP,CAAC,OAAO,EAAE,oBAAoB,CAAC,EAAA,eAAA,EAGvB,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,8nDAAA,EAAA,MAAA,EAAA,CAAA,8xCAAA,CAAA,EAAA;42CAqCW,QAAQ,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AElEpE;;AAEG;;;;"}