@spartan-ng/cli 0.0.1-alpha.657 → 0.0.1-alpha.659

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 (64) hide show
  1. package/package.json +1 -1
  2. package/src/generators/healthcheck/generator.js +2 -0
  3. package/src/generators/healthcheck/generator.js.map +1 -1
  4. package/src/generators/healthcheck/healthchecks/hlm-form-field.d.ts +2 -0
  5. package/src/generators/healthcheck/healthchecks/hlm-form-field.js +35 -0
  6. package/src/generators/healthcheck/healthchecks/hlm-form-field.js.map +1 -0
  7. package/src/generators/migrate-form-field/compat.d.ts +2 -0
  8. package/src/generators/migrate-form-field/compat.js +6 -0
  9. package/src/generators/migrate-form-field/compat.js.map +1 -0
  10. package/src/generators/migrate-form-field/generator.d.ts +4 -0
  11. package/src/generators/migrate-form-field/generator.js +60 -0
  12. package/src/generators/migrate-form-field/generator.js.map +1 -0
  13. package/src/generators/migrate-form-field/schema.d.ts +4 -0
  14. package/src/generators/migrate-form-field/schema.json +19 -0
  15. package/src/generators/ui/libs/autocomplete/files/lib/hlm-autocomplete-input.ts.template +9 -4
  16. package/src/generators/ui/libs/badge/files/lib/hlm-badge.ts.template +7 -8
  17. package/src/generators/ui/libs/card/files/lib/hlm-card-action.ts.template +1 -1
  18. package/src/generators/ui/libs/card/files/lib/hlm-card-content.ts.template +1 -1
  19. package/src/generators/ui/libs/card/files/lib/hlm-card-description.ts.template +1 -1
  20. package/src/generators/ui/libs/card/files/lib/hlm-card-footer.ts.template +1 -4
  21. package/src/generators/ui/libs/card/files/lib/hlm-card-header.ts.template +1 -1
  22. package/src/generators/ui/libs/card/files/lib/hlm-card-title.ts.template +1 -1
  23. package/src/generators/ui/libs/card/files/lib/hlm-card.ts.template +1 -4
  24. package/src/generators/ui/libs/checkbox/files/lib/hlm-checkbox.ts.template +18 -2
  25. package/src/generators/ui/libs/combobox/files/lib/hlm-combobox-chip-input.ts.template +1 -1
  26. package/src/generators/ui/libs/combobox/files/lib/hlm-combobox-chips.ts.template +22 -7
  27. package/src/generators/ui/libs/combobox/files/lib/hlm-combobox-input.ts.template +8 -4
  28. package/src/generators/ui/libs/combobox/files/lib/hlm-combobox-trigger.ts.template +10 -1
  29. package/src/generators/ui/libs/date-picker/files/lib/hlm-date-picker-multi.ts.template +38 -5
  30. package/src/generators/ui/libs/date-picker/files/lib/hlm-date-picker.ts.template +36 -6
  31. package/src/generators/ui/libs/date-picker/files/lib/hlm-date-range-picker.ts.template +33 -4
  32. package/src/generators/ui/libs/field/files/index.ts.template +3 -3
  33. package/src/generators/ui/libs/field/files/lib/hlm-field-content.ts.template +1 -1
  34. package/src/generators/ui/libs/field/files/lib/hlm-field-description.ts.template +40 -2
  35. package/src/generators/ui/libs/field/files/lib/hlm-field-error.ts.template +89 -27
  36. package/src/generators/ui/libs/field/files/lib/hlm-field-group.ts.template +1 -1
  37. package/src/generators/ui/libs/field/files/lib/hlm-field-legend.ts.template +2 -2
  38. package/src/generators/ui/libs/field/files/lib/hlm-field.ts.template +9 -6
  39. package/src/generators/ui/libs/input/files/lib/hlm-input.ts.template +10 -69
  40. package/src/generators/ui/libs/input-group/files/lib/hlm-input-group.ts.template +15 -5
  41. package/src/generators/ui/libs/label/files/lib/hlm-label.ts.template +4 -1
  42. package/src/generators/ui/libs/native-select/files/lib/hlm-native-select.ts.template +32 -13
  43. package/src/generators/ui/libs/radio-group/files/lib/hlm-radio-group.ts.template +18 -2
  44. package/src/generators/ui/libs/radio-group/files/lib/hlm-radio.ts.template +16 -1
  45. package/src/generators/ui/libs/select/files/lib/hlm-select-trigger.ts.template +5 -2
  46. package/src/generators/ui/libs/slider/files/lib/hlm-slider.ts.template +3 -4
  47. package/src/generators/ui/libs/textarea/files/lib/hlm-textarea.ts.template +10 -71
  48. package/src/generators/ui/primitive-deps.js +0 -1
  49. package/src/generators/ui/primitive-deps.js.map +1 -1
  50. package/src/generators/ui/primitives.d.ts +1 -1
  51. package/src/generators/ui/style-luma.css +1365 -0
  52. package/src/generators/ui/style-lyra.css +24 -24
  53. package/src/generators/ui/style-maia.css +24 -24
  54. package/src/generators/ui/style-mira.css +24 -24
  55. package/src/generators/ui/style-nova.css +24 -24
  56. package/src/generators/ui/style-vega.css +24 -24
  57. package/src/generators/ui/supported-ui-libraries.json +54 -56
  58. package/src/generators/ui/libs/form-field/files/index.ts.template +0 -9
  59. package/src/generators/ui/libs/form-field/files/lib/hlm-error.ts.template +0 -12
  60. package/src/generators/ui/libs/form-field/files/lib/hlm-form-field.ts.template +0 -39
  61. package/src/generators/ui/libs/form-field/files/lib/hlm-hint.ts.template +0 -12
  62. package/src/generators/ui/libs/form-field/generator.d.ts +0 -3
  63. package/src/generators/ui/libs/form-field/generator.js +0 -9
  64. package/src/generators/ui/libs/form-field/generator.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spartan-ng/cli",
3
- "version": "0.0.1-alpha.657",
3
+ "version": "0.0.1-alpha.659",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/spartan-ng/spartan"
@@ -21,6 +21,7 @@ const hlm_1 = require("./healthchecks/hlm");
21
21
  const hlm_date_picker_1 = require("./healthchecks/hlm-date-picker");
22
22
  const hlm_dialog_1 = require("./healthchecks/hlm-dialog");
23
23
  const hlm_dialog_portal_1 = require("./healthchecks/hlm-dialog-portal");
24
+ const hlm_form_field_1 = require("./healthchecks/hlm-form-field");
24
25
  const hlm_icon_1 = require("./healthchecks/hlm-icon");
25
26
  const hlm_menu_1 = require("./healthchecks/hlm-menu");
26
27
  const hlm_progress_1 = require("./healthchecks/hlm-progress");
@@ -60,6 +61,7 @@ async function healthcheckGenerator(tree, options) {
60
61
  hlm_menu_1.helmMenuHealthcheck,
61
62
  hlm_dialog_1.helmDialogHealthcheck,
62
63
  hlm_dialog_portal_1.helmDialogPortalHealthcheck,
64
+ hlm_form_field_1.helmFormFieldHealthcheck,
63
65
  sonner_1.sonnerHealthcheck,
64
66
  brn_select_1.brnSelectHealthcheck,
65
67
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../libs/cli/src/generators/healthcheck/generator.ts"],"names":[],"mappings":";;AAkCA,oDA0DC;AA5FD,uCAA4D;AAC5D,+CAAoD;AACpD,iDAAmH;AACnH,gEAAuE;AACvE,gFAAwF;AACxF,wGAAiG;AACjG,oEAA6E;AAC7E,wDAAiE;AACjE,0DAAiE;AACjE,gEAAyE;AACzE,oGAA6F;AAC7F,0DAAmE;AACnE,sEAA8E;AAC9E,0EAAiF;AACjF,8DAAqE;AACrE,8DAAqE;AACrE,4CAA0D;AAC1D,oEAAuE;AACvE,0DAAkE;AAClE,wEAA+E;AAC/E,sDAA8D;AAC9D,sDAA8D;AAC9D,8DAAkE;AAClE,oEAAuE;AACvE,0DAA8D;AAC9D,kEAAyE;AACzE,0EAAgF;AAChF,kDAA0D;AAC1D,oDAA4D;AAE5D,2CAA4C;AAC5C,+CAA+C;AAC/C,2CAAgD;AAEzC,KAAK,UAAU,oBAAoB,CAAC,IAAU,EAAE,OAA8D;IACpH,eAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IAEvC,MAAM,YAAY,GAAkB;QACnC,4BAAkB;QAClB,uCAAuB;QACvB,qCAAsB;QACtB,8BAAmB;QACnB,uCAAqB;QACrB,iCAAqB;QACrB,8BAAiB;QACjB,8CAA2B;QAC3B,qCAAsB;QACtB,gDAA2B;QAC3B,uCAAqB;QACrB,kCAAmB;QACnB,0BAAoB;QACpB,yCAAyB;QACzB,iEAA6B;QAC7B,6DAA2B;QAC3B,wDAAgC;QAChC,yCAAwB;QACxB,mCAAsB;QACtB,iDAA4B;QAC5B,6CAA2B;QAC3B,8BAAmB;QACnB,kCAAqB;QACrB,+CAA2B;QAC3B,0BAAiB;QACjB,iCAAoB;KACpB,CAAC;IAEF,MAAM,aAAa,GAAwB,EAAE,CAAC;IAE9C,MAAM,WAAW,GAAG,MAAM,IAAA,uBAAc,EAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAEnE,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,MAAM,IAAA,uBAAc,EAAC,IAAI,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;QACpE,IAAA,sBAAW,EAAC,MAAM,CAAC,CAAC;QAEpB,IAAI,MAAM,CAAC,MAAM,KAAK,gCAAiB,CAAC,OAAO,EAAE,CAAC;YACjD,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;IACF,CAAC;IAED,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;QACpC,IAAI,MAAM,CAAC,OAAO,IAAI,IAAA,mCAAoB,EAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;YAChE,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,MAAM,IAAA,mBAAU,EAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;YAE7E,IAAI,GAAG,EAAE,CAAC;gBACT,MAAM,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC;YACrF,CAAC;QACF,CAAC;IACF,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QACzB,MAAM,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;IACzB,CAAC;AACF,CAAC;AAED,kBAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../libs/cli/src/generators/healthcheck/generator.ts"],"names":[],"mappings":";;AAmCA,oDA2DC;AA9FD,uCAA4D;AAC5D,+CAAoD;AACpD,iDAAmH;AACnH,gEAAuE;AACvE,gFAAwF;AACxF,wGAAiG;AACjG,oEAA6E;AAC7E,wDAAiE;AACjE,0DAAiE;AACjE,gEAAyE;AACzE,oGAA6F;AAC7F,0DAAmE;AACnE,sEAA8E;AAC9E,0EAAiF;AACjF,8DAAqE;AACrE,8DAAqE;AACrE,4CAA0D;AAC1D,oEAAuE;AACvE,0DAAkE;AAClE,wEAA+E;AAC/E,kEAAyE;AACzE,sDAA8D;AAC9D,sDAA8D;AAC9D,8DAAkE;AAClE,oEAAuE;AACvE,0DAA8D;AAC9D,kEAAyE;AACzE,0EAAgF;AAChF,kDAA0D;AAC1D,oDAA4D;AAE5D,2CAA4C;AAC5C,+CAA+C;AAC/C,2CAAgD;AAEzC,KAAK,UAAU,oBAAoB,CAAC,IAAU,EAAE,OAA8D;IACpH,eAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IAEvC,MAAM,YAAY,GAAkB;QACnC,4BAAkB;QAClB,uCAAuB;QACvB,qCAAsB;QACtB,8BAAmB;QACnB,uCAAqB;QACrB,iCAAqB;QACrB,8BAAiB;QACjB,8CAA2B;QAC3B,qCAAsB;QACtB,gDAA2B;QAC3B,uCAAqB;QACrB,kCAAmB;QACnB,0BAAoB;QACpB,yCAAyB;QACzB,iEAA6B;QAC7B,6DAA2B;QAC3B,wDAAgC;QAChC,yCAAwB;QACxB,mCAAsB;QACtB,iDAA4B;QAC5B,6CAA2B;QAC3B,8BAAmB;QACnB,kCAAqB;QACrB,+CAA2B;QAC3B,yCAAwB;QACxB,0BAAiB;QACjB,iCAAoB;KACpB,CAAC;IAEF,MAAM,aAAa,GAAwB,EAAE,CAAC;IAE9C,MAAM,WAAW,GAAG,MAAM,IAAA,uBAAc,EAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAEnE,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,MAAM,IAAA,uBAAc,EAAC,IAAI,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;QACpE,IAAA,sBAAW,EAAC,MAAM,CAAC,CAAC;QAEpB,IAAI,MAAM,CAAC,MAAM,KAAK,gCAAiB,CAAC,OAAO,EAAE,CAAC;YACjD,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;IACF,CAAC;IAED,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;QACpC,IAAI,MAAM,CAAC,OAAO,IAAI,IAAA,mCAAoB,EAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;YAChE,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,MAAM,IAAA,mBAAU,EAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;YAE7E,IAAI,GAAG,EAAE,CAAC;gBACT,MAAM,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC;YACrF,CAAC;QACF,CAAC;IACF,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QACzB,MAAM,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;IACzB,CAAC;AACF,CAAC;AAED,kBAAe,oBAAoB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { type Healthcheck } from '../healthchecks';
2
+ export declare const helmFormFieldHealthcheck: Healthcheck;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.helmFormFieldHealthcheck = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const devkit_1 = require("@nx/devkit");
6
+ const generator_1 = tslib_1.__importDefault(require("../../migrate-form-field/generator"));
7
+ const healthchecks_1 = require("../healthchecks");
8
+ exports.helmFormFieldHealthcheck = {
9
+ name: 'Helm Form Field',
10
+ async detect(tree, failure, _, { importAlias }) {
11
+ (0, devkit_1.visitNotIgnoredFiles)(tree, '/', (file) => {
12
+ // if the file is a .ts or .htlm file, check for helm form field
13
+ if (!file.endsWith('.ts') && !file.endsWith('.html')) {
14
+ return;
15
+ }
16
+ const contents = tree.read(file, 'utf-8');
17
+ if (!contents) {
18
+ return;
19
+ }
20
+ if (contents.includes("'@spartan-ng/helm/form-field'") ||
21
+ contents.includes(`"${importAlias}/form-field"`) ||
22
+ contents.includes(`hlm-form-field`) ||
23
+ contents.includes(`hlm-hint`) ||
24
+ contents.includes(`hlm-error`)) {
25
+ failure(`The form field package (${importAlias}/form-field) is deprecated. Please use the @spartan-ng/helm/field component instead.`, healthchecks_1.HealthcheckSeverity.Error, true);
26
+ }
27
+ });
28
+ },
29
+ fix: async (tree, { importAlias }) => {
30
+ await (0, generator_1.default)(tree, { skipFormat: true, importAlias });
31
+ return true;
32
+ },
33
+ prompt: 'Would you like to migrate form field imports and selectors? You need to generate the helm components for fields.',
34
+ };
35
+ //# sourceMappingURL=hlm-form-field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hlm-form-field.js","sourceRoot":"","sources":["../../../../../../../libs/cli/src/generators/healthcheck/healthchecks/hlm-form-field.ts"],"names":[],"mappings":";;;;AAAA,uCAAkD;AAClD,2FAA2E;AAC3E,kDAAwE;AAE3D,QAAA,wBAAwB,GAAgB;IACpD,IAAI,EAAE,iBAAiB;IACvB,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,WAAW,EAAE;QAC7C,IAAA,6BAAoB,EAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE;YACxC,gEAAgE;YAChE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBACtD,OAAO;YACR,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAE1C,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACf,OAAO;YACR,CAAC;YAED,IACC,QAAQ,CAAC,QAAQ,CAAC,+BAA+B,CAAC;gBAClD,QAAQ,CAAC,QAAQ,CAAC,IAAI,WAAW,cAAc,CAAC;gBAChD,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,CAAC;gBACnC,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC;gBAC7B,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,EAC7B,CAAC;gBACF,OAAO,CACN,2BAA2B,WAAW,sFAAsF,EAC5H,kCAAmB,CAAC,KAAK,EACzB,IAAI,CACJ,CAAC;YACH,CAAC;QACF,CAAC,CAAC,CAAC;IACJ,CAAC;IACD,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE;QACpC,MAAM,IAAA,mBAAyB,EAAC,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;QACzE,OAAO,IAAI,CAAC;IACb,CAAC;IACD,MAAM,EACL,kHAAkH;CACnH,CAAC"}
@@ -0,0 +1,2 @@
1
+ declare const _default: (generatorOptions: import("./schema").MigrateFormFieldGeneratorSchema) => (tree: any, context: any) => Promise<any>;
2
+ export default _default;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const devkit_1 = require("@nx/devkit");
4
+ const generator_1 = require("./generator");
5
+ exports.default = (0, devkit_1.convertNxGenerator)(generator_1.migrateFormFieldGenerator);
6
+ //# sourceMappingURL=compat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compat.js","sourceRoot":"","sources":["../../../../../../libs/cli/src/generators/migrate-form-field/compat.ts"],"names":[],"mappings":";;AAAA,uCAAgD;AAChD,2CAAwD;AAExD,kBAAe,IAAA,2BAAkB,EAAC,qCAAyB,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { type Tree } from '@nx/devkit';
2
+ import type { MigrateFormFieldGeneratorSchema } from './schema';
3
+ export declare function migrateFormFieldGenerator(tree: Tree, { skipFormat, importAlias }: MigrateFormFieldGeneratorSchema): Promise<void>;
4
+ export default migrateFormFieldGenerator;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.migrateFormFieldGenerator = migrateFormFieldGenerator;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const import_alias_1 = require("../../utils/import-alias");
6
+ const visit_files_1 = require("../../utils/visit-files");
7
+ async function migrateFormFieldGenerator(tree, { skipFormat, importAlias }) {
8
+ updateImports(tree, importAlias);
9
+ replaceSelector(tree);
10
+ if (!skipFormat) {
11
+ await (0, devkit_1.formatFiles)(tree);
12
+ }
13
+ }
14
+ /**
15
+ * Migrate @spartan-ng/hlm/form-field imports to @spartan-ng/hlm/field
16
+ */
17
+ function updateImports(tree, importAlias) {
18
+ (0, visit_files_1.visitFiles)(tree, '/', (path) => {
19
+ // if this is not a typescript file then skip
20
+ if (!path.endsWith('.ts')) {
21
+ return;
22
+ }
23
+ let content = tree.read(path, 'utf-8');
24
+ if (!content) {
25
+ return;
26
+ }
27
+ const formFieldImport = (0, import_alias_1.helmImport)(importAlias, 'form-field');
28
+ const fieldImport = (0, import_alias_1.helmImport)(importAlias, 'field');
29
+ if (content.includes(formFieldImport)) {
30
+ content = content
31
+ .replace(`import { HlmFormFieldImports } from '${formFieldImport}';`, `import { HlmFieldImports } from '${fieldImport}';`)
32
+ .replace(/HlmFormFieldImports/, 'HlmFieldImports');
33
+ }
34
+ tree.write(path, content);
35
+ });
36
+ }
37
+ function replaceSelector(tree) {
38
+ (0, visit_files_1.visitFiles)(tree, '.', (path) => {
39
+ // if this is not an html file or typescript file (inline templates) then skip
40
+ if (!path.endsWith('.html') && !path.endsWith('.ts')) {
41
+ return;
42
+ }
43
+ let content = tree.read(path, 'utf-8');
44
+ if (!content) {
45
+ return;
46
+ }
47
+ if (content.includes('hlm-form-field')) {
48
+ content = content.replace(/hlm-form-field/g, 'hlm-field');
49
+ }
50
+ if (content.includes('hlm-hint')) {
51
+ content = content.replace(/<hlm-hint/g, '<p hlmFieldDescription').replace(/<\/hlm-hint>/g, '</p>');
52
+ }
53
+ if (content.includes('hlm-error')) {
54
+ content = content.replace(/hlm-error/g, 'hlm-field-error');
55
+ }
56
+ tree.write(path, content);
57
+ });
58
+ }
59
+ exports.default = migrateFormFieldGenerator;
60
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../libs/cli/src/generators/migrate-form-field/generator.ts"],"names":[],"mappings":";;AAKA,8DAUC;AAfD,uCAAoD;AACpD,2DAAsD;AACtD,yDAAqD;AAG9C,KAAK,UAAU,yBAAyB,CAC9C,IAAU,EACV,EAAE,UAAU,EAAE,WAAW,EAAmC;IAE5D,aAAa,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACjC,eAAe,CAAC,IAAI,CAAC,CAAC;IAEtB,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,MAAM,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;IACzB,CAAC;AACF,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,IAAU,EAAE,WAAmB;IACrD,IAAA,wBAAU,EAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE;QAC9B,6CAA6C;QAC7C,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO;QACR,CAAC;QAED,IAAI,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAEvC,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,OAAO;QACR,CAAC;QAED,MAAM,eAAe,GAAG,IAAA,yBAAU,EAAC,WAAW,EAAE,YAAY,CAAC,CAAC;QAC9D,MAAM,WAAW,GAAG,IAAA,yBAAU,EAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAErD,IAAI,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;YACvC,OAAO,GAAG,OAAO;iBACf,OAAO,CACP,wCAAwC,eAAe,IAAI,EAC3D,oCAAoC,WAAW,IAAI,CACnD;iBACA,OAAO,CAAC,qBAAqB,EAAE,iBAAiB,CAAC,CAAC;QACrD,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,IAAU;IAClC,IAAA,wBAAU,EAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE;QAC9B,8EAA8E;QAC9E,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACtD,OAAO;QACR,CAAC;QAED,IAAI,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAEvC,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,OAAO;QACR,CAAC;QAED,IAAI,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACxC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YAClC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,wBAAwB,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;QACpG,CAAC;QACD,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YACnC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC;QAC5D,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,kBAAe,yBAAyB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export interface MigrateFormFieldGeneratorSchema {
2
+ skipFormat?: boolean;
3
+ importAlias: string;
4
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema",
3
+ "$id": "MigrateFormField",
4
+ "title": "",
5
+ "type": "object",
6
+ "properties": {
7
+ "skipFormat": {
8
+ "type": "boolean",
9
+ "default": false,
10
+ "description": "Skip formatting files"
11
+ },
12
+ "importAlias": {
13
+ "type": "string",
14
+ "default": "@spartan-ui/helm",
15
+ "description": "The prefix to use for helm imports"
16
+ }
17
+ },
18
+ "required": []
19
+ }
@@ -22,8 +22,9 @@ import { HlmInputGroupImports } from '<%- importAlias %>/input-group';
22
22
  brnAutocompleteInput
23
23
  #autocompleteInput="brnAutocompleteInput"
24
24
  hlmInputGroupInput
25
+ [id]="inputId()"
25
26
  [placeholder]="placeholder()"
26
- [attr.aria-invalid]="ariaInvalid() ? 'true' : null"
27
+ [aria-invalid]="ariaInvalidOverride()"
27
28
  />
28
29
 
29
30
  @if (showSearch()) {
@@ -51,14 +52,18 @@ import { HlmInputGroupImports } from '<%- importAlias %>/input-group';
51
52
  `,
52
53
  })
53
54
  export class HlmAutocompleteInput {
55
+ private static _id = 0;
56
+
57
+ public readonly inputId = input<string>(`hlm-autocomplete-input-${HlmAutocompleteInput._id++}`);
58
+
54
59
  public readonly placeholder = input<string>('');
55
60
 
56
61
  public readonly showSearch = input<boolean, BooleanInput>(true, { transform: booleanAttribute });
57
62
  public readonly showClear = input<boolean, BooleanInput>(false, { transform: booleanAttribute });
58
63
 
59
- // TODO input and input-group styles need to support aria-invalid directly
60
- public readonly ariaInvalid = input<boolean, BooleanInput>(false, {
61
- transform: booleanAttribute,
64
+ /** Manual override for aria-invalid. When not set, auto-detects from the parent autocomplete error state. */
65
+ public readonly ariaInvalidOverride = input<boolean | undefined, BooleanInput>(undefined, {
66
+ transform: (v: BooleanInput) => (v === '' || v === undefined ? undefined : booleanAttribute(v)),
62
67
  alias: 'aria-invalid',
63
68
  });
64
69
  }
@@ -3,17 +3,16 @@ import { classes } from '<%- importAlias %>/utils';
3
3
  import { type VariantProps, cva } from 'class-variance-authority';
4
4
 
5
5
  const badgeVariants = cva(
6
- 'focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:ring-[3px] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>ng-icon]:pointer-events-none [&>ng-icon]:text-xs',
6
+ 'spartan-badge group/badge focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 inline-flex w-fit shrink-0 items-center justify-center overflow-hidden whitespace-nowrap focus-visible:ring-[3px] [&>ng-icon]:pointer-events-none',
7
7
  {
8
8
  variants: {
9
9
  variant: {
10
- default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
11
- secondary: 'bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80',
12
- destructive:
13
- 'bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20',
14
- outline: 'border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground',
15
- ghost: 'hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50',
16
- link: 'text-primary underline-offset-4 hover:underline',
10
+ default: 'spartan-badge-variant-default',
11
+ secondary: 'spartan-badge-variant-secondary',
12
+ destructive: 'spartan-badge-variant-destructive',
13
+ outline: 'spartan-badge-variant-outline',
14
+ ghost: 'spartan-badge-variant-ghost',
15
+ link: 'spartan-badge-variant-link',
17
16
  },
18
17
  },
19
18
  defaultVariants: {
@@ -9,6 +9,6 @@ import { classes } from '<%- importAlias %>/utils';
9
9
  })
10
10
  export class HlmCardAction {
11
11
  constructor() {
12
- classes(() => 'col-start-2 row-span-2 row-start-1 self-start justify-self-end');
12
+ classes(() => 'spartan-card-action col-start-2 row-span-2 row-start-1 self-start justify-self-end');
13
13
  }
14
14
  }
@@ -9,6 +9,6 @@ import { classes } from '<%- importAlias %>/utils';
9
9
  })
10
10
  export class HlmCardContent {
11
11
  constructor() {
12
- classes(() => 'px-6 group-data-[size=sm]/card:px-4');
12
+ classes(() => 'spartan-card-content');
13
13
  }
14
14
  }
@@ -9,6 +9,6 @@ import { classes } from '<%- importAlias %>/utils';
9
9
  })
10
10
  export class HlmCardDescription {
11
11
  constructor() {
12
- classes(() => 'text-muted-foreground text-sm');
12
+ classes(() => 'spartan-card-description');
13
13
  }
14
14
  }
@@ -9,9 +9,6 @@ import { classes } from '<%- importAlias %>/utils';
9
9
  })
10
10
  export class HlmCardFooter {
11
11
  constructor() {
12
- classes(
13
- () =>
14
- 'flex items-center rounded-b-xl px-6 group-data-[size=sm]/card:px-4 [.border-t]:pt-6 group-data-[size=sm]/card:[.border-t]:pt-4',
15
- );
12
+ classes(() => 'spartan-card-footer flex items-center');
16
13
  }
17
14
  }
@@ -11,7 +11,7 @@ export class HlmCardHeader {
11
11
  constructor() {
12
12
  classes(
13
13
  () =>
14
- `group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-6 group-data-[size=sm]/card:px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-6 group-data-[size=sm]/card:[.border-b]:pb-4`,
14
+ `spartan-card-header group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]`,
15
15
  );
16
16
  }
17
17
  }
@@ -9,6 +9,6 @@ import { classes } from '<%- importAlias %>/utils';
9
9
  })
10
10
  export class HlmCardTitle {
11
11
  constructor() {
12
- classes(() => 'text-base leading-normal font-medium group-data-[size=sm]/card:text-sm');
12
+ classes(() => 'spartan-card-title');
13
13
  }
14
14
  }
@@ -12,9 +12,6 @@ export class HlmCard {
12
12
  public readonly size = input<'sm' | 'default'>('default');
13
13
 
14
14
  constructor() {
15
- classes(
16
- () =>
17
- 'group/card ring-foreground/10 bg-card text-card-foreground flex flex-col gap-6 overflow-hidden rounded-xl py-6 text-sm shadow-xs ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl',
18
- );
15
+ classes(() => 'spartan-card group/card flex flex-col');
19
16
  }
20
17
  }
@@ -9,11 +9,13 @@ import {
9
9
  linkedSignal,
10
10
  model,
11
11
  output,
12
+ viewChild,
12
13
  } from '@angular/core';
13
14
  import { type ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
14
15
  import { NgIcon, provideIcons } from '@ng-icons/core';
15
16
  import { lucideCheck } from '@ng-icons/lucide';
16
17
  import { BrnCheckbox } from '@spartan-ng/brain/checkbox';
18
+ import { BrnFieldControlDescribedBy } from '@spartan-ng/brain/field';
17
19
  import type { ChangeFn, TouchFn } from '@spartan-ng/brain/forms';
18
20
  import { HlmIcon } from '<%- importAlias %>/icon';
19
21
  import { hlm } from '<%- importAlias %>/utils';
@@ -31,13 +33,13 @@ export const HLM_CHECKBOX_VALUE_ACCESSOR = {
31
33
  providers: [HLM_CHECKBOX_VALUE_ACCESSOR],
32
34
  viewProviders: [provideIcons({ lucideCheck })],
33
35
  changeDetection: ChangeDetectionStrategy.OnPush,
36
+ hostDirectives: [BrnFieldControlDescribedBy],
34
37
  host: {
35
38
  class: 'contents peer',
36
39
  'data-slot': 'checkbox',
37
40
  '[attr.id]': 'null',
38
41
  '[attr.aria-label]': 'null',
39
42
  '[attr.aria-labelledby]': 'null',
40
- '[attr.aria-describedby]': 'null',
41
43
  '[attr.data-disabled]': '_disabled() ? "" : null',
42
44
  },
43
45
  template: `
@@ -52,6 +54,7 @@ export const HLM_CHECKBOX_VALUE_ACCESSOR = {
52
54
  [aria-label]="ariaLabel()"
53
55
  [aria-labelledby]="ariaLabelledby()"
54
56
  [aria-describedby]="ariaDescribedby()"
57
+ [forceInvalid]="forceInvalid()"
55
58
  (checkedChange)="_handleChange($event)"
56
59
  (touched)="_onTouched?.()"
57
60
  >
@@ -68,9 +71,10 @@ export class HlmCheckbox implements ControlValueAccessor {
68
71
 
69
72
  protected readonly _computedClass = computed(() =>
70
73
  hlm(
71
- 'border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive peer size-4 shrink-0 cursor-default rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
74
+ 'border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 peer size-4 shrink-0 cursor-default rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
72
75
  this.userClass(),
73
76
  this._disabled() ? 'cursor-not-allowed opacity-50' : '',
77
+ this._errorStateClass(),
74
78
  ),
75
79
  );
76
80
 
@@ -107,8 +111,20 @@ export class HlmCheckbox implements ControlValueAccessor {
107
111
  /** Whether the checkbox is disabled. */
108
112
  public readonly disabled = input<boolean, BooleanInput>(false, { transform: booleanAttribute });
109
113
 
114
+ /** Whether to force the checkbox into an invalid state. */
115
+ public readonly forceInvalid = input<boolean, BooleanInput>(false, { transform: booleanAttribute });
116
+
110
117
  protected readonly _disabled = linkedSignal(this.disabled);
111
118
 
119
+ private readonly _brnCheckbox = viewChild.required(BrnCheckbox);
120
+
121
+ private readonly _spartanInvalid = computed(() => this.forceInvalid() || this._brnCheckbox().spartanInvalid?.());
122
+ protected readonly _errorStateClass = computed(() =>
123
+ this._spartanInvalid()
124
+ ? 'border-destructive focus-visible:border-destructive focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40'
125
+ : '',
126
+ );
127
+
112
128
  protected _onChange?: ChangeFn<boolean>;
113
129
  protected _onTouched?: TouchFn;
114
130
 
@@ -4,7 +4,7 @@ import { classes } from '<%- importAlias %>/utils';
4
4
 
5
5
  @Directive({
6
6
  selector: 'input[hlmComboboxChipInput]',
7
- hostDirectives: [{ directive: BrnComboboxChipInput, inputs: ['id'] }],
7
+ hostDirectives: [{ directive: BrnComboboxChipInput, inputs: ['id', 'aria-invalid'] }],
8
8
  host: {
9
9
  'data-slott': 'combobox-chip-input',
10
10
  },
@@ -1,19 +1,34 @@
1
- import { Directive } from '@angular/core';
2
- import { BrnComboboxAnchor, BrnComboboxInputWrapper, BrnComboboxPopoverTrigger } from '@spartan-ng/brain/combobox';
1
+ import { computed, Directive } from '@angular/core';
2
+ import {
3
+ BrnComboboxAnchor,
4
+ BrnComboboxInputWrapper,
5
+ BrnComboboxPopoverTrigger,
6
+ injectBrnComboboxBase,
7
+ } from '@spartan-ng/brain/combobox';
3
8
  import { classes } from '<%- importAlias %>/utils';
4
9
 
5
10
  @Directive({
6
11
  selector: '[hlmComboboxChips],hlm-combobox-chips',
7
12
  hostDirectives: [BrnComboboxInputWrapper, BrnComboboxAnchor, BrnComboboxPopoverTrigger],
8
13
  host: {
9
- 'data-slott': 'combobox-chips',
14
+ 'data-slot': 'combobox-chips',
10
15
  },
11
16
  })
12
17
  export class HlmComboboxChips {
18
+ private readonly _combobox = injectBrnComboboxBase();
19
+
20
+ protected readonly _spartanInvalid = computed(() => this._combobox.controlState?.()?.spartanInvalid);
21
+
22
+ protected readonly _errorStateClass = computed(() =>
23
+ this._spartanInvalid?.()
24
+ ? 'border-destructive focus-within:border-destructive focus-within:ring-destructive/20 dark:focus-within:ring-destructive/40'
25
+ : '',
26
+ );
27
+
13
28
  constructor() {
14
- classes(
15
- () =>
16
- 'dark:bg-input/30 border-input focus-within:border-ring focus-within:ring-ring/50 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive dark:has-aria-invalid:border-destructive/50 has-data-[slot=combobox-chip]:px-1.5; flex min-h-9 flex-wrap items-center gap-1.5 rounded-md border bg-transparent bg-clip-padding px-2.5 py-1.5 text-sm shadow-xs transition-[color,box-shadow] focus-within:ring-[3px] has-aria-invalid:ring-[3px]',
17
- );
29
+ classes(() => [
30
+ 'dark:bg-input/30 border-input focus-within:border-ring focus-within:ring-ring/50 has-data-[slot=combobox-chip]:px-1.5; flex min-h-9 flex-wrap items-center gap-1.5 rounded-md border bg-transparent bg-clip-padding px-2.5 py-1.5 text-sm shadow-xs transition-[color,box-shadow] focus-within:ring-[3px]',
31
+ this._errorStateClass(),
32
+ ]);
18
33
  }
19
34
  }
@@ -18,8 +18,9 @@ import { HlmInputGroupImports } from '<%- importAlias %>/input-group';
18
18
  #comboboxInput="brnComboboxInput"
19
19
  brnComboboxPopoverTrigger
20
20
  hlmInputGroupInput
21
+ [id]="inputId()"
21
22
  [placeholder]="placeholder()"
22
- [attr.aria-invalid]="ariaInvalid() ? 'true' : null"
23
+ [aria-invalid]="ariaInvalidOverride()"
23
24
  />
24
25
 
25
26
  <hlm-input-group-addon align="inline-end">
@@ -56,14 +57,17 @@ import { HlmInputGroupImports } from '<%- importAlias %>/input-group';
56
57
  `,
57
58
  })
58
59
  export class HlmComboboxInput {
60
+ private static _id = 0;
61
+
62
+ public readonly inputId = input<string>(`hlm-combobox-input-${HlmComboboxInput._id++}`);
59
63
  public readonly placeholder = input<string>('');
60
64
 
61
65
  public readonly showTrigger = input<boolean, BooleanInput>(true, { transform: booleanAttribute });
62
66
  public readonly showClear = input<boolean, BooleanInput>(false, { transform: booleanAttribute });
63
67
 
64
- // TODO input and input-group styles need to support aria-invalid directly
65
- public readonly ariaInvalid = input<boolean, BooleanInput>(false, {
66
- transform: booleanAttribute,
68
+ /** Manual override for aria-invalid. When not set, auto-detects from the parent combobox error state. */
69
+ public readonly ariaInvalidOverride = input<boolean | undefined, BooleanInput>(undefined, {
70
+ transform: (v: BooleanInput) => (v === '' || v === undefined ? undefined : booleanAttribute(v)),
67
71
  alias: 'aria-invalid',
68
72
  });
69
73
  }
@@ -7,13 +7,21 @@ import {
7
7
  BrnComboboxPopoverTrigger,
8
8
  BrnComboboxTrigger,
9
9
  } from '@spartan-ng/brain/combobox';
10
+ import { BrnFieldControlDescribedBy } from '@spartan-ng/brain/field';
10
11
  import { ButtonVariants, HlmButton } from '<%- importAlias %>/button';
11
12
  import { hlm } from '<%- importAlias %>/utils';
12
13
  import type { ClassValue } from 'clsx';
13
14
 
14
15
  @Component({
15
16
  selector: 'hlm-combobox-trigger',
16
- imports: [NgIcon, HlmButton, BrnComboboxAnchor, BrnComboboxTrigger, BrnComboboxPopoverTrigger],
17
+ imports: [
18
+ NgIcon,
19
+ HlmButton,
20
+ BrnComboboxAnchor,
21
+ BrnComboboxTrigger,
22
+ BrnComboboxPopoverTrigger,
23
+ BrnFieldControlDescribedBy,
24
+ ],
17
25
  providers: [provideIcons({ lucideChevronDown })],
18
26
  changeDetection: ChangeDetectionStrategy.OnPush,
19
27
  hostDirectives: [BrnComboboxInputWrapper],
@@ -22,6 +30,7 @@ import type { ClassValue } from 'clsx';
22
30
  brnComboboxTrigger
23
31
  brnComboboxAnchor
24
32
  brnComboboxPopoverTrigger
33
+ brnFieldControlDescribedBy
25
34
  hlmBtn
26
35
  data-slot="combobox-trigger"
27
36
  [id]="buttonId()"
@@ -5,6 +5,7 @@ import {
5
5
  Component,
6
6
  computed,
7
7
  forwardRef,
8
+ inject,
8
9
  input,
9
10
  linkedSignal,
10
11
  numberAttribute,
@@ -15,8 +16,8 @@ import { type ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
15
16
  import { provideIcons } from '@ng-icons/core';
16
17
  import { lucideChevronDown } from '@ng-icons/lucide';
17
18
  import type { BrnDialogState } from '@spartan-ng/brain/dialog';
19
+ import { BrnFieldControl, BrnFieldControlDescribedBy, provideBrnLabelable } from '@spartan-ng/brain/field';
18
20
  import type { ChangeFn, TouchFn } from '@spartan-ng/brain/forms';
19
-
20
21
  import { HlmCalendarMulti } from '<%- importAlias %>/calendar';
21
22
  import { HlmIconImports } from '<%- importAlias %>/icon';
22
23
  import { HlmPopoverImports } from '<%- importAlias %>/popover';
@@ -34,20 +35,36 @@ let nextId = 0;
34
35
 
35
36
  @Component({
36
37
  selector: 'hlm-date-picker-multi',
37
- imports: [HlmIconImports, HlmPopoverImports, HlmCalendarMulti],
38
- providers: [HLM_DATE_PICKER_MUTLI_VALUE_ACCESSOR, provideIcons({ lucideChevronDown })],
38
+ imports: [HlmIconImports, HlmPopoverImports, HlmCalendarMulti, BrnFieldControlDescribedBy],
39
+ providers: [
40
+ HLM_DATE_PICKER_MUTLI_VALUE_ACCESSOR,
41
+ provideIcons({ lucideChevronDown }),
42
+ provideBrnLabelable(HlmDatePickerMulti),
43
+ ],
39
44
  changeDetection: ChangeDetectionStrategy.OnPush,
45
+ hostDirectives: [BrnFieldControl],
40
46
  host: {
41
47
  class: 'block',
42
48
  },
43
49
  template: `
44
- <hlm-popover sideOffset="5" [state]="_popoverState()" (stateChanged)="_popoverState.set($event)">
50
+ <hlm-popover
51
+ sideOffset="5"
52
+ [state]="_popoverState()"
53
+ (stateChanged)="_popoverState.set($event)"
54
+ (closed)="_onTouched?.()"
55
+ >
45
56
  <button
46
57
  [id]="buttonId()"
47
58
  type="button"
48
59
  [class]="_computedClass()"
49
60
  [disabled]="_mutableDisabled()"
61
+ [attr.aria-invalid]="_ariaInvalid()"
62
+ [attr.data-invalid]="_ariaInvalid()"
63
+ [attr.data-dirty]="_dirty?.() ? 'true' : null"
64
+ [attr.data-touched]="_touched?.() ? 'true' : null"
65
+ [attr.data-matches-spartan-invalid]="_spartanInvalid?.() ? 'true' : null"
50
66
  hlmPopoverTrigger
67
+ brnFieldControlDescribedBy
51
68
  >
52
69
  <span class="truncate">
53
70
  @if (_formattedDate(); as formattedDate) {
@@ -78,14 +95,28 @@ let nextId = 0;
78
95
  })
79
96
  export class HlmDatePickerMulti<T> implements ControlValueAccessor {
80
97
  private readonly _config = injectHlmDatePickerMultiConfig<T>();
98
+ private readonly _fieldControl = inject(BrnFieldControl, { optional: true });
99
+
100
+ protected readonly _spartanInvalid = this._fieldControl?.spartanInvalid;
101
+ protected readonly _dirty = this._fieldControl?.dirty;
102
+ protected readonly _touched = this._fieldControl?.touched;
103
+ private readonly _invalid = this._fieldControl?.invalid;
104
+
105
+ protected readonly _errorStateClass = computed(() =>
106
+ this._spartanInvalid?.()
107
+ ? 'border-destructive focus-visible:border-destructive focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40'
108
+ : '',
109
+ );
110
+ protected readonly _ariaInvalid = computed(() => (this._invalid?.() ? 'true' : null));
81
111
 
82
112
  public readonly userClass = input<ClassValue>('', { alias: 'class' });
83
113
  protected readonly _computedClass = computed(() =>
84
114
  hlm(
85
- 'ring-offset-background border-input bg-background hover:bg-accent dark:bg-input/30 dark:hover:bg-input/50 hover:text-accent-foreground inline-flex h-9 w-[280px] cursor-default items-center justify-between gap-2 rounded-md border px-3 py-2 text-left text-sm font-normal whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50',
115
+ 'ring-offset-background border-input bg-background hover:bg-accent dark:bg-input/30 dark:hover:bg-input/50 inline-flex h-9 w-[280px] cursor-default items-center justify-between gap-2 rounded-md border px-3 py-2 text-left text-sm font-normal whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50',
86
116
  'focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none',
87
117
  'disabled:pointer-events-none disabled:opacity-50',
88
118
  '[&_ng-icon]:pointer-events-none [&_ng-icon]:shrink-0',
119
+ this._errorStateClass(),
89
120
  this.userClass(),
90
121
  ),
91
122
  );
@@ -144,6 +175,8 @@ export class HlmDatePickerMulti<T> implements ControlValueAccessor {
144
175
 
145
176
  public readonly dateChange = output<T[]>();
146
177
 
178
+ public readonly labelableId = this.buttonId;
179
+
147
180
  protected _onChange?: ChangeFn<T[]>;
148
181
  protected _onTouched?: TouchFn;
149
182