@ngstarter-ui/components 21.0.41 → 21.0.43

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 (42) hide show
  1. package/README.md +0 -1
  2. package/ai/component-registry.json +67 -111
  3. package/fesm2022/ngstarter-ui-components-country-select.mjs +59 -16
  4. package/fesm2022/ngstarter-ui-components-country-select.mjs.map +1 -1
  5. package/fesm2022/ngstarter-ui-components-form-builder.mjs +432 -34
  6. package/fesm2022/ngstarter-ui-components-form-builder.mjs.map +1 -1
  7. package/fesm2022/ngstarter-ui-components-radio.mjs +8 -5
  8. package/fesm2022/ngstarter-ui-components-radio.mjs.map +1 -1
  9. package/fesm2022/ngstarter-ui-components-slide-toggle.mjs +12 -1
  10. package/fesm2022/ngstarter-ui-components-slide-toggle.mjs.map +1 -1
  11. package/package.json +1 -5
  12. package/types/ngstarter-ui-components-country-select.d.ts +20 -9
  13. package/types/ngstarter-ui-components-form-builder.d.ts +63 -5
  14. package/types/ngstarter-ui-components-radio.d.ts +4 -1
  15. package/types/ngstarter-ui-components-slide-toggle.d.ts +6 -1
  16. package/fesm2022/ngstarter-ui-components-form-renderer-autocomplete-many-field-CGQ3pJaG.mjs +0 -124
  17. package/fesm2022/ngstarter-ui-components-form-renderer-autocomplete-many-field-CGQ3pJaG.mjs.map +0 -1
  18. package/fesm2022/ngstarter-ui-components-form-renderer-checkbox-field-CoyKdvhV.mjs +0 -22
  19. package/fesm2022/ngstarter-ui-components-form-renderer-checkbox-field-CoyKdvhV.mjs.map +0 -1
  20. package/fesm2022/ngstarter-ui-components-form-renderer-datepicker-field-Cf3jfPv7.mjs +0 -44
  21. package/fesm2022/ngstarter-ui-components-form-renderer-datepicker-field-Cf3jfPv7.mjs.map +0 -1
  22. package/fesm2022/ngstarter-ui-components-form-renderer-divider-content-D8Sffofu.mjs +0 -17
  23. package/fesm2022/ngstarter-ui-components-form-renderer-divider-content-D8Sffofu.mjs.map +0 -1
  24. package/fesm2022/ngstarter-ui-components-form-renderer-image-content-ICTwkZPa.mjs +0 -17
  25. package/fesm2022/ngstarter-ui-components-form-renderer-image-content-ICTwkZPa.mjs.map +0 -1
  26. package/fesm2022/ngstarter-ui-components-form-renderer-input-field-tZHU8-_L.mjs +0 -35
  27. package/fesm2022/ngstarter-ui-components-form-renderer-input-field-tZHU8-_L.mjs.map +0 -1
  28. package/fesm2022/ngstarter-ui-components-form-renderer-radio-group-field-lc1V-NeL.mjs +0 -38
  29. package/fesm2022/ngstarter-ui-components-form-renderer-radio-group-field-lc1V-NeL.mjs.map +0 -1
  30. package/fesm2022/ngstarter-ui-components-form-renderer-select-field-DyBidcIU.mjs +0 -39
  31. package/fesm2022/ngstarter-ui-components-form-renderer-select-field-DyBidcIU.mjs.map +0 -1
  32. package/fesm2022/ngstarter-ui-components-form-renderer-text-content-BjzH_M3-.mjs +0 -24
  33. package/fesm2022/ngstarter-ui-components-form-renderer-text-content-BjzH_M3-.mjs.map +0 -1
  34. package/fesm2022/ngstarter-ui-components-form-renderer-textarea-field-DxbPsw3z.mjs +0 -37
  35. package/fesm2022/ngstarter-ui-components-form-renderer-textarea-field-DxbPsw3z.mjs.map +0 -1
  36. package/fesm2022/ngstarter-ui-components-form-renderer-timezone-field-DAWrM-jS.mjs +0 -35
  37. package/fesm2022/ngstarter-ui-components-form-renderer-timezone-field-DAWrM-jS.mjs.map +0 -1
  38. package/fesm2022/ngstarter-ui-components-form-renderer-toggle-field-iyqUrWxt.mjs +0 -22
  39. package/fesm2022/ngstarter-ui-components-form-renderer-toggle-field-iyqUrWxt.mjs.map +0 -1
  40. package/fesm2022/ngstarter-ui-components-form-renderer.mjs +0 -317
  41. package/fesm2022/ngstarter-ui-components-form-renderer.mjs.map +0 -1
  42. package/types/ngstarter-ui-components-form-renderer.d.ts +0 -121
package/README.md CHANGED
@@ -102,7 +102,6 @@ The documentation site includes live demos and API examples for each component:
102
102
  - [Country Select](https://docs.ngstarter.com/forms/country)
103
103
  - [Currency Select](https://docs.ngstarter.com/forms/currency-select)
104
104
  - [Date Format Select](https://docs.ngstarter.com/forms/date-format-select)
105
- - [Form Renderer](https://docs.ngstarter.com/forms/form-renderer)
106
105
  - [Inline Text Edit](https://docs.ngstarter.com/forms/inline-text-edit)
107
106
  - [Input](https://docs.ngstarter.com/forms/input)
108
107
  - [Input Mask](https://docs.ngstarter.com/forms/input-mask)
@@ -3,7 +3,7 @@
3
3
  "generatedBy": "scripts/generate-ai-metadata.mjs",
4
4
  "package": "@ngstarter-ui/components",
5
5
  "description": "AI-readable registry for NgStarter UI Angular secondary entry points.",
6
- "componentCount": 127,
6
+ "componentCount": 126,
7
7
  "conventions": {
8
8
  "importPattern": "@ngstarter-ui/components/<component>",
9
9
  "selectorPrefix": "ngs",
@@ -2892,15 +2892,23 @@
2892
2892
  "purpose": "Let users choose a country from the built-in country list and store its ISO country code.",
2893
2893
  "useWhen": "Use in addresses, user profiles, billing, shipping, legal forms, tax forms, tenant settings, and locale settings where the form value should be a country code such as US, PL, or DE. The component shows country flags and names, supports search by name or ISO code, and integrates with FormField. Do not use for phone dialing codes; use PhoneInput. Do not use for currency selection; use CurrencySelect. Do not use for custom region, city, office, or location lists; use Select or Autocomplete.",
2894
2894
  "exampleTopics": [
2895
- "Basic country select"
2895
+ "Unselected country",
2896
+ "Basic country select",
2897
+ "Unselected country select"
2896
2898
  ],
2897
- "minimalExample": "<ngs-form-field class=\"w-1/2\">\n <ngs-label>Country</ngs-label>\n <ngs-country-select [(ngModel)]=\"country\" placeholder=\"Select a country\"/>\n</ngs-form-field>\n<p>\n Selected country: {{ country() }}\n</p>",
2899
+ "minimalExample": "<ngs-form-field class=\"w-1/2\">\n <ngs-label>Country</ngs-label>\n <ngs-country-select [(ngModel)]=\"country\" placeholder=\"Select a country\" clearable/>\n</ngs-form-field>\n<p>\n Selected country: {{ country() }}\n</p>",
2898
2900
  "exampleFiles": [
2899
2901
  {
2900
2902
  "name": "basic-country-select-example",
2901
2903
  "title": "Basic country select",
2902
2904
  "file": "projects/docs/src/app/forms/country/_examples/basic-country-select-example/basic-country-select-example.html",
2903
- "source": "<ngs-form-field class=\"w-1/2\">\n <ngs-label>Country</ngs-label>\n <ngs-country-select [(ngModel)]=\"country\" placeholder=\"Select a country\"/>\n</ngs-form-field>\n<p>\n Selected country: {{ country() }}\n</p>"
2905
+ "source": "<ngs-form-field class=\"w-1/2\">\n <ngs-label>Country</ngs-label>\n <ngs-country-select [(ngModel)]=\"country\" placeholder=\"Select a country\" clearable/>\n</ngs-form-field>\n<p>\n Selected country: {{ country() }}\n</p>"
2906
+ },
2907
+ {
2908
+ "name": "unselected-country-select-example",
2909
+ "title": "Unselected country select",
2910
+ "file": "projects/docs/src/app/forms/country/_examples/unselected-country-select-example/unselected-country-select-example.html",
2911
+ "source": "<ngs-form-field class=\"w-1/2\">\n <ngs-label>Country</ngs-label>\n <ngs-country-select [(ngModel)]=\"country\" placeholder=\"Select a country\" clearable/>\n</ngs-form-field>\n<p>\n Selected country: {{ country() || 'No country selected' }}\n</p>"
2904
2912
  }
2905
2913
  ],
2906
2914
  "previewAsset": "projects/components/country-select/preview.svg",
@@ -2910,18 +2918,28 @@
2910
2918
  "exportedSymbols": [
2911
2919
  "countries",
2912
2920
  "Country",
2913
- "CountrySelect"
2921
+ "CountrySelect",
2922
+ "CountrySelectValue"
2914
2923
  ],
2915
2924
  "inputs": [
2925
+ "aria-describedby",
2926
+ "aria-label",
2927
+ "clearable",
2916
2928
  "disabled",
2929
+ "hideCheckIcon",
2930
+ "id",
2931
+ "multiple",
2917
2932
  "placeholder",
2918
2933
  "required",
2919
2934
  "searchTerm",
2920
- "showCountryCode"
2935
+ "showCountryCode",
2936
+ "tabIndex",
2937
+ "value"
2921
2938
  ],
2922
2939
  "outputs": [
2923
2940
  "closed",
2924
- "opened"
2941
+ "opened",
2942
+ "selectionChange"
2925
2943
  ],
2926
2944
  "cssTokens": [
2927
2945
  "--ngs-color-neutral-500",
@@ -4346,6 +4364,18 @@
4346
4364
  "docsOverviewSource": "projects/docs/src/app/libraries/form-builder/overview/overview.html",
4347
4365
  "purpose": "The NgStarter Angular Form Builder library provides a drag-and-drop form designer, schema model, runtime renderer, and provider registration API for custom field types and field-specific settings.",
4348
4366
  "useWhen": "Choose Form Builder when the workflow matches examples such as Basic form builder, Custom field registration, Form builder renderer.",
4367
+ "schemaFormat": [
4368
+ "FormBuilderSchema shape: { title?: string; fields?: FormBuilderField[]; layout?: FormBuilderLayoutItem[]; sections: FormBuilderSection[] }.",
4369
+ "Root-level fields live in schema.fields and can be placed outside sections. Sections live in schema.sections and contain section.fields. The optional schema.layout array controls top-level canvas order with items like { kind: \"field\" | \"section\", id }.",
4370
+ "FormBuilderSection shape: { id: string; title: string; description?: string; collapsed?: boolean; fields: FormBuilderField[] }.",
4371
+ "FormBuilderField shape: { id: string; name: string; type: string; kind?: \"field\" | \"layout\" | \"static\"; label: string; placeholder?: string; hint?: string; defaultValue?: any; width?: 1..12; multiple?: boolean; clearable?: boolean; required?: boolean; disabled?: boolean; readonly?: boolean; options?: FormBuilderOption[]; validation?: FormBuilderValidationRule[]; visibility?: { form?: boolean; email?: boolean; pdf?: boolean; condition?: string }; settings?: Record<string, any>; children?: FormBuilderField[] }.",
4372
+ "Layout blocks are represented as fields with kind: \"layout\". The built-in group block uses type: \"group\", width: 12, and children: FormBuilderField[]. Section is a top-level FormBuilderSection, not a child field, and should not be nested inside another section/group.",
4373
+ "Static blocks are represented as fields with kind: \"static\". The built-in spacer block uses type: \"spacer\", width: 12, and settings.height with one of 8, 16, 24, 32, 48, or 64.",
4374
+ "Use width as a 12-column span. Default fields are 12 columns unless a narrower width is set. Nested children in group are sorted and rendered inside that group container.",
4375
+ "Option fields use options entries { label: string; value: any; selected?: boolean }. For select, multiple controls array values; clearable enables the clear action. For radio, settings.orientation is \"vertical\" by default and can be \"horizontal\". Date and time fields include type: \"date\" for a single date, type: \"time\" for HH:mm time values, and type: \"date-range\" for a DateRange-like value with start/end dates. Upload fields use type: \"upload\", multiple for File[] values, and settings.accept for comma-separated MIME types such as \"*/*\" or \"image/*,application/pdf\". Upload handling can be provided with [uploadCallback] on ngs-form-builder/ngs-form-renderer or globally through provideFormBuilder({ uploadCallback }). The callback receives { field, control, event, files, fileList, multiple } and may return sync/async value stored in the form control.",
4376
+ "Use <ngs-form-renderer> from @ngstarter-ui/components/form-builder to render a saved FormBuilderSchema as a runtime form. The Angular class export is FormBuilderRenderer.",
4377
+ "Register custom placeholders through provideFormBuilder({ fields/items/settings }) using formBuilderField(), formBuilderItem(), and formBuilderSettings(). Custom field definitions can provide defaults, renderer, settings, validators, acceptsChildren, group, icon, and kind."
4378
+ ],
4349
4379
  "exampleTopics": [
4350
4380
  "Basic form builder",
4351
4381
  "Custom field registration",
@@ -4369,7 +4399,7 @@
4369
4399
  "name": "form-builder-renderer-example",
4370
4400
  "title": "Form builder renderer",
4371
4401
  "file": "projects/docs/src/app/libraries/form-builder/_examples/form-builder-renderer-example/form-builder-renderer-example.html",
4372
- "source": "<div class=\"grid gap-6 lg:grid-cols-[minmax(0,1fr)_320px]\">\n <ngs-form-builder-renderer\n [schema]=\"schema\"\n [(value)]=\"value\"\n submitLabel=\"Save profile\"\n (formSubmit)=\"submittedValue.set($event)\"/>\n <ngs-card>\n <ngs-card-header>\n <ngs-card-title>Submitted value</ngs-card-title>\n </ngs-card-header>\n <ngs-card-content>\n <pre>{{ submittedValue() || value() | json }}</pre>\n </ngs-card-content>\n </ngs-card>\n</div>"
4402
+ "source": "<div class=\"grid gap-6 lg:grid-cols-[minmax(0,1fr)_320px]\">\n <ngs-form-renderer\n [schema]=\"schema\"\n [(value)]=\"value\"\n submitLabel=\"Save profile\"\n (formSubmit)=\"submittedValue.set($event)\"/>\n <ngs-card>\n <ngs-card-header>\n <ngs-card-title>Submitted value</ngs-card-title>\n </ngs-card-header>\n <ngs-card-content>\n <pre>{{ submittedValue() || value() | json }}</pre>\n </ngs-card-content>\n </ngs-card>\n</div>"
4373
4403
  }
4374
4404
  ],
4375
4405
  "previewAsset": null,
@@ -4379,8 +4409,8 @@
4379
4409
  "ngs-basic-form-builder-section-settings",
4380
4410
  "ngs-form-builder",
4381
4411
  "ngs-form-builder-field-host",
4382
- "ngs-form-builder-renderer",
4383
- "ngs-form-builder-settings-host"
4412
+ "ngs-form-builder-settings-host",
4413
+ "ngs-form-renderer"
4384
4414
  ],
4385
4415
  "exportedSymbols": [
4386
4416
  "BasicFormBuilderFieldSettings",
@@ -4392,6 +4422,7 @@
4392
4422
  "FORM_BUILDER_FIELDS",
4393
4423
  "FORM_BUILDER_ITEMS",
4394
4424
  "FORM_BUILDER_SETTINGS",
4425
+ "FORM_BUILDER_UPLOAD_CALLBACK",
4395
4426
  "FormBuilder",
4396
4427
  "FormBuilderComponentImporter",
4397
4428
  "formBuilderField",
@@ -4415,6 +4446,8 @@
4415
4446
  "FormBuilderSettingsContext",
4416
4447
  "FormBuilderSettingsDefinition",
4417
4448
  "FormBuilderSettingsHost",
4449
+ "FormBuilderUploadCallback",
4450
+ "FormBuilderUploadContext",
4418
4451
  "FormBuilderValidationRule",
4419
4452
  "FormBuilderVisibility",
4420
4453
  "provideFormBuilder",
@@ -4432,7 +4465,8 @@
4432
4465
  "section",
4433
4466
  "settingsDefinitions",
4434
4467
  "showSubmit",
4435
- "submitLabel"
4468
+ "submitLabel",
4469
+ "uploadCallback"
4436
4470
  ],
4437
4471
  "outputs": [
4438
4472
  "fieldAdded",
@@ -4452,6 +4486,7 @@
4452
4486
  "--ngs-color-surface",
4453
4487
  "--ngs-color-surface-container-low",
4454
4488
  "--ngs-color-surface-container-lowest",
4489
+ "--ngs-field-radius",
4455
4490
  "--ngs-font-size-base",
4456
4491
  "--ngs-font-size-lg",
4457
4492
  "--ngs-font-size-sm",
@@ -4465,7 +4500,9 @@
4465
4500
  "--ngs-tree-node-drop-line-color",
4466
4501
  "--ngs-tree-node-drop-target-bg",
4467
4502
  "--ngs-tree-node-drop-target-outline",
4468
- "--ngs-tree-node-gap"
4503
+ "--ngs-tree-node-gap",
4504
+ "--ngs-upload-area-border-radius",
4505
+ "--ngs-upload-area-padding"
4469
4506
  ],
4470
4507
  "example": null
4471
4508
  },
@@ -4570,100 +4607,6 @@
4570
4607
  ],
4571
4608
  "example": null
4572
4609
  },
4573
- {
4574
- "name": "form-renderer",
4575
- "title": "Form Renderer",
4576
- "overviewName": "Form Renderer",
4577
- "category": "libraries",
4578
- "package": "@ngstarter-ui/components",
4579
- "importPath": "@ngstarter-ui/components/form-renderer",
4580
- "publicApi": "projects/components/form-renderer/public-api.ts",
4581
- "sourceRoot": "projects/components/form-renderer/src",
4582
- "docsPath": "/libraries/form-renderer",
4583
- "docsOverviewSource": "projects/docs/src/app/libraries/form-renderer/overview/overview.html",
4584
- "purpose": "Render backend-driven or config-driven forms from a FormConfig.",
4585
- "useWhen": "Use when the backend or an admin configuration defines the form structure: dynamic settings, surveys, onboarding schemas, profile or config forms, CMS forms, tenant-specific forms, and feature-specific forms where fields can change without editing the Angular template. FormRenderer creates a FormGroup, renders fields from elements, places them through layout, applies validators and crossValidators, handles initialValue, visibleWhen, disabled state, content blocks, and emits valueChanges, formSubmit, and initialized. Do not use for ordinary static forms where fields are known in Angular code; build those manually with TailwindCSS grid/flex layout and one ngs-form-field per control. Do not use as a replacement for FormField, a wizard/stepper workflow, or an arbitrary page builder.",
4586
- "exampleTopics": [
4587
- "Basic form renderer"
4588
- ],
4589
- "minimalExample": "<ngs-form-renderer #formRenderer\n [config]=\"formConfig()\"\n [initialValue]=\"initialValue()\"\n (valueChanges)=\"onValueChanges($event)\"\n (formSubmit)=\"onFormSubmit($event)\" />\n<div class=\"mt-3\">\n <button ngsButton=\"filled\" (click)=\"formRenderer.submit()\">Submit</button>\n</div>\n@if(submittedData()) {\n <div class=\"font-bold mb-3 mt-6\">Submitted data:</div>\n <pre>{{ submittedData() | json }}</pre>\n}",
4590
- "exampleFiles": [
4591
- {
4592
- "name": "basic-form-renderer-example",
4593
- "title": "Basic form renderer",
4594
- "file": "projects/docs/src/app/libraries/form-renderer/_examples/basic-form-renderer-example/basic-form-renderer-example.html",
4595
- "source": "<ngs-form-renderer #formRenderer\n [config]=\"formConfig()\"\n [initialValue]=\"initialValue()\"\n (valueChanges)=\"onValueChanges($event)\"\n (formSubmit)=\"onFormSubmit($event)\" />\n<div class=\"mt-3\">\n <button ngsButton=\"filled\" (click)=\"formRenderer.submit()\">Submit</button>\n</div>\n@if(submittedData()) {\n <div class=\"font-bold mb-3 mt-6\">Submitted data:</div>\n <pre>{{ submittedData() | json }}</pre>\n}"
4596
- }
4597
- ],
4598
- "previewAsset": "projects/components/form-renderer/preview.svg",
4599
- "selectors": [
4600
- "ngs-autocomplete-many-field",
4601
- "ngs-checkbox-field",
4602
- "ngs-component-loader",
4603
- "ngs-datepicker-field",
4604
- "ngs-divider-content",
4605
- "ngs-form-renderer",
4606
- "ngs-image-content",
4607
- "ngs-input-field",
4608
- "ngs-radio-group-field",
4609
- "ngs-select-field",
4610
- "ngs-text-content",
4611
- "ngs-textarea-field",
4612
- "ngs-timezone-field",
4613
- "ngs-toggle-field"
4614
- ],
4615
- "exportedSymbols": [
4616
- "AutocompleteManyField",
4617
- "CheckboxField",
4618
- "ComponentConfig",
4619
- "ComponentImporter",
4620
- "ComponentLoader",
4621
- "ComponentNode",
4622
- "ComponentRegistryService",
4623
- "ContentConfig",
4624
- "CrossValidatorFn",
4625
- "DatepickerField",
4626
- "DividerContent",
4627
- "FORM_RENDERER_FIELD_REGISTRY",
4628
- "FormConfig",
4629
- "FormFieldConfig",
4630
- "FormGeneratorService",
4631
- "FormRenderer",
4632
- "GridNode",
4633
- "ImageContent",
4634
- "InputField",
4635
- "LayoutNode",
4636
- "RadioGroupField",
4637
- "SelectField",
4638
- "TextareaField",
4639
- "TextContent",
4640
- "TimezoneField",
4641
- "ToggleField",
4642
- "ValidatorConfig",
4643
- "ValidatorRegistryService"
4644
- ],
4645
- "inputs": [
4646
- "colspan",
4647
- "componentConfig",
4648
- "config",
4649
- "control"
4650
- ],
4651
- "outputs": [
4652
- "formSubmit",
4653
- "initialized",
4654
- "valueChanges"
4655
- ],
4656
- "cssTokens": [
4657
- "--ngs-color-outline-variant",
4658
- "--ngs-font-size-xs",
4659
- "--ngs-form-field-outlined-label-text-color",
4660
- "--ngs-form-field-outlined-label-text-size",
4661
- "--ngs-form-field-subscript-text-size",
4662
- "--ngs-form-field-subscript-text-tracking",
4663
- "--ngs-form-field-subscript-text-weight"
4664
- ],
4665
- "example": null
4666
- },
4667
4610
  {
4668
4611
  "name": "gauge",
4669
4612
  "title": "Gauge",
@@ -7533,6 +7476,7 @@
7533
7476
  "useWhen": "Use ngs-radio-group with ngs-radio-button for simple text options in forms, settings, filters, and preference screens where all options should be visible and the user can choose only one. Put value on each radio button and bind the group value with value, ngModel, or formControlName. Use name when native form grouping matters, disabled on the group or individual radio button when needed, and change when the screen must react to selection changes. Do not use Radio for multiple selection; use Checkbox. Do not use for long option lists; use Select or Autocomplete. Do not use for rich options with descriptions, icons, or large clickable blocks; use RadioCard. Do not use as a segmented view switcher; use Segmented or ButtonToggle. Do not use for yes/no boolean settings when SlideToggle or Checkbox better matches the meaning.",
7534
7477
  "exampleTopics": [
7535
7478
  "Basic Radios",
7479
+ "Radio Orientation",
7536
7480
  "Radio Card",
7537
7481
  "Basic radio"
7538
7482
  ],
@@ -7549,6 +7493,12 @@
7549
7493
  "title": "Radio card",
7550
7494
  "file": "projects/docs/src/app/forms/radio/_examples/radio-card-example/radio-card-example.html",
7551
7495
  "source": "<form [formGroup]=\"form\">\n <ngs-radio-card-group formControlName=\"privacy\">\n <ngs-radio-card value=\"open\">\n <ngs-radio-card-title>\n <ngs-icon name=\"fluent:globe-24-regular\"/>\n Open\n </ngs-radio-card-title>\n <ngs-radio-card-content>\n Posts and community members are public and visible to search engines.\n </ngs-radio-card-content>\n </ngs-radio-card>\n <ngs-radio-card value=\"closed\">\n <ngs-radio-card-title>\n <ngs-icon name=\"fluent:lock-closed-24-regular\"/>\n Closed\n </ngs-radio-card-title>\n <ngs-radio-card-content>\n Only members can view community members and posts. Content is not visible to search engines.\n </ngs-radio-card-content>\n </ngs-radio-card>\n </ngs-radio-card-group>\n</form>\n<div class=\"mt-10\">\n <p>\n <button ngsButton=\"filled\" (click)=\"toggleDisabled()\">\n {{ form.get('privacy')?.disabled ? 'Enable' : 'Disable' }} Control\n </button>\n </p>\n <p>Control value: {{ form.get('privacy')?.value }}</p>\n</div>"
7496
+ },
7497
+ {
7498
+ "name": "radio-orientation-example",
7499
+ "title": "Radio orientation",
7500
+ "file": "projects/docs/src/app/forms/radio/_examples/radio-orientation-example/radio-orientation-example.html",
7501
+ "source": "<div class=\"grid gap-8 md:grid-cols-2\">\n <div class=\"flex flex-col gap-4\">\n <h4 class=\"text-sm font-medium\">Vertical</h4>\n <ngs-radio-group orientation=\"vertical\" aria-label=\"Notification frequency\">\n <ngs-radio-button value=\"instant\">Instant</ngs-radio-button>\n <ngs-radio-button value=\"daily\">Daily summary</ngs-radio-button>\n <ngs-radio-button value=\"weekly\">Weekly digest</ngs-radio-button>\n </ngs-radio-group>\n </div>\n <div class=\"flex flex-col gap-4\">\n <h4 class=\"text-sm font-medium\">Horizontal</h4>\n <ngs-radio-group orientation=\"horizontal\" aria-label=\"Billing cycle\">\n <ngs-radio-button value=\"monthly\">Monthly</ngs-radio-button>\n <ngs-radio-button value=\"annual\">Annual</ngs-radio-button>\n </ngs-radio-group>\n </div>\n</div>"
7552
7502
  }
7553
7503
  ],
7554
7504
  "previewAsset": "projects/components/radio/preview.svg",
@@ -7558,13 +7508,15 @@
7558
7508
  ],
7559
7509
  "exportedSymbols": [
7560
7510
  "RadioButton",
7561
- "RadioGroup"
7511
+ "RadioGroup",
7512
+ "RadioGroupOrientation"
7562
7513
  ],
7563
7514
  "inputs": [
7564
7515
  "checked",
7565
7516
  "disabled",
7566
7517
  "id",
7567
7518
  "name",
7519
+ "orientation",
7568
7520
  "value"
7569
7521
  ],
7570
7522
  "outputs": [
@@ -7583,7 +7535,8 @@
7583
7535
  "--ngs-radio-button-inner-circle-size",
7584
7536
  "--ngs-radio-button-label-color",
7585
7537
  "--ngs-radio-button-size",
7586
- "--ngs-radio-button-unchecked-color"
7538
+ "--ngs-radio-button-unchecked-color",
7539
+ "--ngs-radio-group-gap"
7587
7540
  ],
7588
7541
  "example": null
7589
7542
  },
@@ -8676,11 +8629,13 @@
8676
8629
  ],
8677
8630
  "previewAsset": "projects/components/slide-toggle/preview.svg",
8678
8631
  "selectors": [
8679
- "ngs-slide-toggle"
8632
+ "ngs-slide-toggle",
8633
+ "ngs-slide-toggle-group"
8680
8634
  ],
8681
8635
  "exportedSymbols": [
8682
8636
  "SlideToggle",
8683
- "SlideToggleChange"
8637
+ "SlideToggleChange",
8638
+ "SlideToggleGroup"
8684
8639
  ],
8685
8640
  "inputs": [
8686
8641
  "aria-describedby",
@@ -8708,6 +8663,7 @@
8708
8663
  "--ngs-color-surface-container-highest",
8709
8664
  "--ngs-slide-toggle-bar-checked-color",
8710
8665
  "--ngs-slide-toggle-bar-color",
8666
+ "--ngs-slide-toggle-group-gap",
8711
8667
  "--ngs-slide-toggle-height",
8712
8668
  "--ngs-slide-toggle-thumb-checked-color",
8713
8669
  "--ngs-slide-toggle-thumb-color",
@@ -10923,7 +10879,7 @@
10923
10879
  "docsPath": "/libraries/visual-builder",
10924
10880
  "docsOverviewSource": "projects/docs/src/app/libraries/visual-builder/overview/overview.html",
10925
10881
  "purpose": "Experimental scaffold for a future no-code or low-code visual workspace.",
10926
- "useWhen": "Do not use ngs-visual-builder for production admin screens, dashboards, visual editors, page builders, or real no-code/low-code workflows yet. The current component is only a placeholder: it has no inputs, outputs, layout regions, canvas, drag and drop, inspector, persistence, or editable block model. Mention it only as an experimental scaffold for future visual builder work. Use ContentEditor for block-based CMS/page content, ImageDesigner for creative image composition, Tiles for editable dashboard widgets or config-driven dashboard renderers, Grid for static dashboard layouts, FormRenderer for backend-driven forms, KanbanBoard for workflow columns, and normal NgStarter layout/components for admin screens.",
10882
+ "useWhen": "Do not use ngs-visual-builder for production admin screens, dashboards, visual editors, page builders, or real no-code/low-code workflows yet. The current component is only a placeholder: it has no inputs, outputs, layout regions, canvas, drag and drop, inspector, persistence, or editable block model. Mention it only as an experimental scaffold for future visual builder work. Use ContentEditor for block-based CMS/page content, ImageDesigner for creative image composition, Tiles for editable dashboard widgets or config-driven dashboard renderers, Grid for static dashboard layouts, Form Builder for schema-driven forms, KanbanBoard for workflow columns, and normal NgStarter layout/components for admin screens.",
10927
10883
  "exampleTopics": [
10928
10884
  "Visual Builder"
10929
10885
  ],
@@ -269,16 +269,22 @@ class CountrySelect {
269
269
  _renderer = inject(Renderer2);
270
270
  _formField = inject(FORM_FIELD, { optional: true });
271
271
  static nextId = 0;
272
- id = `ngs-country-select-${CountrySelect.nextId++}`;
273
272
  stateChanges = new Subject();
274
273
  searchTerm = model('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : /* istanbul ignore next */ []));
275
274
  visibleCountryCount = signal(CountrySelect.COUNTRY_RENDER_CHUNK_SIZE, ...(ngDevMode ? [{ debugName: "visibleCountryCount" }] : /* istanbul ignore next */ []));
276
- _valueSignal = signal(null, ...(ngDevMode ? [{ debugName: "_valueSignal" }] : /* istanbul ignore next */ []));
275
+ valueSignal = model(null, { ...(ngDevMode ? { debugName: "valueSignal" } : /* istanbul ignore next */ {}), alias: 'value' });
277
276
  _focusedSignal = signal(false, ...(ngDevMode ? [{ debugName: "_focusedSignal" }] : /* istanbul ignore next */ []));
278
277
  _touched = false;
278
+ idSignal = input(`ngs-country-select-${CountrySelect.nextId++}`, { ...(ngDevMode ? { debugName: "idSignal" } : /* istanbul ignore next */ {}), alias: 'id' });
279
279
  placeholderInputSignal = input('', { ...(ngDevMode ? { debugName: "placeholderInputSignal" } : /* istanbul ignore next */ {}), alias: 'placeholder' });
280
280
  isRequiredSignal = model(false, { ...(ngDevMode ? { debugName: "isRequiredSignal" } : /* istanbul ignore next */ {}), alias: 'required' });
281
281
  isDisabledSignal = model(false, { ...(ngDevMode ? { debugName: "isDisabledSignal" } : /* istanbul ignore next */ {}), alias: 'disabled' });
282
+ multiple = input(false, { ...(ngDevMode ? { debugName: "multiple" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
283
+ hideCheckIcon = input(false, { ...(ngDevMode ? { debugName: "hideCheckIcon" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
284
+ clearable = input(false, { ...(ngDevMode ? { debugName: "clearable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
285
+ ariaLabel = input(null, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
286
+ tabIndex = input(0, { ...(ngDevMode ? { debugName: "tabIndex" } : /* istanbul ignore next */ {}), transform: (value) => value == null ? 0 : parseInt(value + '', 10) });
287
+ ariaDescribedby = input(null, { ...(ngDevMode ? { debugName: "ariaDescribedby" } : /* istanbul ignore next */ {}), alias: 'aria-describedby' });
282
288
  showCountryCode = input(false, { ...(ngDevMode ? { debugName: "showCountryCode" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
283
289
  internalCountries = countries;
284
290
  filteredCountries = computed(() => {
@@ -294,9 +300,30 @@ class CountrySelect {
294
300
  visibleCountries = computed(() => {
295
301
  return this.filteredCountries().slice(0, this.visibleCountryCount());
296
302
  }, ...(ngDevMode ? [{ debugName: "visibleCountries" }] : /* istanbul ignore next */ []));
303
+ selectedCountryDisplays = computed(() => {
304
+ const value = this.valueSignal();
305
+ const selectedCodes = Array.isArray(value)
306
+ ? value
307
+ : value
308
+ ? [value]
309
+ : [];
310
+ return selectedCodes
311
+ .map((code) => this.internalCountries.find((country) => country.code === code))
312
+ .filter((country) => !!country);
313
+ }, ...(ngDevMode ? [{ debugName: "selectedCountryDisplays" }] : /* istanbul ignore next */ []));
297
314
  selectedCountryDisplay = computed(() => {
298
- return this.internalCountries.find(c => c.code === this._valueSignal());
315
+ return this.selectedCountryDisplays()[0] ?? null;
299
316
  }, ...(ngDevMode ? [{ debugName: "selectedCountryDisplay" }] : /* istanbul ignore next */ []));
317
+ selectedCountriesText = computed(() => {
318
+ return this.selectedCountryDisplays()
319
+ .map((country) => {
320
+ if (this.showCountryCode()) {
321
+ return `${country.name} (${country.code})`;
322
+ }
323
+ return country.name;
324
+ })
325
+ .join(', ');
326
+ }, ...(ngDevMode ? [{ debugName: "selectedCountriesText" }] : /* istanbul ignore next */ []));
300
327
  ngsSelect = viewChild.required('ngsSelect');
301
328
  searchInput = viewChild.required('searchInput');
302
329
  fm = inject(FocusMonitor);
@@ -305,6 +332,7 @@ class CountrySelect {
305
332
  destroyRef = inject(DestroyRef);
306
333
  opened = output();
307
334
  closed = output();
335
+ selectionChange = output();
308
336
  onChangeFn = () => { };
309
337
  onTouchedFn = () => { };
310
338
  _countryRenderTimeout;
@@ -319,14 +347,20 @@ class CountrySelect {
319
347
  this.stateChanges.complete();
320
348
  });
321
349
  effect(() => {
322
- this.onChangeFn(this._valueSignal());
350
+ this.onChangeFn(this.valueSignal());
323
351
  });
324
352
  effect(() => {
325
- this._valueSignal();
353
+ this.valueSignal();
326
354
  this._focusedSignal();
327
355
  this.isRequiredSignal();
328
356
  this.isDisabledSignal();
329
357
  this.placeholderInputSignal();
358
+ this.multiple();
359
+ this.hideCheckIcon();
360
+ this.clearable();
361
+ this.ariaLabel();
362
+ this.tabIndex();
363
+ this.ariaDescribedby();
330
364
  this.ngControl?.control?.status;
331
365
  this.stateChanges.next();
332
366
  });
@@ -349,11 +383,14 @@ class CountrySelect {
349
383
  ngOnDestroy() {
350
384
  this.clearCountrySelectTimeouts();
351
385
  }
386
+ get id() {
387
+ return this.idSignal();
388
+ }
352
389
  get value() {
353
- return this._valueSignal();
390
+ return this.valueSignal();
354
391
  }
355
392
  set value(val) {
356
- this._valueSignal.set(val);
393
+ this.valueSignal.set(val);
357
394
  }
358
395
  get focused() {
359
396
  return this._focusedSignal() || (this.ngsSelect?.()?.panelOpen() ?? false);
@@ -391,10 +428,14 @@ class CountrySelect {
391
428
  this.isDisabledSignal.set(coerceBooleanProperty(dis));
392
429
  }
393
430
  get empty() {
394
- return !this._valueSignal();
431
+ const value = this.valueSignal();
432
+ if (Array.isArray(value)) {
433
+ return value.length === 0;
434
+ }
435
+ return !value;
395
436
  }
396
437
  get shouldLabelFloat() {
397
- return this._focusedSignal() || !this.empty;
438
+ return this.focused || !this.empty;
398
439
  }
399
440
  get errorState() {
400
441
  return !!(this.ngControl?.invalid && (this.ngControl?.touched || this._touched));
@@ -416,7 +457,7 @@ class CountrySelect {
416
457
  this.ngsSelect().open();
417
458
  }
418
459
  writeValue(value) {
419
- this._valueSignal.set(value);
460
+ this.valueSignal.set(value);
420
461
  }
421
462
  registerOnChange(fn) {
422
463
  this.onChangeFn = fn;
@@ -434,6 +475,7 @@ class CountrySelect {
434
475
  onSelectionChange(event) {
435
476
  this.value = event.value;
436
477
  this.onTouchedFn();
478
+ this.selectionChange.emit(event);
437
479
  }
438
480
  clearSearch(event) {
439
481
  event.stopPropagation();
@@ -468,7 +510,8 @@ class CountrySelect {
468
510
  }
469
511
  getInitialVisibleCountryCount() {
470
512
  const countries = this.filteredCountries();
471
- const selectedCountryCode = this._valueSignal();
513
+ const value = this.valueSignal();
514
+ const selectedCountryCode = Array.isArray(value) ? value[0] : value;
472
515
  if (!selectedCountryCode) {
473
516
  return CountrySelect.COUNTRY_RENDER_CHUNK_SIZE;
474
517
  }
@@ -509,12 +552,12 @@ class CountrySelect {
509
552
  this._countrySearchFocusTimeout = undefined;
510
553
  }
511
554
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: CountrySelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
512
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: CountrySelect, isStandalone: true, selector: "ngs-country-select", inputs: { searchTerm: { classPropertyName: "searchTerm", publicName: "searchTerm", isSignal: true, isRequired: false, transformFunction: null }, placeholderInputSignal: { classPropertyName: "placeholderInputSignal", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, isRequiredSignal: { classPropertyName: "isRequiredSignal", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, isDisabledSignal: { classPropertyName: "isDisabledSignal", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showCountryCode: { classPropertyName: "showCountryCode", publicName: "showCountryCode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { searchTerm: "searchTermChange", isRequiredSignal: "requiredChange", isDisabledSignal: "disabledChange", opened: "opened", closed: "closed" }, host: { listeners: { "focus": "onFocusIn()", "blur": "onFocusOut($event)" }, properties: { "class.floating": "shouldLabelFloat", "id": "id", "attr.tabindex": "disabled ? -1 : 0" }, classAttribute: "ngs-country-select" }, providers: [
555
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: CountrySelect, isStandalone: true, selector: "ngs-country-select", inputs: { searchTerm: { classPropertyName: "searchTerm", publicName: "searchTerm", isSignal: true, isRequired: false, transformFunction: null }, valueSignal: { classPropertyName: "valueSignal", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, idSignal: { classPropertyName: "idSignal", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, placeholderInputSignal: { classPropertyName: "placeholderInputSignal", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, isRequiredSignal: { classPropertyName: "isRequiredSignal", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, isDisabledSignal: { classPropertyName: "isDisabledSignal", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, hideCheckIcon: { classPropertyName: "hideCheckIcon", publicName: "hideCheckIcon", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedby: { classPropertyName: "ariaDescribedby", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, showCountryCode: { classPropertyName: "showCountryCode", publicName: "showCountryCode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { searchTerm: "searchTermChange", valueSignal: "valueChange", isRequiredSignal: "requiredChange", isDisabledSignal: "disabledChange", opened: "opened", closed: "closed", selectionChange: "selectionChange" }, host: { listeners: { "focus": "onFocusIn()", "blur": "onFocusOut($event)" }, properties: { "class.floating": "shouldLabelFloat", "id": "id", "attr.tabindex": "disabled ? -1 : tabIndex()" }, classAttribute: "ngs-country-select" }, providers: [
513
556
  {
514
557
  provide: FormFieldControl,
515
558
  useExisting: forwardRef(() => CountrySelect),
516
559
  },
517
- ], viewQueries: [{ propertyName: "ngsSelect", first: true, predicate: ["ngsSelect"], descendants: true, isSignal: true }, { propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], exportAs: ["ngsCountrySelect"], ngImport: i0, template: "<ngs-select\n #ngsSelect\n [value]=\"value\"\n (selectionChange)=\"onSelectionChange($event)\"\n (opened)=\"onSelectOpened()\"\n (closed)=\"onSelectClosed()\"\n [placeholder]=\"placeholder\"\n [required]=\"isRequiredSignal()\"\n [disabled]=\"isDisabledSignal()\">\n <ngs-select-trigger class=\"select-trigger\">\n @let selectedCountry = selectedCountryDisplay();\n @if (selectedCountry) {\n <span class=\"ngs-select-trigger-content\">\n <span class=\"flag-icon\" aria-hidden=\"true\">{{ selectedCountry.flag }}</span>\n <span class=\"ngs-select-trigger-text\">\n {{ selectedCountry.name }}\n @if (showCountryCode()) {\n <span class=\"code\">({{ selectedCountry.code }})</span>\n }\n </span>\n </span>\n }\n\n @if (!selectedCountryDisplay() && placeholder) {\n <span class=\"ngs-select-trigger-text placeholder-text\">\n {{ placeholder }}\n </span>\n }\n </ngs-select-trigger>\n\n <ngs-select-header>\n <div class=\"sticky top-0 z-1 bg-surface-container-lowest\">\n <input #searchInput\n type=\"text\"\n placeholder=\"Search...\"\n autocomplete=\"off\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"onCountrySearch($event)\"\n class=\"w-full text-sm focus:outline-none border-b border-surface-container-high focus:border-b-primary h-14 px-3\">\n @if (searchTerm().trim()) {\n <div class=\"absolute end-1 top-1/2 -translate-y-1/2\">\n <button\n ngsIconButton\n (click)=\"clearSearch($event)\"\n class=\"clear-button\"\n type=\"button\"\n aria-label=\"Clear search\">\n <ngs-icon name=\"fluent:dismiss-24-regular\"/>\n </button>\n </div>\n }\n </div>\n </ngs-select-header>\n\n @for (country of visibleCountries(); track country.code) {\n <ngs-option [value]=\"country.code\">\n <div class=\"flex items-center gap-2\">\n <span class=\"select-option-icon text-2xl\" aria-hidden=\"true\">{{ country.flag }}</span>\n <span class=\"select-option-text\">\n {{ country.name }}\n @if (showCountryCode()) {\n <span class=\"code\">({{ country.code }})</span>\n }\n </span>\n </div>\n </ngs-option>\n } @empty {\n @if (searchTerm()) {\n <div class=\"text-sm px-4 py-3\">\n <span i18n>Country not found</span>.\n </div>\n }\n }\n\n @if (visibleCountryCount() < filteredCountries().length) {\n <div class=\"select-loading\">\n Loading more countries...\n </div>\n }\n</ngs-select>\n", styles: [":host{display:block}:host .flag-icon{display:inline-flex;align-items:center;justify-content:center;width:22px;font-size:22px;line-height:1}:host .select-option-content{display:flex;align-items:center;gap:var(--ngs-dropdown-item-gap);min-width:0;width:100%;height:100%;overflow:hidden;white-space:nowrap}:host .select-option-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .code{text-transform:uppercase;color:var(--ngs-color-neutral-500)}:host .select-loading{padding:calc(var(--spacing, .25rem) * 2) calc(var(--spacing, .25rem) * 4);color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "component", type: Option, selector: "ngs-option", inputs: ["value", "data", "disabled", "selected"], outputs: ["onSelectionChange"], exportAs: ["ngsOption"] }, { kind: "component", type: Icon, selector: "ngs-icon", inputs: ["name"], exportAs: ["ngsIcon"] }, { kind: "component", type: Select, selector: "ngs-select", inputs: ["id", "placeholder", "disabled", "required", "multiple", "hideCheckIcon", "clearable", "aria-label", "tabIndex", "aria-describedby", "value"], outputs: ["selectionChange", "opened", "closed", "valueChange"], exportAs: ["ngsSelect"] }, { kind: "directive", type: SelectTrigger, selector: "ngs-select-trigger" }, { 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: "component", type: Button, selector: " button[ngsButton], button[ngsIconButton], a[ngsButton], a[ngsIconButton] ", inputs: ["ngsButton", "ngsIconButton", "loading", "disabled", "disabledInteractive", "disableRipple", "reverse", "fullWidth", "hideTextOnMobile"], exportAs: ["ngsButton"] }, { kind: "component", type: SelectHeader, selector: "ngs-select-header" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
560
+ ], viewQueries: [{ propertyName: "ngsSelect", first: true, predicate: ["ngsSelect"], descendants: true, isSignal: true }, { propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], exportAs: ["ngsCountrySelect"], ngImport: i0, template: "<ngs-select\n #ngsSelect\n [value]=\"value\"\n (selectionChange)=\"onSelectionChange($event)\"\n (opened)=\"onSelectOpened()\"\n (closed)=\"onSelectClosed()\"\n [id]=\"id + '-select'\"\n [placeholder]=\"placeholder\"\n [required]=\"isRequiredSignal()\"\n [disabled]=\"isDisabledSignal()\"\n [multiple]=\"multiple()\"\n [hideCheckIcon]=\"hideCheckIcon()\"\n [clearable]=\"clearable()\"\n [aria-label]=\"ariaLabel()\"\n [tabIndex]=\"tabIndex()\"\n [aria-describedby]=\"ariaDescribedby()\">\n <ngs-select-trigger class=\"select-trigger\">\n @let selectedCountries = selectedCountryDisplays();\n @if (selectedCountries.length === 1) {\n @let selectedCountry = selectedCountries[0];\n <span class=\"ngs-select-trigger-content\">\n <span class=\"flag-icon\" aria-hidden=\"true\">{{ selectedCountry.flag }}</span>\n <span class=\"ngs-select-trigger-text\">\n {{ selectedCountry.name }}\n @if (showCountryCode()) {\n <span class=\"code\">({{ selectedCountry.code }})</span>\n }\n </span>\n </span>\n } @else if (selectedCountries.length > 1) {\n <span class=\"ngs-select-trigger-content\">\n <span class=\"ngs-select-trigger-text\">\n {{ selectedCountriesText() }}\n </span>\n </span>\n }\n\n </ngs-select-trigger>\n\n <ngs-select-header>\n <div class=\"sticky top-0 z-1 bg-surface-container-lowest\">\n <input #searchInput\n type=\"text\"\n placeholder=\"Search...\"\n autocomplete=\"off\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"onCountrySearch($event)\"\n class=\"w-full text-sm focus:outline-none border-b border-surface-container-high focus:border-b-primary h-14 px-3\">\n @if (searchTerm().trim()) {\n <div class=\"absolute end-1 top-1/2 -translate-y-1/2\">\n <button\n ngsIconButton\n (click)=\"clearSearch($event)\"\n class=\"clear-button\"\n type=\"button\"\n aria-label=\"Clear search\">\n <ngs-icon name=\"fluent:dismiss-24-regular\"/>\n </button>\n </div>\n }\n </div>\n </ngs-select-header>\n\n @for (country of visibleCountries(); track country.code) {\n <ngs-option [value]=\"country.code\">\n <div class=\"flex items-center gap-2\">\n <span class=\"select-option-icon text-2xl\" aria-hidden=\"true\">{{ country.flag }}</span>\n <span class=\"select-option-text\">\n {{ country.name }}\n @if (showCountryCode()) {\n <span class=\"code\">({{ country.code }})</span>\n }\n </span>\n </div>\n </ngs-option>\n } @empty {\n @if (searchTerm()) {\n <div class=\"text-sm px-4 py-3\">\n <span i18n>Country not found</span>.\n </div>\n }\n }\n\n @if (visibleCountryCount() < filteredCountries().length) {\n <div class=\"select-loading\">\n Loading more countries...\n </div>\n }\n</ngs-select>\n", styles: [":host{display:block}:host .flag-icon{display:inline-flex;align-items:center;justify-content:center;width:22px;font-size:22px;line-height:1}:host .select-option-content{display:flex;align-items:center;gap:var(--ngs-dropdown-item-gap);min-width:0;width:100%;height:100%;overflow:hidden;white-space:nowrap}:host .select-option-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .code{text-transform:uppercase;color:var(--ngs-color-neutral-500)}:host .select-loading{padding:calc(var(--spacing, .25rem) * 2) calc(var(--spacing, .25rem) * 4);color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "component", type: Option, selector: "ngs-option", inputs: ["value", "data", "disabled", "selected"], outputs: ["onSelectionChange"], exportAs: ["ngsOption"] }, { kind: "component", type: Icon, selector: "ngs-icon", inputs: ["name"], exportAs: ["ngsIcon"] }, { kind: "component", type: Select, selector: "ngs-select", inputs: ["id", "placeholder", "disabled", "required", "multiple", "hideCheckIcon", "clearable", "aria-label", "tabIndex", "aria-describedby", "value"], outputs: ["selectionChange", "opened", "closed", "valueChange"], exportAs: ["ngsSelect"] }, { kind: "directive", type: SelectTrigger, selector: "ngs-select-trigger" }, { 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: "component", type: Button, selector: " button[ngsButton], button[ngsIconButton], a[ngsButton], a[ngsIconButton] ", inputs: ["ngsButton", "ngsIconButton", "loading", "disabled", "disabledInteractive", "disableRipple", "reverse", "fullWidth", "hideTextOnMobile"], exportAs: ["ngsButton"] }, { kind: "component", type: SelectHeader, selector: "ngs-select-header" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
518
561
  }
519
562
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: CountrySelect, decorators: [{
520
563
  type: Component,
@@ -536,11 +579,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
536
579
  'class': 'ngs-country-select',
537
580
  '[class.floating]': 'shouldLabelFloat',
538
581
  '[id]': 'id',
539
- '[attr.tabindex]': 'disabled ? -1 : 0',
582
+ '[attr.tabindex]': 'disabled ? -1 : tabIndex()',
540
583
  '(focus)': 'onFocusIn()',
541
584
  '(blur)': 'onFocusOut($event)',
542
- }, template: "<ngs-select\n #ngsSelect\n [value]=\"value\"\n (selectionChange)=\"onSelectionChange($event)\"\n (opened)=\"onSelectOpened()\"\n (closed)=\"onSelectClosed()\"\n [placeholder]=\"placeholder\"\n [required]=\"isRequiredSignal()\"\n [disabled]=\"isDisabledSignal()\">\n <ngs-select-trigger class=\"select-trigger\">\n @let selectedCountry = selectedCountryDisplay();\n @if (selectedCountry) {\n <span class=\"ngs-select-trigger-content\">\n <span class=\"flag-icon\" aria-hidden=\"true\">{{ selectedCountry.flag }}</span>\n <span class=\"ngs-select-trigger-text\">\n {{ selectedCountry.name }}\n @if (showCountryCode()) {\n <span class=\"code\">({{ selectedCountry.code }})</span>\n }\n </span>\n </span>\n }\n\n @if (!selectedCountryDisplay() && placeholder) {\n <span class=\"ngs-select-trigger-text placeholder-text\">\n {{ placeholder }}\n </span>\n }\n </ngs-select-trigger>\n\n <ngs-select-header>\n <div class=\"sticky top-0 z-1 bg-surface-container-lowest\">\n <input #searchInput\n type=\"text\"\n placeholder=\"Search...\"\n autocomplete=\"off\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"onCountrySearch($event)\"\n class=\"w-full text-sm focus:outline-none border-b border-surface-container-high focus:border-b-primary h-14 px-3\">\n @if (searchTerm().trim()) {\n <div class=\"absolute end-1 top-1/2 -translate-y-1/2\">\n <button\n ngsIconButton\n (click)=\"clearSearch($event)\"\n class=\"clear-button\"\n type=\"button\"\n aria-label=\"Clear search\">\n <ngs-icon name=\"fluent:dismiss-24-regular\"/>\n </button>\n </div>\n }\n </div>\n </ngs-select-header>\n\n @for (country of visibleCountries(); track country.code) {\n <ngs-option [value]=\"country.code\">\n <div class=\"flex items-center gap-2\">\n <span class=\"select-option-icon text-2xl\" aria-hidden=\"true\">{{ country.flag }}</span>\n <span class=\"select-option-text\">\n {{ country.name }}\n @if (showCountryCode()) {\n <span class=\"code\">({{ country.code }})</span>\n }\n </span>\n </div>\n </ngs-option>\n } @empty {\n @if (searchTerm()) {\n <div class=\"text-sm px-4 py-3\">\n <span i18n>Country not found</span>.\n </div>\n }\n }\n\n @if (visibleCountryCount() < filteredCountries().length) {\n <div class=\"select-loading\">\n Loading more countries...\n </div>\n }\n</ngs-select>\n", styles: [":host{display:block}:host .flag-icon{display:inline-flex;align-items:center;justify-content:center;width:22px;font-size:22px;line-height:1}:host .select-option-content{display:flex;align-items:center;gap:var(--ngs-dropdown-item-gap);min-width:0;width:100%;height:100%;overflow:hidden;white-space:nowrap}:host .select-option-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .code{text-transform:uppercase;color:var(--ngs-color-neutral-500)}:host .select-loading{padding:calc(var(--spacing, .25rem) * 2) calc(var(--spacing, .25rem) * 4);color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
543
- }], ctorParameters: () => [], propDecorators: { searchTerm: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchTerm", required: false }] }, { type: i0.Output, args: ["searchTermChange"] }], placeholderInputSignal: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], isRequiredSignal: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }, { type: i0.Output, args: ["requiredChange"] }], isDisabledSignal: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], showCountryCode: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCountryCode", required: false }] }], ngsSelect: [{ type: i0.ViewChild, args: ['ngsSelect', { isSignal: true }] }], searchInput: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
585
+ }, template: "<ngs-select\n #ngsSelect\n [value]=\"value\"\n (selectionChange)=\"onSelectionChange($event)\"\n (opened)=\"onSelectOpened()\"\n (closed)=\"onSelectClosed()\"\n [id]=\"id + '-select'\"\n [placeholder]=\"placeholder\"\n [required]=\"isRequiredSignal()\"\n [disabled]=\"isDisabledSignal()\"\n [multiple]=\"multiple()\"\n [hideCheckIcon]=\"hideCheckIcon()\"\n [clearable]=\"clearable()\"\n [aria-label]=\"ariaLabel()\"\n [tabIndex]=\"tabIndex()\"\n [aria-describedby]=\"ariaDescribedby()\">\n <ngs-select-trigger class=\"select-trigger\">\n @let selectedCountries = selectedCountryDisplays();\n @if (selectedCountries.length === 1) {\n @let selectedCountry = selectedCountries[0];\n <span class=\"ngs-select-trigger-content\">\n <span class=\"flag-icon\" aria-hidden=\"true\">{{ selectedCountry.flag }}</span>\n <span class=\"ngs-select-trigger-text\">\n {{ selectedCountry.name }}\n @if (showCountryCode()) {\n <span class=\"code\">({{ selectedCountry.code }})</span>\n }\n </span>\n </span>\n } @else if (selectedCountries.length > 1) {\n <span class=\"ngs-select-trigger-content\">\n <span class=\"ngs-select-trigger-text\">\n {{ selectedCountriesText() }}\n </span>\n </span>\n }\n\n </ngs-select-trigger>\n\n <ngs-select-header>\n <div class=\"sticky top-0 z-1 bg-surface-container-lowest\">\n <input #searchInput\n type=\"text\"\n placeholder=\"Search...\"\n autocomplete=\"off\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"onCountrySearch($event)\"\n class=\"w-full text-sm focus:outline-none border-b border-surface-container-high focus:border-b-primary h-14 px-3\">\n @if (searchTerm().trim()) {\n <div class=\"absolute end-1 top-1/2 -translate-y-1/2\">\n <button\n ngsIconButton\n (click)=\"clearSearch($event)\"\n class=\"clear-button\"\n type=\"button\"\n aria-label=\"Clear search\">\n <ngs-icon name=\"fluent:dismiss-24-regular\"/>\n </button>\n </div>\n }\n </div>\n </ngs-select-header>\n\n @for (country of visibleCountries(); track country.code) {\n <ngs-option [value]=\"country.code\">\n <div class=\"flex items-center gap-2\">\n <span class=\"select-option-icon text-2xl\" aria-hidden=\"true\">{{ country.flag }}</span>\n <span class=\"select-option-text\">\n {{ country.name }}\n @if (showCountryCode()) {\n <span class=\"code\">({{ country.code }})</span>\n }\n </span>\n </div>\n </ngs-option>\n } @empty {\n @if (searchTerm()) {\n <div class=\"text-sm px-4 py-3\">\n <span i18n>Country not found</span>.\n </div>\n }\n }\n\n @if (visibleCountryCount() < filteredCountries().length) {\n <div class=\"select-loading\">\n Loading more countries...\n </div>\n }\n</ngs-select>\n", styles: [":host{display:block}:host .flag-icon{display:inline-flex;align-items:center;justify-content:center;width:22px;font-size:22px;line-height:1}:host .select-option-content{display:flex;align-items:center;gap:var(--ngs-dropdown-item-gap);min-width:0;width:100%;height:100%;overflow:hidden;white-space:nowrap}:host .select-option-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .code{text-transform:uppercase;color:var(--ngs-color-neutral-500)}:host .select-loading{padding:calc(var(--spacing, .25rem) * 2) calc(var(--spacing, .25rem) * 4);color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
586
+ }], ctorParameters: () => [], propDecorators: { searchTerm: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchTerm", required: false }] }, { type: i0.Output, args: ["searchTermChange"] }], valueSignal: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], idSignal: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], placeholderInputSignal: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], isRequiredSignal: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }, { type: i0.Output, args: ["requiredChange"] }], isDisabledSignal: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], hideCheckIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideCheckIcon", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], ariaDescribedby: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-describedby", required: false }] }], showCountryCode: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCountryCode", required: false }] }], ngsSelect: [{ type: i0.ViewChild, args: ['ngsSelect', { isSignal: true }] }], searchInput: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }] } });
544
587
 
545
588
  /**
546
589
  * Generated bundle index. Do not edit.