@compsych-ui-components/angular 8.0.2 → 8.0.4
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.
- package/esm2022/index.js +6 -0
- package/esm2022/index.js.map +1 -1
- package/esm2022/lib/card-base/card.js +3 -3
- package/esm2022/lib/card-base/card.js.map +1 -1
- package/esm2022/lib/chip/chip.js +168 -0
- package/esm2022/lib/chip/chip.js.map +1 -0
- package/esm2022/lib/dropdown/dropdown.js +5 -177
- package/esm2022/lib/dropdown/dropdown.js.map +1 -1
- package/index.d.ts +13 -10
- package/lib/card-base/card.d.ts +3 -3
- package/lib/chip/chip.d.ts +108 -0
- package/package.json +1 -1
|
@@ -179,7 +179,9 @@ export class DropdownComponent {
|
|
|
179
179
|
}
|
|
180
180
|
/** Value array bound to the CDK listbox (single-select, so 0 or 1 entry). */
|
|
181
181
|
get listboxValue() {
|
|
182
|
-
|
|
182
|
+
if (!this.value || !this.options.some((o) => o.value === this.value))
|
|
183
|
+
return [];
|
|
184
|
+
return [this.value];
|
|
183
185
|
}
|
|
184
186
|
/** True when the bound FormControl is invalid and touched, or when `error` is explicitly set. */
|
|
185
187
|
get hasError() {
|
|
@@ -297,185 +299,11 @@ export class DropdownComponent {
|
|
|
297
299
|
this.cdr.markForCheck();
|
|
298
300
|
}
|
|
299
301
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
300
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DropdownComponent, isStandalone: true, selector: "compsych-dropdown", inputs: { label: "label", placeholder: "placeholder", value: "value", options: "options", size: "size", id: "id", ariaDescribedBy: "ariaDescribedBy", supportingText: "supportingText", errorMessage: "errorMessage", errorMessages: "errorMessages", required: ["required", "required", booleanAttribute], error: ["error", "error", booleanAttribute], disabled: ["disabled", "disabled", booleanAttribute], hideLabel: ["hideLabel", "hideLabel", booleanAttribute], labelPosition: "labelPosition", inlineLabel: ["inlineLabel", "inlineLabel", booleanAttribute], leadingIcon: "leadingIcon", optionsLeadingIcon: "optionsLeadingIcon" }, outputs: { valueChange: "valueChange", opened: "opened", closed: "closed" }, viewQueries: [{ propertyName: "triggerBtn", first: true, predicate: ["triggerBtn"], descendants: true }, { propertyName: "listboxEl", first: true, predicate: ["listboxEl"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
301
|
-
<div [class]="wrapperClasses">
|
|
302
|
-
<div class="label-row" [class.sr-only]="hideLabel || inlineLabel">
|
|
303
|
-
<label class="label" [id]="labelId" [for]="triggerId">
|
|
304
|
-
{{ label }}
|
|
305
|
-
@if (isRequired) {
|
|
306
|
-
<span class="required-marker" aria-hidden="true"> *</span>
|
|
307
|
-
}
|
|
308
|
-
</label>
|
|
309
|
-
</div>
|
|
310
|
-
|
|
311
|
-
<button
|
|
312
|
-
#triggerBtn
|
|
313
|
-
cdkOverlayOrigin
|
|
314
|
-
#origin="cdkOverlayOrigin"
|
|
315
|
-
type="button"
|
|
316
|
-
class="trigger"
|
|
317
|
-
[id]="triggerId"
|
|
318
|
-
[class.placeholder]="!selectedOption && !inlineLabel"
|
|
319
|
-
[class.error]="hasError"
|
|
320
|
-
[class.open]="isOpen"
|
|
321
|
-
[disabled]="disabled"
|
|
322
|
-
role="combobox"
|
|
323
|
-
aria-haspopup="listbox"
|
|
324
|
-
[attr.aria-expanded]="isOpen"
|
|
325
|
-
[attr.aria-controls]="listboxId"
|
|
326
|
-
[attr.aria-labelledby]="labelId + ' ' + triggerId"
|
|
327
|
-
[attr.aria-invalid]="hasError ? 'true' : null"
|
|
328
|
-
[attr.aria-required]="isRequired ? 'true' : null"
|
|
329
|
-
[attr.aria-describedby]="describedBy"
|
|
330
|
-
(click)="toggle()"
|
|
331
|
-
(keydown)="onTriggerKeydown($event)"
|
|
332
|
-
>
|
|
333
|
-
@if (triggerIconSvg) {
|
|
334
|
-
<span class="trigger-icon" aria-hidden="true" [innerHTML]="triggerIconSvg"></span>
|
|
335
|
-
}
|
|
336
|
-
<span class="trigger-text">{{ triggerText }}</span>
|
|
337
|
-
<span class="trigger-chevron" aria-hidden="true" [innerHTML]="chevronSvg"></span>
|
|
338
|
-
</button>
|
|
339
|
-
|
|
340
|
-
@if (displayedSupportingText) {
|
|
341
|
-
<span class="supporting-text" [class.supporting-text--error]="hasError" [id]="triggerId + '-supporting'">
|
|
342
|
-
{{ displayedSupportingText }}
|
|
343
|
-
</span>
|
|
344
|
-
}
|
|
345
|
-
</div>
|
|
346
|
-
|
|
347
|
-
<ng-template
|
|
348
|
-
cdkConnectedOverlay
|
|
349
|
-
[cdkConnectedOverlayOrigin]="origin"
|
|
350
|
-
[cdkConnectedOverlayOpen]="isOpen"
|
|
351
|
-
[cdkConnectedOverlayWidth]="overlayWidth"
|
|
352
|
-
[cdkConnectedOverlayPositions]="overlayPositions"
|
|
353
|
-
[cdkConnectedOverlayViewportMargin]="8"
|
|
354
|
-
(overlayOutsideClick)="close()"
|
|
355
|
-
(attach)="onPanelAttach()"
|
|
356
|
-
(detach)="close()"
|
|
357
|
-
>
|
|
358
|
-
<ul
|
|
359
|
-
#listboxEl
|
|
360
|
-
cdkListbox
|
|
361
|
-
class="panel"
|
|
362
|
-
[class.sm]="size === 'sm'"
|
|
363
|
-
[style.width.px]="overlayWidth"
|
|
364
|
-
[id]="listboxId"
|
|
365
|
-
[attr.aria-labelledby]="labelId"
|
|
366
|
-
[cdkListboxValue]="listboxValue"
|
|
367
|
-
(cdkListboxValueChange)="onListboxValueChange($event)"
|
|
368
|
-
(keydown)="onListboxKeydown($event)"
|
|
369
|
-
>
|
|
370
|
-
@for (opt of options; track opt.value) {
|
|
371
|
-
<li class="option" [class.error]="opt.error" [cdkOption]="opt.value" [cdkOptionDisabled]="!!opt.disabled">
|
|
372
|
-
<span class="option-inner">
|
|
373
|
-
@if (opt.iconSvg || opt.icon || optionsLeadingIcon) {
|
|
374
|
-
<span class="option-icon" aria-hidden="true" [innerHTML]="optionIcon(opt)"></span>
|
|
375
|
-
}
|
|
376
|
-
<span class="option-text">
|
|
377
|
-
<span class="option-label">{{ opt.label }}</span>
|
|
378
|
-
@if (opt.supportingText) {
|
|
379
|
-
<span class="option-support">{{ opt.supportingText }}</span>
|
|
380
|
-
}
|
|
381
|
-
</span>
|
|
382
|
-
</span>
|
|
383
|
-
</li>
|
|
384
|
-
}
|
|
385
|
-
</ul>
|
|
386
|
-
</ng-template>
|
|
387
|
-
`, isInline: true, styles: [":host{display:block}.cmp-dropdown{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-8)}.cmp-dropdown.label-left{display:grid;grid-template-columns:auto minmax(0,1fr);grid-template-areas:\"label field\" \". support\";align-items:center;column-gap:var(--spacing-padding-sys-padding-12);row-gap:var(--spacing-padding-sys-padding-4)}.cmp-dropdown.label-left .label-row{grid-area:label}.cmp-dropdown.label-left .trigger{grid-area:field}.cmp-dropdown.label-left .supporting-text{grid-area:support}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.label-row{display:flex;justify-content:space-between;align-items:center;gap:var(--spacing-padding-sys-padding-8)}.label{font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);font-weight:var(--label-medium-sys-font-weight-emphasized);line-height:var(--label-medium-sys-line-height);letter-spacing:var(--label-medium-sys-tracking);color:var(--surface-surface-sys-on-surface)}.cmp-dropdown.lg .label{font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--label-large-sys-line-height);letter-spacing:var(--label-large-sys-tracking)}.required-marker{color:var(--custom-info-sys-info)}.cmp-dropdown.disabled .label{opacity:.64}.trigger{box-sizing:border-box;width:100%;display:flex;align-items:center;gap:var(--spacing-padding-sys-padding-8);margin:0;border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline-variant);border-radius:var(--border-radius-sys-radius-sm);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);font-family:var(--body-medium-sys-font-family);font-weight:var(--body-medium-sys-font-weight);text-align:start;cursor:pointer;appearance:none;-webkit-appearance:none;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background-color .15s ease}.trigger-icon{display:flex;align-items:center;flex-shrink:0;color:var(--surface-surface-sys-on-surface-variant)}.trigger-text{flex:1 0 0;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.trigger.placeholder .trigger-text{color:var(--surface-surface-sys-on-surface-variant)}.trigger-chevron{display:flex;align-items:center;flex-shrink:0;color:var(--surface-surface-sys-on-surface-variant);transition:color .15s ease,transform .16s ease}.trigger:focus-visible .trigger-chevron,.trigger.open .trigger-chevron{color:var(--accent-primary-sys-primary)}.trigger.open .trigger-chevron{transform:rotate(180deg)}.trigger.error:not(:disabled) .trigger-chevron{color:var(--error-sys-error)}@media(prefers-reduced-motion:reduce){.trigger-chevron{transition:color .15s ease}}.trigger:hover:not(:disabled):not(.open){box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014}.trigger:focus-visible,.trigger.open{border-color:var(--accent-primary-sys-primary);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.trigger.error:not(:disabled):not(.open){border-color:var(--error-sys-error)}.trigger.error:focus-visible,.trigger.error.open{border-color:var(--error-sys-error);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.trigger:disabled{background-color:var(--surface-surface-container-sys-surface-container-low);color:var(--surface-surface-sys-on-surface-variant);border-color:var(--outline-sys-outline-variant);cursor:not-allowed}.trigger:disabled .trigger-text,.trigger:disabled .trigger-icon,.trigger:disabled .trigger-chevron{opacity:.64}.supporting-text{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);font-weight:var(--body-small-sys-font-weight);line-height:var(--body-small-sys-line-height);color:var(--surface-surface-sys-on-surface-variant)}.supporting-text--error{color:var(--error-sys-error)}.cmp-dropdown.disabled .supporting-text{opacity:.64}.cmp-dropdown.sm .trigger{height:40px;gap:var(--spacing-padding-sys-padding-4);padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-12);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.cmp-dropdown.md .trigger{height:48px;padding:var(--spacing-padding-sys-padding-12) var(--spacing-padding-sys-padding-16);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height)}.cmp-dropdown.lg .trigger{height:56px;padding:var(--spacing-padding-sys-padding-16) var(--spacing-padding-sys-padding-20);border-radius:var(--border-radius-sys-radius-md);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height)}.panel{box-sizing:border-box;list-style:none;margin:0;padding:var(--spacing-padding-sys-padding-4) 0;max-height:340px;overflow-x:hidden;overflow-y:auto;background-color:var(--surface-surface-container-sys-surface-container-lowest);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline-variant);border-radius:var(--border-radius-sys-radius-md);box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014;outline:none;font-family:var(--body-medium-sys-font-family)}.option{box-sizing:border-box;display:block;padding:var(--spacing-padding-sys-padding-4) var(--spacing-padding-sys-padding-8);cursor:pointer;outline:none;-webkit-user-select:none;user-select:none}.option-inner{display:flex;align-items:flex-start;gap:var(--spacing-padding-sys-padding-12);padding:var(--spacing-padding-sys-padding-8);border-radius:var(--border-radius-sys-radius-sm);color:var(--surface-surface-sys-on-surface);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height);transition:background-color .12s ease-out}.panel.sm .option-inner{font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.option-icon{display:flex;align-items:center;justify-content:center;flex-shrink:0;height:20px;color:var(--surface-surface-sys-on-surface);margin-top:var(--spacing-padding-sys-padding-2)}.option-text{flex:1 1 auto;min-width:0;display:flex;align-items:baseline;flex-wrap:wrap;column-gap:var(--spacing-padding-sys-padding-8);row-gap:var(--spacing-padding-sys-padding-2)}.option-label{min-width:0}.option-support{color:var(--surface-surface-sys-on-surface-variant);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.option.error .option-icon,.option.error .option-label{color:var(--error-sys-error)}.option:hover:not([aria-disabled=true]) .option-inner{background-color:color-mix(in srgb,var(--surface-surface-sys-on-surface-variant) 8%,transparent)}.option:focus-visible{outline:none}.option:focus-visible .option-inner{background-color:color-mix(in srgb,var(--surface-surface-sys-on-surface-variant) 8%,transparent);outline:var(--border-width-sys-stroke-medium) solid var(--accent-primary-sys-primary);outline-offset:-2px}.option[aria-disabled=true]{cursor:not-allowed}.option[aria-disabled=true] .option-inner{color:var(--surface-surface-sys-on-surface-variant);opacity:.64}@media(forced-colors:active){.trigger{border:1px solid ButtonText;forced-color-adjust:none}.trigger:focus-visible,.trigger.open{outline:2px solid Highlight;outline-offset:2px}.trigger:disabled{color:GrayText;border-color:GrayText;background:ButtonFace;opacity:1}.cmp-dropdown.disabled .label{color:GrayText;opacity:1}.supporting-text--error{color:LinkText}.panel{border:1px solid ButtonText}.option:focus-visible .option-inner{outline:2px solid Highlight;outline-offset:-2px}}\n"], dependencies: [{ kind: "directive", type: CdkListbox, selector: "[cdkListbox]", inputs: ["id", "tabindex", "cdkListboxValue", "cdkListboxMultiple", "cdkListboxDisabled", "cdkListboxUseActiveDescendant", "cdkListboxOrientation", "cdkListboxCompareWith", "cdkListboxNavigationWrapDisabled", "cdkListboxNavigatesDisabledOptions"], outputs: ["cdkListboxValueChange"], exportAs: ["cdkListbox"] }, { kind: "directive", type: CdkOption, selector: "[cdkOption]", inputs: ["id", "cdkOption", "cdkOptionTypeaheadLabel", "cdkOptionDisabled", "tabindex"], exportAs: ["cdkOption"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
302
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DropdownComponent, isStandalone: true, selector: "compsych-dropdown", inputs: { label: "label", placeholder: "placeholder", value: "value", options: "options", size: "size", id: "id", ariaDescribedBy: "ariaDescribedBy", supportingText: "supportingText", errorMessage: "errorMessage", errorMessages: "errorMessages", required: ["required", "required", booleanAttribute], error: ["error", "error", booleanAttribute], disabled: ["disabled", "disabled", booleanAttribute], hideLabel: ["hideLabel", "hideLabel", booleanAttribute], labelPosition: "labelPosition", inlineLabel: ["inlineLabel", "inlineLabel", booleanAttribute], leadingIcon: "leadingIcon", optionsLeadingIcon: "optionsLeadingIcon" }, outputs: { valueChange: "valueChange", opened: "opened", closed: "closed" }, viewQueries: [{ propertyName: "triggerBtn", first: true, predicate: ["triggerBtn"], descendants: true }, { propertyName: "listboxEl", first: true, predicate: ["listboxEl"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div [class]=\"wrapperClasses\">\n <div class=\"label-row\" [class.sr-only]=\"hideLabel || inlineLabel\">\n <label class=\"label\" [id]=\"labelId\" [for]=\"triggerId\">\n {{ label }}\n @if (isRequired) {\n <span class=\"required-marker\" aria-hidden=\"true\"> *</span>\n }\n </label>\n </div>\n\n <button\n #triggerBtn\n cdkOverlayOrigin\n #origin=\"cdkOverlayOrigin\"\n type=\"button\"\n class=\"trigger\"\n [id]=\"triggerId\"\n [class.placeholder]=\"!selectedOption && !inlineLabel\"\n [class.error]=\"hasError\"\n [class.open]=\"isOpen\"\n [disabled]=\"disabled\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-labelledby]=\"labelId + ' ' + triggerId\"\n [attr.aria-invalid]=\"hasError ? 'true' : null\"\n [attr.aria-required]=\"isRequired ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy\"\n (click)=\"toggle()\"\n (keydown)=\"onTriggerKeydown($event)\"\n >\n @if (triggerIconSvg) {\n <span class=\"trigger-icon\" aria-hidden=\"true\" [innerHTML]=\"triggerIconSvg\"></span>\n }\n <span class=\"trigger-text\">{{ triggerText }}</span>\n <span class=\"trigger-chevron\" aria-hidden=\"true\" [innerHTML]=\"chevronSvg\"></span>\n </button>\n\n @if (displayedSupportingText) {\n <span class=\"supporting-text\" [class.supporting-text--error]=\"hasError\" [id]=\"triggerId + '-supporting'\">\n {{ displayedSupportingText }}\n </span>\n }\n</div>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"origin\"\n [cdkConnectedOverlayOpen]=\"isOpen\"\n [cdkConnectedOverlayWidth]=\"overlayWidth\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayViewportMargin]=\"8\"\n (overlayOutsideClick)=\"close()\"\n (attach)=\"onPanelAttach()\"\n (detach)=\"close()\"\n>\n @if (isOpen) {\n <ul\n #listboxEl\n cdkListbox\n class=\"panel\"\n [class.sm]=\"size === 'sm'\"\n [style.width.px]=\"overlayWidth\"\n [id]=\"listboxId\"\n [attr.aria-labelledby]=\"labelId\"\n [cdkListboxValue]=\"listboxValue\"\n (cdkListboxValueChange)=\"onListboxValueChange($event)\"\n (keydown)=\"onListboxKeydown($event)\"\n >\n @for (opt of options; track opt.value) {\n <li class=\"option\" [class.error]=\"opt.error\" [cdkOption]=\"opt.value\" [cdkOptionDisabled]=\"!!opt.disabled\">\n <span class=\"option-inner\">\n @if (opt.iconSvg || opt.icon || optionsLeadingIcon) {\n <span class=\"option-icon\" aria-hidden=\"true\" [innerHTML]=\"optionIcon(opt)\"></span>\n }\n <span class=\"option-text\">\n <span class=\"option-label\">{{ opt.label }}</span>\n @if (opt.supportingText) {\n <span class=\"option-support\">{{ opt.supportingText }}</span>\n }\n </span>\n </span>\n </li>\n }\n </ul>\n }\n</ng-template>\n", styles: [":host{display:block}.cmp-dropdown{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-8)}.cmp-dropdown.label-left{display:grid;grid-template-columns:auto minmax(0,1fr);grid-template-areas:\"label field\" \". support\";align-items:center;column-gap:var(--spacing-padding-sys-padding-12);row-gap:var(--spacing-padding-sys-padding-4)}.cmp-dropdown.label-left .label-row{grid-area:label}.cmp-dropdown.label-left .trigger{grid-area:field}.cmp-dropdown.label-left .supporting-text{grid-area:support}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.label-row{display:flex;justify-content:space-between;align-items:center;gap:var(--spacing-padding-sys-padding-8)}.label{font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);font-weight:var(--label-medium-sys-font-weight-emphasized);line-height:var(--label-medium-sys-line-height);letter-spacing:var(--label-medium-sys-tracking);color:var(--surface-surface-sys-on-surface)}.cmp-dropdown.lg .label{font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--label-large-sys-line-height);letter-spacing:var(--label-large-sys-tracking)}.required-marker{color:var(--custom-info-sys-info)}.cmp-dropdown.disabled .label{opacity:.64}.trigger{box-sizing:border-box;width:100%;display:flex;align-items:center;gap:var(--spacing-padding-sys-padding-8);margin:0;border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline-variant);border-radius:var(--border-radius-sys-radius-sm);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);font-family:var(--body-medium-sys-font-family);font-weight:var(--body-medium-sys-font-weight);text-align:start;cursor:pointer;appearance:none;-webkit-appearance:none;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background-color .15s ease}.trigger-icon{display:flex;align-items:center;flex-shrink:0;color:var(--surface-surface-sys-on-surface-variant)}.trigger-text{flex:1 0 0;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.trigger.placeholder .trigger-text{color:var(--surface-surface-sys-on-surface-variant)}.trigger-chevron{display:flex;align-items:center;flex-shrink:0;color:var(--surface-surface-sys-on-surface-variant);transition:color .15s ease,transform .16s ease}.trigger:focus-visible .trigger-chevron,.trigger.open .trigger-chevron{color:var(--accent-primary-sys-primary)}.trigger.open .trigger-chevron{transform:rotate(180deg)}.trigger.error:not(:disabled) .trigger-chevron{color:var(--error-sys-error)}@media(prefers-reduced-motion:reduce){.trigger-chevron{transition:color .15s ease}}.trigger:hover:not(:disabled):not(.open){box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014}.trigger:focus-visible,.trigger.open{border-color:var(--accent-primary-sys-primary);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.trigger.error:not(:disabled):not(.open){border-color:var(--error-sys-error)}.trigger.error:focus-visible,.trigger.error.open{border-color:var(--error-sys-error);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.trigger:disabled{background-color:var(--surface-surface-container-sys-surface-container-low);color:var(--surface-surface-sys-on-surface-variant);border-color:var(--outline-sys-outline-variant);cursor:not-allowed}.trigger:disabled .trigger-text,.trigger:disabled .trigger-icon,.trigger:disabled .trigger-chevron{opacity:.64}.supporting-text{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);font-weight:var(--body-small-sys-font-weight);line-height:var(--body-small-sys-line-height);color:var(--surface-surface-sys-on-surface-variant)}.supporting-text--error{color:var(--error-sys-error)}.cmp-dropdown.disabled .supporting-text{opacity:.64}.cmp-dropdown.sm .trigger{height:40px;gap:var(--spacing-padding-sys-padding-4);padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-12);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.cmp-dropdown.md .trigger{height:48px;padding:var(--spacing-padding-sys-padding-12) var(--spacing-padding-sys-padding-16);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height)}.cmp-dropdown.lg .trigger{height:56px;padding:var(--spacing-padding-sys-padding-16) var(--spacing-padding-sys-padding-20);border-radius:var(--border-radius-sys-radius-md);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height)}.panel{box-sizing:border-box;list-style:none;margin:0;padding:var(--spacing-padding-sys-padding-4) 0;max-height:340px;overflow-x:hidden;overflow-y:auto;background-color:var(--surface-surface-container-sys-surface-container-lowest);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline-variant);border-radius:var(--border-radius-sys-radius-md);box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014;outline:none;font-family:var(--body-medium-sys-font-family)}.option{box-sizing:border-box;display:block;padding:var(--spacing-padding-sys-padding-4) var(--spacing-padding-sys-padding-8);cursor:pointer;outline:none;-webkit-user-select:none;user-select:none}.option-inner{display:flex;align-items:flex-start;gap:var(--spacing-padding-sys-padding-12);padding:var(--spacing-padding-sys-padding-8);border-radius:var(--border-radius-sys-radius-sm);color:var(--surface-surface-sys-on-surface);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height);transition:background-color .12s ease-out}.panel.sm .option-inner{font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.option-icon{display:flex;align-items:center;justify-content:center;flex-shrink:0;height:20px;color:var(--surface-surface-sys-on-surface);margin-top:var(--spacing-padding-sys-padding-2)}.option-text{flex:1 1 auto;min-width:0;display:flex;align-items:baseline;flex-wrap:wrap;column-gap:var(--spacing-padding-sys-padding-8);row-gap:var(--spacing-padding-sys-padding-2)}.option-label{min-width:0}.option-support{color:var(--surface-surface-sys-on-surface-variant);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.option.error .option-icon,.option.error .option-label{color:var(--error-sys-error)}.option:hover:not([aria-disabled=true]) .option-inner{background-color:color-mix(in srgb,var(--surface-surface-sys-on-surface-variant) 8%,transparent)}.option:focus-visible{outline:none}.option:focus-visible .option-inner{background-color:color-mix(in srgb,var(--surface-surface-sys-on-surface-variant) 8%,transparent);outline:var(--border-width-sys-stroke-medium) solid var(--accent-primary-sys-primary);outline-offset:-2px}.option[aria-disabled=true]{cursor:not-allowed}.option[aria-disabled=true] .option-inner{color:var(--surface-surface-sys-on-surface-variant);opacity:.64}@media(forced-colors:active){.trigger{border:1px solid ButtonText;forced-color-adjust:none}.trigger:focus-visible,.trigger.open{outline:2px solid Highlight;outline-offset:2px}.trigger:disabled{color:GrayText;border-color:GrayText;background:ButtonFace;opacity:1}.cmp-dropdown.disabled .label{color:GrayText;opacity:1}.supporting-text--error{color:LinkText}.panel{border:1px solid ButtonText}.option:focus-visible .option-inner{outline:2px solid Highlight;outline-offset:-2px}}\n"], dependencies: [{ kind: "directive", type: CdkListbox, selector: "[cdkListbox]", inputs: ["id", "tabindex", "cdkListboxValue", "cdkListboxMultiple", "cdkListboxDisabled", "cdkListboxUseActiveDescendant", "cdkListboxOrientation", "cdkListboxCompareWith", "cdkListboxNavigationWrapDisabled", "cdkListboxNavigatesDisabledOptions"], outputs: ["cdkListboxValueChange"], exportAs: ["cdkListbox"] }, { kind: "directive", type: CdkOption, selector: "[cdkOption]", inputs: ["id", "cdkOption", "cdkOptionTypeaheadLabel", "cdkOptionDisabled", "tabindex"], exportAs: ["cdkOption"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
388
303
|
}
|
|
389
304
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DropdownComponent, decorators: [{
|
|
390
305
|
type: Component,
|
|
391
|
-
args: [{ selector: 'compsych-dropdown', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CdkListbox, CdkOption, CdkConnectedOverlay, CdkOverlayOrigin], template: `
|
|
392
|
-
<div [class]="wrapperClasses">
|
|
393
|
-
<div class="label-row" [class.sr-only]="hideLabel || inlineLabel">
|
|
394
|
-
<label class="label" [id]="labelId" [for]="triggerId">
|
|
395
|
-
{{ label }}
|
|
396
|
-
@if (isRequired) {
|
|
397
|
-
<span class="required-marker" aria-hidden="true"> *</span>
|
|
398
|
-
}
|
|
399
|
-
</label>
|
|
400
|
-
</div>
|
|
401
|
-
|
|
402
|
-
<button
|
|
403
|
-
#triggerBtn
|
|
404
|
-
cdkOverlayOrigin
|
|
405
|
-
#origin="cdkOverlayOrigin"
|
|
406
|
-
type="button"
|
|
407
|
-
class="trigger"
|
|
408
|
-
[id]="triggerId"
|
|
409
|
-
[class.placeholder]="!selectedOption && !inlineLabel"
|
|
410
|
-
[class.error]="hasError"
|
|
411
|
-
[class.open]="isOpen"
|
|
412
|
-
[disabled]="disabled"
|
|
413
|
-
role="combobox"
|
|
414
|
-
aria-haspopup="listbox"
|
|
415
|
-
[attr.aria-expanded]="isOpen"
|
|
416
|
-
[attr.aria-controls]="listboxId"
|
|
417
|
-
[attr.aria-labelledby]="labelId + ' ' + triggerId"
|
|
418
|
-
[attr.aria-invalid]="hasError ? 'true' : null"
|
|
419
|
-
[attr.aria-required]="isRequired ? 'true' : null"
|
|
420
|
-
[attr.aria-describedby]="describedBy"
|
|
421
|
-
(click)="toggle()"
|
|
422
|
-
(keydown)="onTriggerKeydown($event)"
|
|
423
|
-
>
|
|
424
|
-
@if (triggerIconSvg) {
|
|
425
|
-
<span class="trigger-icon" aria-hidden="true" [innerHTML]="triggerIconSvg"></span>
|
|
426
|
-
}
|
|
427
|
-
<span class="trigger-text">{{ triggerText }}</span>
|
|
428
|
-
<span class="trigger-chevron" aria-hidden="true" [innerHTML]="chevronSvg"></span>
|
|
429
|
-
</button>
|
|
430
|
-
|
|
431
|
-
@if (displayedSupportingText) {
|
|
432
|
-
<span class="supporting-text" [class.supporting-text--error]="hasError" [id]="triggerId + '-supporting'">
|
|
433
|
-
{{ displayedSupportingText }}
|
|
434
|
-
</span>
|
|
435
|
-
}
|
|
436
|
-
</div>
|
|
437
|
-
|
|
438
|
-
<ng-template
|
|
439
|
-
cdkConnectedOverlay
|
|
440
|
-
[cdkConnectedOverlayOrigin]="origin"
|
|
441
|
-
[cdkConnectedOverlayOpen]="isOpen"
|
|
442
|
-
[cdkConnectedOverlayWidth]="overlayWidth"
|
|
443
|
-
[cdkConnectedOverlayPositions]="overlayPositions"
|
|
444
|
-
[cdkConnectedOverlayViewportMargin]="8"
|
|
445
|
-
(overlayOutsideClick)="close()"
|
|
446
|
-
(attach)="onPanelAttach()"
|
|
447
|
-
(detach)="close()"
|
|
448
|
-
>
|
|
449
|
-
<ul
|
|
450
|
-
#listboxEl
|
|
451
|
-
cdkListbox
|
|
452
|
-
class="panel"
|
|
453
|
-
[class.sm]="size === 'sm'"
|
|
454
|
-
[style.width.px]="overlayWidth"
|
|
455
|
-
[id]="listboxId"
|
|
456
|
-
[attr.aria-labelledby]="labelId"
|
|
457
|
-
[cdkListboxValue]="listboxValue"
|
|
458
|
-
(cdkListboxValueChange)="onListboxValueChange($event)"
|
|
459
|
-
(keydown)="onListboxKeydown($event)"
|
|
460
|
-
>
|
|
461
|
-
@for (opt of options; track opt.value) {
|
|
462
|
-
<li class="option" [class.error]="opt.error" [cdkOption]="opt.value" [cdkOptionDisabled]="!!opt.disabled">
|
|
463
|
-
<span class="option-inner">
|
|
464
|
-
@if (opt.iconSvg || opt.icon || optionsLeadingIcon) {
|
|
465
|
-
<span class="option-icon" aria-hidden="true" [innerHTML]="optionIcon(opt)"></span>
|
|
466
|
-
}
|
|
467
|
-
<span class="option-text">
|
|
468
|
-
<span class="option-label">{{ opt.label }}</span>
|
|
469
|
-
@if (opt.supportingText) {
|
|
470
|
-
<span class="option-support">{{ opt.supportingText }}</span>
|
|
471
|
-
}
|
|
472
|
-
</span>
|
|
473
|
-
</span>
|
|
474
|
-
</li>
|
|
475
|
-
}
|
|
476
|
-
</ul>
|
|
477
|
-
</ng-template>
|
|
478
|
-
`, styles: [":host{display:block}.cmp-dropdown{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-8)}.cmp-dropdown.label-left{display:grid;grid-template-columns:auto minmax(0,1fr);grid-template-areas:\"label field\" \". support\";align-items:center;column-gap:var(--spacing-padding-sys-padding-12);row-gap:var(--spacing-padding-sys-padding-4)}.cmp-dropdown.label-left .label-row{grid-area:label}.cmp-dropdown.label-left .trigger{grid-area:field}.cmp-dropdown.label-left .supporting-text{grid-area:support}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.label-row{display:flex;justify-content:space-between;align-items:center;gap:var(--spacing-padding-sys-padding-8)}.label{font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);font-weight:var(--label-medium-sys-font-weight-emphasized);line-height:var(--label-medium-sys-line-height);letter-spacing:var(--label-medium-sys-tracking);color:var(--surface-surface-sys-on-surface)}.cmp-dropdown.lg .label{font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--label-large-sys-line-height);letter-spacing:var(--label-large-sys-tracking)}.required-marker{color:var(--custom-info-sys-info)}.cmp-dropdown.disabled .label{opacity:.64}.trigger{box-sizing:border-box;width:100%;display:flex;align-items:center;gap:var(--spacing-padding-sys-padding-8);margin:0;border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline-variant);border-radius:var(--border-radius-sys-radius-sm);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);font-family:var(--body-medium-sys-font-family);font-weight:var(--body-medium-sys-font-weight);text-align:start;cursor:pointer;appearance:none;-webkit-appearance:none;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background-color .15s ease}.trigger-icon{display:flex;align-items:center;flex-shrink:0;color:var(--surface-surface-sys-on-surface-variant)}.trigger-text{flex:1 0 0;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.trigger.placeholder .trigger-text{color:var(--surface-surface-sys-on-surface-variant)}.trigger-chevron{display:flex;align-items:center;flex-shrink:0;color:var(--surface-surface-sys-on-surface-variant);transition:color .15s ease,transform .16s ease}.trigger:focus-visible .trigger-chevron,.trigger.open .trigger-chevron{color:var(--accent-primary-sys-primary)}.trigger.open .trigger-chevron{transform:rotate(180deg)}.trigger.error:not(:disabled) .trigger-chevron{color:var(--error-sys-error)}@media(prefers-reduced-motion:reduce){.trigger-chevron{transition:color .15s ease}}.trigger:hover:not(:disabled):not(.open){box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014}.trigger:focus-visible,.trigger.open{border-color:var(--accent-primary-sys-primary);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.trigger.error:not(:disabled):not(.open){border-color:var(--error-sys-error)}.trigger.error:focus-visible,.trigger.error.open{border-color:var(--error-sys-error);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.trigger:disabled{background-color:var(--surface-surface-container-sys-surface-container-low);color:var(--surface-surface-sys-on-surface-variant);border-color:var(--outline-sys-outline-variant);cursor:not-allowed}.trigger:disabled .trigger-text,.trigger:disabled .trigger-icon,.trigger:disabled .trigger-chevron{opacity:.64}.supporting-text{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);font-weight:var(--body-small-sys-font-weight);line-height:var(--body-small-sys-line-height);color:var(--surface-surface-sys-on-surface-variant)}.supporting-text--error{color:var(--error-sys-error)}.cmp-dropdown.disabled .supporting-text{opacity:.64}.cmp-dropdown.sm .trigger{height:40px;gap:var(--spacing-padding-sys-padding-4);padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-12);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.cmp-dropdown.md .trigger{height:48px;padding:var(--spacing-padding-sys-padding-12) var(--spacing-padding-sys-padding-16);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height)}.cmp-dropdown.lg .trigger{height:56px;padding:var(--spacing-padding-sys-padding-16) var(--spacing-padding-sys-padding-20);border-radius:var(--border-radius-sys-radius-md);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height)}.panel{box-sizing:border-box;list-style:none;margin:0;padding:var(--spacing-padding-sys-padding-4) 0;max-height:340px;overflow-x:hidden;overflow-y:auto;background-color:var(--surface-surface-container-sys-surface-container-lowest);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline-variant);border-radius:var(--border-radius-sys-radius-md);box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014;outline:none;font-family:var(--body-medium-sys-font-family)}.option{box-sizing:border-box;display:block;padding:var(--spacing-padding-sys-padding-4) var(--spacing-padding-sys-padding-8);cursor:pointer;outline:none;-webkit-user-select:none;user-select:none}.option-inner{display:flex;align-items:flex-start;gap:var(--spacing-padding-sys-padding-12);padding:var(--spacing-padding-sys-padding-8);border-radius:var(--border-radius-sys-radius-sm);color:var(--surface-surface-sys-on-surface);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height);transition:background-color .12s ease-out}.panel.sm .option-inner{font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.option-icon{display:flex;align-items:center;justify-content:center;flex-shrink:0;height:20px;color:var(--surface-surface-sys-on-surface);margin-top:var(--spacing-padding-sys-padding-2)}.option-text{flex:1 1 auto;min-width:0;display:flex;align-items:baseline;flex-wrap:wrap;column-gap:var(--spacing-padding-sys-padding-8);row-gap:var(--spacing-padding-sys-padding-2)}.option-label{min-width:0}.option-support{color:var(--surface-surface-sys-on-surface-variant);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.option.error .option-icon,.option.error .option-label{color:var(--error-sys-error)}.option:hover:not([aria-disabled=true]) .option-inner{background-color:color-mix(in srgb,var(--surface-surface-sys-on-surface-variant) 8%,transparent)}.option:focus-visible{outline:none}.option:focus-visible .option-inner{background-color:color-mix(in srgb,var(--surface-surface-sys-on-surface-variant) 8%,transparent);outline:var(--border-width-sys-stroke-medium) solid var(--accent-primary-sys-primary);outline-offset:-2px}.option[aria-disabled=true]{cursor:not-allowed}.option[aria-disabled=true] .option-inner{color:var(--surface-surface-sys-on-surface-variant);opacity:.64}@media(forced-colors:active){.trigger{border:1px solid ButtonText;forced-color-adjust:none}.trigger:focus-visible,.trigger.open{outline:2px solid Highlight;outline-offset:2px}.trigger:disabled{color:GrayText;border-color:GrayText;background:ButtonFace;opacity:1}.cmp-dropdown.disabled .label{color:GrayText;opacity:1}.supporting-text--error{color:LinkText}.panel{border:1px solid ButtonText}.option:focus-visible .option-inner{outline:2px solid Highlight;outline-offset:-2px}}\n"] }]
|
|
306
|
+
args: [{ selector: 'compsych-dropdown', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CdkListbox, CdkOption, CdkConnectedOverlay, CdkOverlayOrigin], template: "<div [class]=\"wrapperClasses\">\n <div class=\"label-row\" [class.sr-only]=\"hideLabel || inlineLabel\">\n <label class=\"label\" [id]=\"labelId\" [for]=\"triggerId\">\n {{ label }}\n @if (isRequired) {\n <span class=\"required-marker\" aria-hidden=\"true\"> *</span>\n }\n </label>\n </div>\n\n <button\n #triggerBtn\n cdkOverlayOrigin\n #origin=\"cdkOverlayOrigin\"\n type=\"button\"\n class=\"trigger\"\n [id]=\"triggerId\"\n [class.placeholder]=\"!selectedOption && !inlineLabel\"\n [class.error]=\"hasError\"\n [class.open]=\"isOpen\"\n [disabled]=\"disabled\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-labelledby]=\"labelId + ' ' + triggerId\"\n [attr.aria-invalid]=\"hasError ? 'true' : null\"\n [attr.aria-required]=\"isRequired ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy\"\n (click)=\"toggle()\"\n (keydown)=\"onTriggerKeydown($event)\"\n >\n @if (triggerIconSvg) {\n <span class=\"trigger-icon\" aria-hidden=\"true\" [innerHTML]=\"triggerIconSvg\"></span>\n }\n <span class=\"trigger-text\">{{ triggerText }}</span>\n <span class=\"trigger-chevron\" aria-hidden=\"true\" [innerHTML]=\"chevronSvg\"></span>\n </button>\n\n @if (displayedSupportingText) {\n <span class=\"supporting-text\" [class.supporting-text--error]=\"hasError\" [id]=\"triggerId + '-supporting'\">\n {{ displayedSupportingText }}\n </span>\n }\n</div>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"origin\"\n [cdkConnectedOverlayOpen]=\"isOpen\"\n [cdkConnectedOverlayWidth]=\"overlayWidth\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayViewportMargin]=\"8\"\n (overlayOutsideClick)=\"close()\"\n (attach)=\"onPanelAttach()\"\n (detach)=\"close()\"\n>\n @if (isOpen) {\n <ul\n #listboxEl\n cdkListbox\n class=\"panel\"\n [class.sm]=\"size === 'sm'\"\n [style.width.px]=\"overlayWidth\"\n [id]=\"listboxId\"\n [attr.aria-labelledby]=\"labelId\"\n [cdkListboxValue]=\"listboxValue\"\n (cdkListboxValueChange)=\"onListboxValueChange($event)\"\n (keydown)=\"onListboxKeydown($event)\"\n >\n @for (opt of options; track opt.value) {\n <li class=\"option\" [class.error]=\"opt.error\" [cdkOption]=\"opt.value\" [cdkOptionDisabled]=\"!!opt.disabled\">\n <span class=\"option-inner\">\n @if (opt.iconSvg || opt.icon || optionsLeadingIcon) {\n <span class=\"option-icon\" aria-hidden=\"true\" [innerHTML]=\"optionIcon(opt)\"></span>\n }\n <span class=\"option-text\">\n <span class=\"option-label\">{{ opt.label }}</span>\n @if (opt.supportingText) {\n <span class=\"option-support\">{{ opt.supportingText }}</span>\n }\n </span>\n </span>\n </li>\n }\n </ul>\n }\n</ng-template>\n", styles: [":host{display:block}.cmp-dropdown{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-8)}.cmp-dropdown.label-left{display:grid;grid-template-columns:auto minmax(0,1fr);grid-template-areas:\"label field\" \". support\";align-items:center;column-gap:var(--spacing-padding-sys-padding-12);row-gap:var(--spacing-padding-sys-padding-4)}.cmp-dropdown.label-left .label-row{grid-area:label}.cmp-dropdown.label-left .trigger{grid-area:field}.cmp-dropdown.label-left .supporting-text{grid-area:support}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.label-row{display:flex;justify-content:space-between;align-items:center;gap:var(--spacing-padding-sys-padding-8)}.label{font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);font-weight:var(--label-medium-sys-font-weight-emphasized);line-height:var(--label-medium-sys-line-height);letter-spacing:var(--label-medium-sys-tracking);color:var(--surface-surface-sys-on-surface)}.cmp-dropdown.lg .label{font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--label-large-sys-line-height);letter-spacing:var(--label-large-sys-tracking)}.required-marker{color:var(--custom-info-sys-info)}.cmp-dropdown.disabled .label{opacity:.64}.trigger{box-sizing:border-box;width:100%;display:flex;align-items:center;gap:var(--spacing-padding-sys-padding-8);margin:0;border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline-variant);border-radius:var(--border-radius-sys-radius-sm);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);font-family:var(--body-medium-sys-font-family);font-weight:var(--body-medium-sys-font-weight);text-align:start;cursor:pointer;appearance:none;-webkit-appearance:none;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background-color .15s ease}.trigger-icon{display:flex;align-items:center;flex-shrink:0;color:var(--surface-surface-sys-on-surface-variant)}.trigger-text{flex:1 0 0;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.trigger.placeholder .trigger-text{color:var(--surface-surface-sys-on-surface-variant)}.trigger-chevron{display:flex;align-items:center;flex-shrink:0;color:var(--surface-surface-sys-on-surface-variant);transition:color .15s ease,transform .16s ease}.trigger:focus-visible .trigger-chevron,.trigger.open .trigger-chevron{color:var(--accent-primary-sys-primary)}.trigger.open .trigger-chevron{transform:rotate(180deg)}.trigger.error:not(:disabled) .trigger-chevron{color:var(--error-sys-error)}@media(prefers-reduced-motion:reduce){.trigger-chevron{transition:color .15s ease}}.trigger:hover:not(:disabled):not(.open){box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014}.trigger:focus-visible,.trigger.open{border-color:var(--accent-primary-sys-primary);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.trigger.error:not(:disabled):not(.open){border-color:var(--error-sys-error)}.trigger.error:focus-visible,.trigger.error.open{border-color:var(--error-sys-error);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.trigger:disabled{background-color:var(--surface-surface-container-sys-surface-container-low);color:var(--surface-surface-sys-on-surface-variant);border-color:var(--outline-sys-outline-variant);cursor:not-allowed}.trigger:disabled .trigger-text,.trigger:disabled .trigger-icon,.trigger:disabled .trigger-chevron{opacity:.64}.supporting-text{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);font-weight:var(--body-small-sys-font-weight);line-height:var(--body-small-sys-line-height);color:var(--surface-surface-sys-on-surface-variant)}.supporting-text--error{color:var(--error-sys-error)}.cmp-dropdown.disabled .supporting-text{opacity:.64}.cmp-dropdown.sm .trigger{height:40px;gap:var(--spacing-padding-sys-padding-4);padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-12);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.cmp-dropdown.md .trigger{height:48px;padding:var(--spacing-padding-sys-padding-12) var(--spacing-padding-sys-padding-16);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height)}.cmp-dropdown.lg .trigger{height:56px;padding:var(--spacing-padding-sys-padding-16) var(--spacing-padding-sys-padding-20);border-radius:var(--border-radius-sys-radius-md);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height)}.panel{box-sizing:border-box;list-style:none;margin:0;padding:var(--spacing-padding-sys-padding-4) 0;max-height:340px;overflow-x:hidden;overflow-y:auto;background-color:var(--surface-surface-container-sys-surface-container-lowest);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline-variant);border-radius:var(--border-radius-sys-radius-md);box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014;outline:none;font-family:var(--body-medium-sys-font-family)}.option{box-sizing:border-box;display:block;padding:var(--spacing-padding-sys-padding-4) var(--spacing-padding-sys-padding-8);cursor:pointer;outline:none;-webkit-user-select:none;user-select:none}.option-inner{display:flex;align-items:flex-start;gap:var(--spacing-padding-sys-padding-12);padding:var(--spacing-padding-sys-padding-8);border-radius:var(--border-radius-sys-radius-sm);color:var(--surface-surface-sys-on-surface);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height);transition:background-color .12s ease-out}.panel.sm .option-inner{font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.option-icon{display:flex;align-items:center;justify-content:center;flex-shrink:0;height:20px;color:var(--surface-surface-sys-on-surface);margin-top:var(--spacing-padding-sys-padding-2)}.option-text{flex:1 1 auto;min-width:0;display:flex;align-items:baseline;flex-wrap:wrap;column-gap:var(--spacing-padding-sys-padding-8);row-gap:var(--spacing-padding-sys-padding-2)}.option-label{min-width:0}.option-support{color:var(--surface-surface-sys-on-surface-variant);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.option.error .option-icon,.option.error .option-label{color:var(--error-sys-error)}.option:hover:not([aria-disabled=true]) .option-inner{background-color:color-mix(in srgb,var(--surface-surface-sys-on-surface-variant) 8%,transparent)}.option:focus-visible{outline:none}.option:focus-visible .option-inner{background-color:color-mix(in srgb,var(--surface-surface-sys-on-surface-variant) 8%,transparent);outline:var(--border-width-sys-stroke-medium) solid var(--accent-primary-sys-primary);outline-offset:-2px}.option[aria-disabled=true]{cursor:not-allowed}.option[aria-disabled=true] .option-inner{color:var(--surface-surface-sys-on-surface-variant);opacity:.64}@media(forced-colors:active){.trigger{border:1px solid ButtonText;forced-color-adjust:none}.trigger:focus-visible,.trigger.open{outline:2px solid Highlight;outline-offset:2px}.trigger:disabled{color:GrayText;border-color:GrayText;background:ButtonFace;opacity:1}.cmp-dropdown.disabled .label{color:GrayText;opacity:1}.supporting-text--error{color:LinkText}.panel{border:1px solid ButtonText}.option:focus-visible .option-inner{outline:2px solid Highlight;outline-offset:-2px}}\n"] }]
|
|
479
307
|
}], ctorParameters: () => [], propDecorators: { triggerBtn: [{
|
|
480
308
|
type: ViewChild,
|
|
481
309
|
args: ['triggerBtn']
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/dropdown/dropdown.ts"],"names":[],"mappings":"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","sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ElementRef,\r\n EventEmitter,\r\n Input,\r\n OnChanges,\r\n Output,\r\n ViewChild,\r\n booleanAttribute,\r\n inject,\r\n} from '@angular/core';\r\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\r\nimport { ControlValueAccessor, NgControl, Validators } from '@angular/forms';\r\nimport { CdkListbox, CdkOption, ListboxValueChangeEvent } from '@angular/cdk/listbox';\r\nimport { CdkConnectedOverlay, CdkOverlayOrigin, ConnectedPosition } from '@angular/cdk/overlay';\r\nimport { LUCIDE_ICONS } from '@compsych-ui-components/shared/assets/lucide-icons.generated';\r\nimport { DropdownLabelPosition, DropdownOption, DropdownSize } from './dropdown-types';\r\n\r\nlet nextId = 0;\r\n\r\n/**\r\n * A labelled single-select dropdown built on the CDK Listbox (`@angular/cdk/listbox`)\r\n * and CDK Overlay. The trigger renders the closed field (label, placeholder/selected\r\n * value and chevron); the options open in an overlay panel with full keyboard support\r\n * (arrow keys, Home/End, typeahead) provided by the CDK listbox.\r\n *\r\n * Implements `ControlValueAccessor` so it works with both reactive forms\r\n * (`formControlName`, `[formControl]`) and template-driven forms (`ngModel`). The error\r\n * visual state is driven automatically by the bound `FormControl`'s validity once the\r\n * field has been touched, and can also be set explicitly via the `error` input.\r\n */\r\n@Component({\r\n selector: 'compsych-dropdown',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n imports: [CdkListbox, CdkOption, CdkConnectedOverlay, CdkOverlayOrigin],\r\n template: `\r\n <div [class]=\"wrapperClasses\">\r\n <div class=\"label-row\" [class.sr-only]=\"hideLabel || inlineLabel\">\r\n <label class=\"label\" [id]=\"labelId\" [for]=\"triggerId\">\r\n {{ label }}\r\n @if (isRequired) {\r\n <span class=\"required-marker\" aria-hidden=\"true\"> *</span>\r\n }\r\n </label>\r\n </div>\r\n\r\n <button\r\n #triggerBtn\r\n cdkOverlayOrigin\r\n #origin=\"cdkOverlayOrigin\"\r\n type=\"button\"\r\n class=\"trigger\"\r\n [id]=\"triggerId\"\r\n [class.placeholder]=\"!selectedOption && !inlineLabel\"\r\n [class.error]=\"hasError\"\r\n [class.open]=\"isOpen\"\r\n [disabled]=\"disabled\"\r\n role=\"combobox\"\r\n aria-haspopup=\"listbox\"\r\n [attr.aria-expanded]=\"isOpen\"\r\n [attr.aria-controls]=\"listboxId\"\r\n [attr.aria-labelledby]=\"labelId + ' ' + triggerId\"\r\n [attr.aria-invalid]=\"hasError ? 'true' : null\"\r\n [attr.aria-required]=\"isRequired ? 'true' : null\"\r\n [attr.aria-describedby]=\"describedBy\"\r\n (click)=\"toggle()\"\r\n (keydown)=\"onTriggerKeydown($event)\"\r\n >\r\n @if (triggerIconSvg) {\r\n <span class=\"trigger-icon\" aria-hidden=\"true\" [innerHTML]=\"triggerIconSvg\"></span>\r\n }\r\n <span class=\"trigger-text\">{{ triggerText }}</span>\r\n <span class=\"trigger-chevron\" aria-hidden=\"true\" [innerHTML]=\"chevronSvg\"></span>\r\n </button>\r\n\r\n @if (displayedSupportingText) {\r\n <span class=\"supporting-text\" [class.supporting-text--error]=\"hasError\" [id]=\"triggerId + '-supporting'\">\r\n {{ displayedSupportingText }}\r\n </span>\r\n }\r\n </div>\r\n\r\n <ng-template\r\n cdkConnectedOverlay\r\n [cdkConnectedOverlayOrigin]=\"origin\"\r\n [cdkConnectedOverlayOpen]=\"isOpen\"\r\n [cdkConnectedOverlayWidth]=\"overlayWidth\"\r\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\r\n [cdkConnectedOverlayViewportMargin]=\"8\"\r\n (overlayOutsideClick)=\"close()\"\r\n (attach)=\"onPanelAttach()\"\r\n (detach)=\"close()\"\r\n >\r\n <ul\r\n #listboxEl\r\n cdkListbox\r\n class=\"panel\"\r\n [class.sm]=\"size === 'sm'\"\r\n [style.width.px]=\"overlayWidth\"\r\n [id]=\"listboxId\"\r\n [attr.aria-labelledby]=\"labelId\"\r\n [cdkListboxValue]=\"listboxValue\"\r\n (cdkListboxValueChange)=\"onListboxValueChange($event)\"\r\n (keydown)=\"onListboxKeydown($event)\"\r\n >\r\n @for (opt of options; track opt.value) {\r\n <li class=\"option\" [class.error]=\"opt.error\" [cdkOption]=\"opt.value\" [cdkOptionDisabled]=\"!!opt.disabled\">\r\n <span class=\"option-inner\">\r\n @if (opt.iconSvg || opt.icon || optionsLeadingIcon) {\r\n <span class=\"option-icon\" aria-hidden=\"true\" [innerHTML]=\"optionIcon(opt)\"></span>\r\n }\r\n <span class=\"option-text\">\r\n <span class=\"option-label\">{{ opt.label }}</span>\r\n @if (opt.supportingText) {\r\n <span class=\"option-support\">{{ opt.supportingText }}</span>\r\n }\r\n </span>\r\n </span>\r\n </li>\r\n }\r\n </ul>\r\n </ng-template>\r\n `,\r\n styleUrl: './dropdown.css',\r\n})\r\nexport class DropdownComponent implements ControlValueAccessor, OnChanges {\r\n private readonly _autoId = `cmp-dropdown-${++nextId}`;\r\n private readonly cdr = inject(ChangeDetectorRef);\r\n private readonly sanitizer = inject(DomSanitizer);\r\n private _onChange: (value: string) => void = () => {};\r\n private _onTouched: () => void = () => {};\r\n\r\n /** Injected when the component is used inside a reactive or template-driven form. */\r\n protected readonly ngControl: NgControl | null;\r\n\r\n constructor() {\r\n const ctrl = inject(NgControl, { optional: true, self: true });\r\n if (ctrl) ctrl.valueAccessor = this;\r\n this.ngControl = ctrl;\r\n this.chevronSvg = this.buildIconSvg('chevron-down');\r\n }\r\n\r\n @ViewChild('triggerBtn') private triggerBtn?: ElementRef<HTMLButtonElement>;\r\n @ViewChild('listboxEl') private listboxEl?: ElementRef<HTMLUListElement>;\r\n\r\n /** Label displayed above the trigger. */\r\n @Input() label = '';\r\n /** Text shown in the trigger when no option is selected. */\r\n @Input() placeholder = '';\r\n /** Currently selected value. When used in a form, the form controls the value. */\r\n @Input() value = '';\r\n /** Selectable options rendered in the overlay panel. */\r\n @Input() options: DropdownOption[] = [];\r\n /** Size variant — controls height, padding, and font size. Defaults to `'md'`. */\r\n @Input() size: DropdownSize = 'md';\r\n /** Native `id` attribute for the trigger. Auto-generated if omitted. */\r\n @Input() id?: string;\r\n /**\r\n * Id(s) of external elements that describe the field, space-separated.\r\n * Forwarded to `aria-describedby` on the trigger.\r\n */\r\n @Input() ariaDescribedBy?: string;\r\n /** Text displayed below the trigger. Shown in red when in error state. */\r\n @Input() supportingText = '';\r\n /**\r\n * Static error text shown in red when in error state. Used when the dropdown is\r\n * outside a form (with `[error]=\"true\"`). When inside a form, prefer `errorMessages`.\r\n */\r\n @Input() errorMessage = '';\r\n /**\r\n * Map of Angular validator error keys to display strings. The component reads\r\n * `ngControl.errors` to determine which validator failed and shows the matching\r\n * message in red below the trigger. Example: `{ required: 'Please choose an option' }`\r\n */\r\n @Input() errorMessages: Record<string, string> = {};\r\n /**\r\n * Adds a blue asterisk after the label. When used in a form, this is set\r\n * automatically if the bound `FormControl` has `Validators.required`.\r\n * Supports boolean coercion.\r\n */\r\n @Input({ transform: booleanAttribute }) required = false;\r\n /**\r\n * Applies the error visual state (red border) explicitly. When used in a form,\r\n * the error state is driven automatically from the `FormControl`'s validity once\r\n * the field is touched — this input is only needed outside of forms. Supports boolean coercion.\r\n */\r\n @Input({ transform: booleanAttribute }) error = false;\r\n /** Disables the dropdown. When used in a form, also responds to `FormControl.disable()`. Supports boolean coercion. */\r\n @Input({ transform: booleanAttribute }) disabled = false;\r\n /** Visually hides the label while keeping it accessible to screen readers. Supports boolean coercion. */\r\n @Input({ transform: booleanAttribute }) hideLabel = false;\r\n /** Places the label above the trigger (`'top'`, default) or beside it (`'left'`). */\r\n @Input() labelPosition: DropdownLabelPosition = 'top';\r\n /**\r\n * Renders the label as the trigger's own resting text instead of a separate\r\n * label above/beside it — e.g. the trigger reads \"I want to\" until an option\r\n * is chosen. No visible external label is shown, but the `label` is still\r\n * exposed to assistive tech (a screen-reader-only `<label>` + `aria-labelledby`),\r\n * so the field is announced as a properly labelled combobox. Supports boolean coercion.\r\n */\r\n @Input({ transform: booleanAttribute }) inlineLabel = false;\r\n /** Lucide icon name rendered inside the trigger, before the displayed value/placeholder (e.g. `'globe'`). */\r\n @Input() leadingIcon?: string;\r\n /** Default Lucide icon shown before every option's label; each option's own `icon` (from the Icon Mapping) overrides it. */\r\n @Input() optionsLeadingIcon?: string;\r\n\r\n /** Emits the newly selected value whenever the user picks an option. */\r\n @Output() readonly valueChange = new EventEmitter<string>();\r\n /** Emitted when the overlay panel opens. */\r\n @Output() readonly opened = new EventEmitter<void>();\r\n /** Emitted when the overlay panel closes. */\r\n @Output() readonly closed = new EventEmitter<void>();\r\n\r\n protected readonly chevronSvg: SafeHtml;\r\n protected leadingIconSvg: SafeHtml | null = null;\r\n private readonly optionIconCache = new Map<string, SafeHtml>();\r\n\r\n protected isOpen = false;\r\n protected overlayWidth = 0;\r\n protected readonly overlayPositions: ConnectedPosition[] = [\r\n { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },\r\n { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },\r\n ];\r\n\r\n ngOnChanges(): void {\r\n this.leadingIconSvg = this.leadingIcon ? this.buildIconSvg(this.leadingIcon) : null;\r\n }\r\n\r\n private buildIconSvg(name: string): SafeHtml {\r\n const svg = (LUCIDE_ICONS[name] ?? '').replace('<svg ', '<svg width=\"20\" height=\"20\" ');\r\n return this.sanitizer.bypassSecurityTrustHtml(svg);\r\n }\r\n\r\n /**\r\n * Scales trusted raw SVG (e.g. a flag) to 20px wide, preserving aspect ratio via its\r\n * viewBox. Only the root `<svg>` tag's width/height are normalized — inner elements\r\n * (rects/paths that draw the icon) keep their own dimensions.\r\n */\r\n private buildRawIconSvg(svg: string): SafeHtml {\r\n const sized = svg.replace(/<svg\\b[^>]*>/, (openTag) =>\r\n openTag.replace(/\\s(?:width|height)=\"[^\"]*\"/g, '').replace('<svg', '<svg width=\"20\"')\r\n );\r\n return this.sanitizer.bypassSecurityTrustHtml(sized);\r\n }\r\n\r\n /**\r\n * Resolves an icon to cached SafeHtml: a trusted raw `iconSvg` wins, else a Lucide\r\n * `iconName`. Returns null when neither is set. Cached so rows keep a stable reference.\r\n */\r\n private resolveIcon(iconSvg: string | undefined, iconName: string | undefined): SafeHtml | null {\r\n if (iconSvg) {\r\n const key = 'raw:' + iconSvg;\r\n let svg = this.optionIconCache.get(key);\r\n if (!svg) {\r\n svg = this.buildRawIconSvg(iconSvg);\r\n this.optionIconCache.set(key, svg);\r\n }\r\n return svg;\r\n }\r\n if (iconName) {\r\n let svg = this.optionIconCache.get(iconName);\r\n if (!svg) {\r\n svg = this.buildIconSvg(iconName);\r\n this.optionIconCache.set(iconName, svg);\r\n }\r\n return svg;\r\n }\r\n return null;\r\n }\r\n\r\n /** Icon for an option row: its own `iconSvg`/`icon`, else the shared `optionsLeadingIcon`. */\r\n protected optionIcon(option: DropdownOption): SafeHtml | null {\r\n return this.resolveIcon(option.iconSvg, option.icon ?? this.optionsLeadingIcon);\r\n }\r\n\r\n /** Icon in the trigger: the selected option's own icon (flag/glyph), else the fixed `leadingIcon`. */\r\n get triggerIconSvg(): SafeHtml | null {\r\n return this.resolveIcon(this.selectedOption?.iconSvg, this.selectedOption?.icon) ?? this.leadingIconSvg;\r\n }\r\n\r\n get triggerId(): string {\r\n return this.id ?? this._autoId;\r\n }\r\n\r\n get labelId(): string {\r\n return this.triggerId + '-label';\r\n }\r\n\r\n get listboxId(): string {\r\n return this.triggerId + '-listbox';\r\n }\r\n\r\n get selectedOption(): DropdownOption | undefined {\r\n return this.options.find((o) => o.value === this.value);\r\n }\r\n\r\n /**\r\n * Resting text shown in the trigger: the selected option's label, else the\r\n * `label` in inline-label mode (so the label reads as the field's own text),\r\n * else the placeholder.\r\n */\r\n get triggerText(): string {\r\n return this.selectedOption?.label || (this.inlineLabel ? this.label : this.placeholder);\r\n }\r\n\r\n /** Value array bound to the CDK listbox (single-select, so 0 or 1 entry). */\r\n get listboxValue(): readonly string[] {\r\n return this.value ? [this.value] : [];\r\n }\r\n\r\n /** True when the bound FormControl is invalid and touched, or when `error` is explicitly set. */\r\n get hasError(): boolean {\r\n return this.error || !!(this.ngControl?.invalid && this.ngControl.touched);\r\n }\r\n\r\n /** True when `required` is set explicitly, or when the bound FormControl has Validators.required. */\r\n get isRequired(): boolean {\r\n return this.required || !!this.ngControl?.control?.hasValidator(Validators.required);\r\n }\r\n\r\n private get activeErrorMessage(): string {\r\n const errors = this.ngControl?.errors;\r\n if (!errors) return '';\r\n for (const key of Object.keys(errors)) {\r\n if (this.errorMessages[key]) return this.errorMessages[key];\r\n }\r\n return '';\r\n }\r\n\r\n get displayedSupportingText(): string {\r\n if (this.hasError) {\r\n return this.activeErrorMessage || this.errorMessage || this.supportingText;\r\n }\r\n return this.supportingText;\r\n }\r\n\r\n get describedBy(): string | null {\r\n const parts: string[] = [];\r\n if (this.ariaDescribedBy) parts.push(this.ariaDescribedBy);\r\n if (this.supportingText || this.errorMessage || Object.keys(this.errorMessages).length) {\r\n parts.push(this.triggerId + '-supporting');\r\n }\r\n return parts.length ? parts.join(' ') : null;\r\n }\r\n\r\n get wrapperClasses(): string {\r\n return ['cmp-dropdown', this.size, this.disabled ? 'disabled' : '', this.labelPosition === 'left' ? 'label-left' : '']\r\n .filter(Boolean)\r\n .join(' ');\r\n }\r\n\r\n // ── Open / close ──────────────────────────────────────────────────────────\r\n\r\n protected toggle(): void {\r\n if (this.isOpen) {\r\n this.close(true);\r\n } else {\r\n this.open();\r\n }\r\n }\r\n\r\n protected open(): void {\r\n if (this.disabled || this.isOpen) return;\r\n this.overlayWidth = this.triggerBtn?.nativeElement.offsetWidth ?? 0;\r\n this.isOpen = true;\r\n this.opened.emit();\r\n this.cdr.markForCheck();\r\n }\r\n\r\n protected close(refocus = false): void {\r\n if (!this.isOpen) return;\r\n this.isOpen = false;\r\n this._onTouched();\r\n this.closed.emit();\r\n if (refocus) {\r\n setTimeout(() => this.triggerBtn?.nativeElement.focus());\r\n }\r\n this.cdr.markForCheck();\r\n }\r\n\r\n protected onPanelAttach(): void {\r\n setTimeout(() => this.listboxEl?.nativeElement.focus());\r\n }\r\n\r\n // ── Keyboard ──────────────────────────────────────────────────────────────\r\n\r\n protected onTriggerKeydown(event: KeyboardEvent): void {\r\n if (this.disabled) return;\r\n if ((event.key === 'ArrowDown' || event.key === 'ArrowUp') && !this.isOpen) {\r\n event.preventDefault();\r\n this.open();\r\n } else if (event.key === 'Escape' && this.isOpen) {\r\n event.preventDefault();\r\n this.close(true);\r\n }\r\n }\r\n\r\n protected onListboxKeydown(event: KeyboardEvent): void {\r\n if (event.key === 'Escape') {\r\n event.preventDefault();\r\n this.close(true);\r\n } else if (event.key === 'Tab') {\r\n event.preventDefault();\r\n this.close(true);\r\n }\r\n }\r\n\r\n protected onListboxValueChange(event: ListboxValueChangeEvent<string>): void {\r\n const next = event.value[0] ?? '';\r\n this.value = next;\r\n this._onChange(next);\r\n this.valueChange.emit(next);\r\n this.close(true);\r\n }\r\n\r\n // ── ControlValueAccessor ────────────────────────────────────────────────────\r\n\r\n writeValue(value: string | null): void {\r\n this.value = value ?? '';\r\n this.cdr.markForCheck();\r\n }\r\n\r\n registerOnChange(fn: (value: string) => void): void {\r\n this._onChange = fn;\r\n }\r\n\r\n registerOnTouched(fn: () => void): void {\r\n this._onTouched = fn;\r\n }\r\n\r\n /** Called by Angular when the bound FormControl is programmatically disabled or enabled. */\r\n setDisabledState(isDisabled: boolean): void {\r\n this.disabled = isDisabled;\r\n this.cdr.markForCheck();\r\n }\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"dropdown.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/dropdown/dropdown.ts","../../../../../../packages/angular/src/lib/dropdown/dropdown.html"],"names":[],"mappings":"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","sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ElementRef,\r\n EventEmitter,\r\n Input,\r\n OnChanges,\r\n Output,\r\n ViewChild,\r\n booleanAttribute,\r\n inject,\r\n} from '@angular/core';\r\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\r\nimport { ControlValueAccessor, NgControl, Validators } from '@angular/forms';\r\nimport { CdkListbox, CdkOption, ListboxValueChangeEvent } from '@angular/cdk/listbox';\r\nimport { CdkConnectedOverlay, CdkOverlayOrigin, ConnectedPosition } from '@angular/cdk/overlay';\r\nimport { LUCIDE_ICONS } from '@compsych-ui-components/shared/assets/lucide-icons.generated';\r\nimport { DropdownLabelPosition, DropdownOption, DropdownSize } from './dropdown-types';\r\n\r\nlet nextId = 0;\r\n\r\n/**\r\n * A labelled single-select dropdown built on the CDK Listbox (`@angular/cdk/listbox`)\r\n * and CDK Overlay. The trigger renders the closed field (label, placeholder/selected\r\n * value and chevron); the options open in an overlay panel with full keyboard support\r\n * (arrow keys, Home/End, typeahead) provided by the CDK listbox.\r\n *\r\n * Implements `ControlValueAccessor` so it works with both reactive forms\r\n * (`formControlName`, `[formControl]`) and template-driven forms (`ngModel`). The error\r\n * visual state is driven automatically by the bound `FormControl`'s validity once the\r\n * field has been touched, and can also be set explicitly via the `error` input.\r\n */\r\n@Component({\r\n selector: 'compsych-dropdown',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n imports: [CdkListbox, CdkOption, CdkConnectedOverlay, CdkOverlayOrigin],\r\n templateUrl: './dropdown.html',\r\n styleUrl: './dropdown.css',\r\n})\r\nexport class DropdownComponent implements ControlValueAccessor, OnChanges {\r\n private readonly _autoId = `cmp-dropdown-${++nextId}`;\r\n private readonly cdr = inject(ChangeDetectorRef);\r\n private readonly sanitizer = inject(DomSanitizer);\r\n private _onChange: (value: string) => void = () => {};\r\n private _onTouched: () => void = () => {};\r\n\r\n /** Injected when the component is used inside a reactive or template-driven form. */\r\n protected readonly ngControl: NgControl | null;\r\n\r\n constructor() {\r\n const ctrl = inject(NgControl, { optional: true, self: true });\r\n if (ctrl) ctrl.valueAccessor = this;\r\n this.ngControl = ctrl;\r\n this.chevronSvg = this.buildIconSvg('chevron-down');\r\n }\r\n\r\n @ViewChild('triggerBtn') private triggerBtn?: ElementRef<HTMLButtonElement>;\r\n @ViewChild('listboxEl') private listboxEl?: ElementRef<HTMLUListElement>;\r\n\r\n /** Label displayed above the trigger. */\r\n @Input() label = '';\r\n /** Text shown in the trigger when no option is selected. */\r\n @Input() placeholder = '';\r\n /** Currently selected value. When used in a form, the form controls the value. */\r\n @Input() value = '';\r\n /** Selectable options rendered in the overlay panel. */\r\n @Input() options: DropdownOption[] = [];\r\n /** Size variant — controls height, padding, and font size. Defaults to `'md'`. */\r\n @Input() size: DropdownSize = 'md';\r\n /** Native `id` attribute for the trigger. Auto-generated if omitted. */\r\n @Input() id?: string;\r\n /**\r\n * Id(s) of external elements that describe the field, space-separated.\r\n * Forwarded to `aria-describedby` on the trigger.\r\n */\r\n @Input() ariaDescribedBy?: string;\r\n /** Text displayed below the trigger. Shown in red when in error state. */\r\n @Input() supportingText = '';\r\n /**\r\n * Static error text shown in red when in error state. Used when the dropdown is\r\n * outside a form (with `[error]=\"true\"`). When inside a form, prefer `errorMessages`.\r\n */\r\n @Input() errorMessage = '';\r\n /**\r\n * Map of Angular validator error keys to display strings. The component reads\r\n * `ngControl.errors` to determine which validator failed and shows the matching\r\n * message in red below the trigger. Example: `{ required: 'Please choose an option' }`\r\n */\r\n @Input() errorMessages: Record<string, string> = {};\r\n /**\r\n * Adds a blue asterisk after the label. When used in a form, this is set\r\n * automatically if the bound `FormControl` has `Validators.required`.\r\n * Supports boolean coercion.\r\n */\r\n @Input({ transform: booleanAttribute }) required = false;\r\n /**\r\n * Applies the error visual state (red border) explicitly. When used in a form,\r\n * the error state is driven automatically from the `FormControl`'s validity once\r\n * the field is touched — this input is only needed outside of forms. Supports boolean coercion.\r\n */\r\n @Input({ transform: booleanAttribute }) error = false;\r\n /** Disables the dropdown. When used in a form, also responds to `FormControl.disable()`. Supports boolean coercion. */\r\n @Input({ transform: booleanAttribute }) disabled = false;\r\n /** Visually hides the label while keeping it accessible to screen readers. Supports boolean coercion. */\r\n @Input({ transform: booleanAttribute }) hideLabel = false;\r\n /** Places the label above the trigger (`'top'`, default) or beside it (`'left'`). */\r\n @Input() labelPosition: DropdownLabelPosition = 'top';\r\n /**\r\n * Renders the label as the trigger's own resting text instead of a separate\r\n * label above/beside it — e.g. the trigger reads \"I want to\" until an option\r\n * is chosen. No visible external label is shown, but the `label` is still\r\n * exposed to assistive tech (a screen-reader-only `<label>` + `aria-labelledby`),\r\n * so the field is announced as a properly labelled combobox. Supports boolean coercion.\r\n */\r\n @Input({ transform: booleanAttribute }) inlineLabel = false;\r\n /** Lucide icon name rendered inside the trigger, before the displayed value/placeholder (e.g. `'globe'`). */\r\n @Input() leadingIcon?: string;\r\n /** Default Lucide icon shown before every option's label; each option's own `icon` (from the Icon Mapping) overrides it. */\r\n @Input() optionsLeadingIcon?: string;\r\n\r\n /** Emits the newly selected value whenever the user picks an option. */\r\n @Output() readonly valueChange = new EventEmitter<string>();\r\n /** Emitted when the overlay panel opens. */\r\n @Output() readonly opened = new EventEmitter<void>();\r\n /** Emitted when the overlay panel closes. */\r\n @Output() readonly closed = new EventEmitter<void>();\r\n\r\n protected readonly chevronSvg: SafeHtml;\r\n protected leadingIconSvg: SafeHtml | null = null;\r\n private readonly optionIconCache = new Map<string, SafeHtml>();\r\n\r\n protected isOpen = false;\r\n protected overlayWidth = 0;\r\n protected readonly overlayPositions: ConnectedPosition[] = [\r\n { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },\r\n { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },\r\n ];\r\n\r\n ngOnChanges(): void {\r\n this.leadingIconSvg = this.leadingIcon ? this.buildIconSvg(this.leadingIcon) : null;\r\n }\r\n\r\n private buildIconSvg(name: string): SafeHtml {\r\n const svg = (LUCIDE_ICONS[name] ?? '').replace('<svg ', '<svg width=\"20\" height=\"20\" ');\r\n return this.sanitizer.bypassSecurityTrustHtml(svg);\r\n }\r\n\r\n /**\r\n * Scales trusted raw SVG (e.g. a flag) to 20px wide, preserving aspect ratio via its\r\n * viewBox. Only the root `<svg>` tag's width/height are normalized — inner elements\r\n * (rects/paths that draw the icon) keep their own dimensions.\r\n */\r\n private buildRawIconSvg(svg: string): SafeHtml {\r\n const sized = svg.replace(/<svg\\b[^>]*>/, (openTag) =>\r\n openTag.replace(/\\s(?:width|height)=\"[^\"]*\"/g, '').replace('<svg', '<svg width=\"20\"')\r\n );\r\n return this.sanitizer.bypassSecurityTrustHtml(sized);\r\n }\r\n\r\n /**\r\n * Resolves an icon to cached SafeHtml: a trusted raw `iconSvg` wins, else a Lucide\r\n * `iconName`. Returns null when neither is set. Cached so rows keep a stable reference.\r\n */\r\n private resolveIcon(iconSvg: string | undefined, iconName: string | undefined): SafeHtml | null {\r\n if (iconSvg) {\r\n const key = 'raw:' + iconSvg;\r\n let svg = this.optionIconCache.get(key);\r\n if (!svg) {\r\n svg = this.buildRawIconSvg(iconSvg);\r\n this.optionIconCache.set(key, svg);\r\n }\r\n return svg;\r\n }\r\n if (iconName) {\r\n let svg = this.optionIconCache.get(iconName);\r\n if (!svg) {\r\n svg = this.buildIconSvg(iconName);\r\n this.optionIconCache.set(iconName, svg);\r\n }\r\n return svg;\r\n }\r\n return null;\r\n }\r\n\r\n /** Icon for an option row: its own `iconSvg`/`icon`, else the shared `optionsLeadingIcon`. */\r\n protected optionIcon(option: DropdownOption): SafeHtml | null {\r\n return this.resolveIcon(option.iconSvg, option.icon ?? this.optionsLeadingIcon);\r\n }\r\n\r\n /** Icon in the trigger: the selected option's own icon (flag/glyph), else the fixed `leadingIcon`. */\r\n get triggerIconSvg(): SafeHtml | null {\r\n return this.resolveIcon(this.selectedOption?.iconSvg, this.selectedOption?.icon) ?? this.leadingIconSvg;\r\n }\r\n\r\n get triggerId(): string {\r\n return this.id ?? this._autoId;\r\n }\r\n\r\n get labelId(): string {\r\n return this.triggerId + '-label';\r\n }\r\n\r\n get listboxId(): string {\r\n return this.triggerId + '-listbox';\r\n }\r\n\r\n get selectedOption(): DropdownOption | undefined {\r\n return this.options.find((o) => o.value === this.value);\r\n }\r\n\r\n /**\r\n * Resting text shown in the trigger: the selected option's label, else the\r\n * `label` in inline-label mode (so the label reads as the field's own text),\r\n * else the placeholder.\r\n */\r\n get triggerText(): string {\r\n return this.selectedOption?.label || (this.inlineLabel ? this.label : this.placeholder);\r\n }\r\n\r\n /** Value array bound to the CDK listbox (single-select, so 0 or 1 entry). */\r\n get listboxValue(): readonly string[] {\r\n if (!this.value || !this.options.some((o) => o.value === this.value)) return [];\r\n return [this.value];\r\n }\r\n\r\n /** True when the bound FormControl is invalid and touched, or when `error` is explicitly set. */\r\n get hasError(): boolean {\r\n return this.error || !!(this.ngControl?.invalid && this.ngControl.touched);\r\n }\r\n\r\n /** True when `required` is set explicitly, or when the bound FormControl has Validators.required. */\r\n get isRequired(): boolean {\r\n return this.required || !!this.ngControl?.control?.hasValidator(Validators.required);\r\n }\r\n\r\n private get activeErrorMessage(): string {\r\n const errors = this.ngControl?.errors;\r\n if (!errors) return '';\r\n for (const key of Object.keys(errors)) {\r\n if (this.errorMessages[key]) return this.errorMessages[key];\r\n }\r\n return '';\r\n }\r\n\r\n get displayedSupportingText(): string {\r\n if (this.hasError) {\r\n return this.activeErrorMessage || this.errorMessage || this.supportingText;\r\n }\r\n return this.supportingText;\r\n }\r\n\r\n get describedBy(): string | null {\r\n const parts: string[] = [];\r\n if (this.ariaDescribedBy) parts.push(this.ariaDescribedBy);\r\n if (this.supportingText || this.errorMessage || Object.keys(this.errorMessages).length) {\r\n parts.push(this.triggerId + '-supporting');\r\n }\r\n return parts.length ? parts.join(' ') : null;\r\n }\r\n\r\n get wrapperClasses(): string {\r\n return ['cmp-dropdown', this.size, this.disabled ? 'disabled' : '', this.labelPosition === 'left' ? 'label-left' : '']\r\n .filter(Boolean)\r\n .join(' ');\r\n }\r\n\r\n // ── Open / close ──────────────────────────────────────────────────────────\r\n\r\n protected toggle(): void {\r\n if (this.isOpen) {\r\n this.close(true);\r\n } else {\r\n this.open();\r\n }\r\n }\r\n\r\n protected open(): void {\r\n if (this.disabled || this.isOpen) return;\r\n this.overlayWidth = this.triggerBtn?.nativeElement.offsetWidth ?? 0;\r\n this.isOpen = true;\r\n this.opened.emit();\r\n this.cdr.markForCheck();\r\n }\r\n\r\n protected close(refocus = false): void {\r\n if (!this.isOpen) return;\r\n this.isOpen = false;\r\n this._onTouched();\r\n this.closed.emit();\r\n if (refocus) {\r\n setTimeout(() => this.triggerBtn?.nativeElement.focus());\r\n }\r\n this.cdr.markForCheck();\r\n }\r\n\r\n protected onPanelAttach(): void {\r\n setTimeout(() => this.listboxEl?.nativeElement.focus());\r\n }\r\n\r\n // ── Keyboard ──────────────────────────────────────────────────────────────\r\n\r\n protected onTriggerKeydown(event: KeyboardEvent): void {\r\n if (this.disabled) return;\r\n if ((event.key === 'ArrowDown' || event.key === 'ArrowUp') && !this.isOpen) {\r\n event.preventDefault();\r\n this.open();\r\n } else if (event.key === 'Escape' && this.isOpen) {\r\n event.preventDefault();\r\n this.close(true);\r\n }\r\n }\r\n\r\n protected onListboxKeydown(event: KeyboardEvent): void {\r\n if (event.key === 'Escape') {\r\n event.preventDefault();\r\n this.close(true);\r\n } else if (event.key === 'Tab') {\r\n event.preventDefault();\r\n this.close(true);\r\n }\r\n }\r\n\r\n protected onListboxValueChange(event: ListboxValueChangeEvent<string>): void {\r\n const next = event.value[0] ?? '';\r\n this.value = next;\r\n this._onChange(next);\r\n this.valueChange.emit(next);\r\n this.close(true);\r\n }\r\n\r\n // ── ControlValueAccessor ────────────────────────────────────────────────────\r\n\r\n writeValue(value: string | null): void {\r\n this.value = value ?? '';\r\n this.cdr.markForCheck();\r\n }\r\n\r\n registerOnChange(fn: (value: string) => void): void {\r\n this._onChange = fn;\r\n }\r\n\r\n registerOnTouched(fn: () => void): void {\r\n this._onTouched = fn;\r\n }\r\n\r\n /** Called by Angular when the bound FormControl is programmatically disabled or enabled. */\r\n setDisabledState(isDisabled: boolean): void {\r\n this.disabled = isDisabled;\r\n this.cdr.markForCheck();\r\n }\r\n\r\n}\r\n","<div [class]=\"wrapperClasses\">\n <div class=\"label-row\" [class.sr-only]=\"hideLabel || inlineLabel\">\n <label class=\"label\" [id]=\"labelId\" [for]=\"triggerId\">\n {{ label }}\n @if (isRequired) {\n <span class=\"required-marker\" aria-hidden=\"true\"> *</span>\n }\n </label>\n </div>\n\n <button\n #triggerBtn\n cdkOverlayOrigin\n #origin=\"cdkOverlayOrigin\"\n type=\"button\"\n class=\"trigger\"\n [id]=\"triggerId\"\n [class.placeholder]=\"!selectedOption && !inlineLabel\"\n [class.error]=\"hasError\"\n [class.open]=\"isOpen\"\n [disabled]=\"disabled\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-labelledby]=\"labelId + ' ' + triggerId\"\n [attr.aria-invalid]=\"hasError ? 'true' : null\"\n [attr.aria-required]=\"isRequired ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy\"\n (click)=\"toggle()\"\n (keydown)=\"onTriggerKeydown($event)\"\n >\n @if (triggerIconSvg) {\n <span class=\"trigger-icon\" aria-hidden=\"true\" [innerHTML]=\"triggerIconSvg\"></span>\n }\n <span class=\"trigger-text\">{{ triggerText }}</span>\n <span class=\"trigger-chevron\" aria-hidden=\"true\" [innerHTML]=\"chevronSvg\"></span>\n </button>\n\n @if (displayedSupportingText) {\n <span class=\"supporting-text\" [class.supporting-text--error]=\"hasError\" [id]=\"triggerId + '-supporting'\">\n {{ displayedSupportingText }}\n </span>\n }\n</div>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"origin\"\n [cdkConnectedOverlayOpen]=\"isOpen\"\n [cdkConnectedOverlayWidth]=\"overlayWidth\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayViewportMargin]=\"8\"\n (overlayOutsideClick)=\"close()\"\n (attach)=\"onPanelAttach()\"\n (detach)=\"close()\"\n>\n @if (isOpen) {\n <ul\n #listboxEl\n cdkListbox\n class=\"panel\"\n [class.sm]=\"size === 'sm'\"\n [style.width.px]=\"overlayWidth\"\n [id]=\"listboxId\"\n [attr.aria-labelledby]=\"labelId\"\n [cdkListboxValue]=\"listboxValue\"\n (cdkListboxValueChange)=\"onListboxValueChange($event)\"\n (keydown)=\"onListboxKeydown($event)\"\n >\n @for (opt of options; track opt.value) {\n <li class=\"option\" [class.error]=\"opt.error\" [cdkOption]=\"opt.value\" [cdkOptionDisabled]=\"!!opt.disabled\">\n <span class=\"option-inner\">\n @if (opt.iconSvg || opt.icon || optionsLeadingIcon) {\n <span class=\"option-icon\" aria-hidden=\"true\" [innerHTML]=\"optionIcon(opt)\"></span>\n }\n <span class=\"option-text\">\n <span class=\"option-label\">{{ opt.label }}</span>\n @if (opt.supportingText) {\n <span class=\"option-support\">{{ opt.supportingText }}</span>\n }\n </span>\n </span>\n </li>\n }\n </ul>\n }\n</ng-template>\n"]}
|
package/index.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
2
|
import * as i1 from "./lib/button/button";
|
|
3
|
-
import * as i2 from "./lib/
|
|
4
|
-
import * as i3 from "./lib/
|
|
5
|
-
import * as i4 from "./lib/
|
|
6
|
-
import * as i5 from "./lib/
|
|
7
|
-
import * as i6 from "./lib/
|
|
8
|
-
import * as i7 from "./lib/
|
|
9
|
-
import * as i8 from "./lib/
|
|
10
|
-
import * as i9 from "./lib/
|
|
11
|
-
import * as i10 from "./lib/dropdown/dropdown
|
|
3
|
+
import * as i2 from "./lib/chip/chip";
|
|
4
|
+
import * as i3 from "./lib/accordion/accordion";
|
|
5
|
+
import * as i4 from "./lib/footer/footer";
|
|
6
|
+
import * as i5 from "./lib/header-nav/header-nav";
|
|
7
|
+
import * as i6 from "./lib/sidebar/sidebar";
|
|
8
|
+
import * as i7 from "./lib/card-base/card";
|
|
9
|
+
import * as i8 from "./lib/text-input/text-input";
|
|
10
|
+
import * as i9 from "./lib/dialog/dialog";
|
|
11
|
+
import * as i10 from "./lib/dropdown/dropdown";
|
|
12
|
+
import * as i11 from "./lib/dropdown/dropdown-menu";
|
|
12
13
|
export { ButtonComponent } from './lib/button/button';
|
|
13
14
|
export type { ButtonVariant, ButtonSize, ButtonType } from './lib/button/button';
|
|
14
15
|
export { AccordionComponent } from './lib/accordion/accordion';
|
|
@@ -19,6 +20,8 @@ export { HeaderNavComponent } from './lib/header-nav/header-nav';
|
|
|
19
20
|
export type { HeaderNavLevel, NavAction } from './lib/header-nav/header-nav';
|
|
20
21
|
export { SidebarComponent } from './lib/sidebar/sidebar';
|
|
21
22
|
export type { SidebarItem } from './lib/sidebar/sidebar';
|
|
23
|
+
export { ChipComponent } from './lib/chip/chip';
|
|
24
|
+
export type { ChipVariant, ChipUsage, ChipSize } from './lib/chip/chip';
|
|
22
25
|
export { CardComponent } from './lib/card-base/card';
|
|
23
26
|
export type { CardVariant, CardAppearance, CardRadius, CardPadding, CardOrientation, CardSize, CardMetricSize, CardHeadingLevel, CardCategoryStyle, CardIconStyle, BannerMetric } from './lib/card-base/card';
|
|
24
27
|
export { RippleDirective } from './lib/card-base/ripple.directive';
|
|
@@ -36,6 +39,6 @@ export { DropdownMenuComponent } from './lib/dropdown/dropdown-menu';
|
|
|
36
39
|
export type { DropdownSize, DropdownOption, DropdownLabelPosition, DropdownMenuKind, DropdownMenuItem, } from './lib/dropdown/dropdown-types';
|
|
37
40
|
export declare class CompsychAngularModule {
|
|
38
41
|
static ɵfac: i0.ɵɵFactoryDeclaration<CompsychAngularModule, never>;
|
|
39
|
-
static ɵmod: i0.ɵɵNgModuleDeclaration<CompsychAngularModule, never, [typeof i1.ButtonComponent, typeof i2.
|
|
42
|
+
static ɵmod: i0.ɵɵNgModuleDeclaration<CompsychAngularModule, never, [typeof i1.ButtonComponent, typeof i2.ChipComponent, typeof i3.AccordionComponent, typeof i4.FooterComponent, typeof i5.HeaderNavComponent, typeof i6.SidebarComponent, typeof i7.CardComponent, typeof i8.TextInputComponent, typeof i9.DialogComponent, typeof i10.DropdownComponent, typeof i11.DropdownMenuComponent], [typeof i1.ButtonComponent, typeof i2.ChipComponent, typeof i3.AccordionComponent, typeof i4.FooterComponent, typeof i5.HeaderNavComponent, typeof i6.SidebarComponent, typeof i7.CardComponent, typeof i8.TextInputComponent, typeof i9.DialogComponent, typeof i10.DropdownComponent, typeof i11.DropdownMenuComponent]>;
|
|
40
43
|
static ɵinj: i0.ɵɵInjectorDeclaration<CompsychAngularModule>;
|
|
41
44
|
}
|
package/lib/card-base/card.d.ts
CHANGED
|
@@ -142,7 +142,7 @@ export declare class CardComponent {
|
|
|
142
142
|
readonly trailingIcon: import("@angular/core").InputSignal<string>;
|
|
143
143
|
/** Action: layout orientation. */
|
|
144
144
|
readonly orientation: import("@angular/core").InputSignal<CardOrientation>;
|
|
145
|
-
/** Action: stretch the card to 100% of its container width. */
|
|
145
|
+
/** Action/basic: stretch the card to 100% of its container width. */
|
|
146
146
|
readonly fullWidth: import("@angular/core").InputSignal<boolean>;
|
|
147
147
|
/** Metric: size variant. Defaults to `medium`. */
|
|
148
148
|
readonly metricSize: import("@angular/core").InputSignal<CardMetricSize>;
|
|
@@ -179,9 +179,9 @@ export declare class CardComponent {
|
|
|
179
179
|
*/
|
|
180
180
|
readonly effectiveHeadingLevel: import("@angular/core").Signal<CardHeadingLevel | undefined>;
|
|
181
181
|
/** Base Card Item plain-icon glyph size by `size` (48 md, 32 sm, 24 xs). */
|
|
182
|
-
readonly basicIconSize: import("@angular/core").Signal<
|
|
182
|
+
readonly basicIconSize: import("@angular/core").Signal<32 | 24 | 48>;
|
|
183
183
|
/** Base Card Item arrow glyph size by `size` (24 md, 20 sm/xs). */
|
|
184
|
-
readonly basicArrowSize: import("@angular/core").Signal<
|
|
184
|
+
readonly basicArrowSize: import("@angular/core").Signal<24 | 20>;
|
|
185
185
|
/** CSS `url(...)` for the media layer, or null when no image is set. */
|
|
186
186
|
readonly mediaUrl: import("@angular/core").Signal<string | null>;
|
|
187
187
|
/** Padding resolved per variant; `basic` honors the `padding` input. */
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { SafeHtml } from '@angular/platform-browser';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
import * as i1 from "../card-base/ripple.directive";
|
|
4
|
+
/** Figma "Style". */
|
|
5
|
+
export type ChipVariant = 'tonal' | 'outlined' | 'filled';
|
|
6
|
+
/** Figma "Usage". `inverse` pairs with the tonal style only. */
|
|
7
|
+
export type ChipUsage = 'neutral' | 'informative' | 'positive' | 'danger' | 'warning' | 'inverse';
|
|
8
|
+
/** Chip dimensions — 20 / 24 / 32 / 40 tall. */
|
|
9
|
+
export type ChipSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
10
|
+
/**
|
|
11
|
+
* Chip — Figma "Chip" component set (Core Components › Molecules, node 14:942).
|
|
12
|
+
*
|
|
13
|
+
* Three axes: Style (Tonal / Outlined / Filled) × Usage (Neutral / Informative /
|
|
14
|
+
* Positive / Danger / Warning, plus Inverse on Tonal only) × Size (sm / md / lg /
|
|
15
|
+
* xl = 20 / 24 / 32 / 40). Booleans: leading icon, dismissible, badge — all off by
|
|
16
|
+
* default, as in Figma.
|
|
17
|
+
*
|
|
18
|
+
* Chips are INTERACTIVE BY DEFAULT (ruled 2026-09-02, Jean — the Material 3
|
|
19
|
+
* convention: every chip is something you press; a read-only status marker is a
|
|
20
|
+
* Badge). The icon + label render inside a real `<button>` whose hit area is the
|
|
21
|
+
* whole pill; `(click)` on the host is the chip's action. The interaction recipe is
|
|
22
|
+
* Button's (ruled 2026-08-24): the chip's own foreground as an 8% state layer,
|
|
23
|
+
* 120ms hover-in / 240ms release, the level-2 elevation lift, the shared press
|
|
24
|
+
* ripple, and the Focus Ring drawn around the pill. The Figma set authors no State
|
|
25
|
+
* variants — recorded as a reconciliation; the recipe follows them the day they
|
|
26
|
+
* exist. `[interactive]="false"` opts a chip out (a display-only tag inside another
|
|
27
|
+
* control) — then the host is a plain pill with no role.
|
|
28
|
+
*
|
|
29
|
+
* The dismiss affordance is a SIBLING button, never nested in the primary one
|
|
30
|
+
* (nested interactive controls are invalid); it emits `dismiss` and stops its click
|
|
31
|
+
* at the host so the chip's action does not fire. It is ALWAYS the outermost
|
|
32
|
+
* element — icon, label, badge, then the x (ruled 2026-09-02, Jean; the Figma file
|
|
33
|
+
* orders the badge after the Close Button). With a trailing control the pill's end
|
|
34
|
+
* padding tightens so the control sits concentric with the pill's end, and a
|
|
35
|
+
* leading icon mirrors that inset at the start (ruled 2026-09-02; the file authors
|
|
36
|
+
* symmetric padding).
|
|
37
|
+
*
|
|
38
|
+
* Colour recipe (usage → sys tokens; neutral maps to the surface family):
|
|
39
|
+
* tonal bg `<usage>`-container fg on-`<usage>`-container
|
|
40
|
+
* filled bg `<usage>`, white content (neutral: plain white pill — Figma
|
|
41
|
+
* authors a level-2 elevation on it; ruled 2026-08-24 that chips carry
|
|
42
|
+
* no resting shadow, Figma queued to update)
|
|
43
|
+
* outlined border `<usage>`-container, label `<usage>`, icon = label (neutral:
|
|
44
|
+
* outline / on-surface). EXCEPT warning: label and icon take
|
|
45
|
+
* on-warning-container — an accessibility exception ruled 2026-09-02
|
|
46
|
+
* (sys-warning on white is 3.09:1).
|
|
47
|
+
* inverse transparent-white-20 + backdrop blur, white content — for placement
|
|
48
|
+
* on photos. Tonal only, per the Figma set.
|
|
49
|
+
*
|
|
50
|
+
* Figma reconciliations this implementation carries (flagged to design, majority
|
|
51
|
+
* pattern followed instead):
|
|
52
|
+
* - Outlined/Informative binds primary-family colours in the file where its three
|
|
53
|
+
* status siblings all use their own family. Implemented info-family.
|
|
54
|
+
* - The Warning badge count binds on-surface in the file where every other usage
|
|
55
|
+
* uses the usage foreground. Implemented as the usage foreground.
|
|
56
|
+
* - The counter inverts on filled-status and inverse chips for AA (see chip.css).
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```html
|
|
60
|
+
* <compsych-chip label="Active" variant="tonal" usage="positive" size="md" />
|
|
61
|
+
* <compsych-chip label="Filter" leadingIcon="tag" [dismissible]="true"
|
|
62
|
+
* (dismiss)="remove()" (click)="apply()" />
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare class ChipComponent {
|
|
66
|
+
/** Figma "Style". Defaults to Tonal, as the set does. */
|
|
67
|
+
readonly variant: import("@angular/core").InputSignal<ChipVariant>;
|
|
68
|
+
/** Figma "Usage". `inverse` pairs with the tonal style only. */
|
|
69
|
+
readonly usage: import("@angular/core").InputSignal<ChipUsage>;
|
|
70
|
+
readonly size: import("@angular/core").InputSignal<ChipSize>;
|
|
71
|
+
readonly label: import("@angular/core").InputSignal<string>;
|
|
72
|
+
/** Lucide icon name for the leading slot (Figma "Leading Icon" + swap). */
|
|
73
|
+
readonly leadingIcon: import("@angular/core").InputSignal<string | undefined>;
|
|
74
|
+
/** Shows the dismiss affordance (Figma "Dismissible"). */
|
|
75
|
+
readonly dismissible: import("@angular/core").InputSignal<boolean>;
|
|
76
|
+
/** Badge count (Figma "Badge"). Undefined = no badge. */
|
|
77
|
+
readonly badge: import("@angular/core").InputSignal<string | number | undefined>;
|
|
78
|
+
/** Accessible name for the dismiss button. */
|
|
79
|
+
readonly dismissLabel: import("@angular/core").InputSignal<string>;
|
|
80
|
+
/**
|
|
81
|
+
* Chips are interactive by default (ruled 2026-09-02): icon + label sit in a real
|
|
82
|
+
* button and `(click)` on the host is the action. `false` renders a display-only
|
|
83
|
+
* pill — no role, no state layer, no ripple.
|
|
84
|
+
*/
|
|
85
|
+
readonly interactive: import("@angular/core").InputSignal<boolean>;
|
|
86
|
+
/** Disables the primary button (interactive chips only). */
|
|
87
|
+
readonly disabled: import("@angular/core").InputSignal<boolean>;
|
|
88
|
+
/** Emitted by the dismiss button. The host app removes the chip. */
|
|
89
|
+
readonly dismiss: import("@angular/core").OutputEmitterRef<void>;
|
|
90
|
+
private readonly sanitizer;
|
|
91
|
+
private readonly ripple;
|
|
92
|
+
private static nextId;
|
|
93
|
+
/** Links the primary button to its counter (aria-describedby). */
|
|
94
|
+
protected readonly badgeId: string;
|
|
95
|
+
constructor();
|
|
96
|
+
/** Leading icon px per size (Figma: 16 at sm/md, 20 at lg/xl). */
|
|
97
|
+
readonly iconPx: import("@angular/core").Signal<20 | 16>;
|
|
98
|
+
/** Dismiss "x" px per size (Figma close slot: 12 at sm/md, 16 at lg/xl). */
|
|
99
|
+
readonly xPx: import("@angular/core").Signal<16 | 12>;
|
|
100
|
+
protected readonly leadingIconSvg: import("@angular/core").Signal<SafeHtml | null>;
|
|
101
|
+
protected readonly dismissIconSvg: import("@angular/core").Signal<SafeHtml>;
|
|
102
|
+
/** Renders a sized Lucide glyph at the DS absolute stroke weight for that size. */
|
|
103
|
+
private iconSvg;
|
|
104
|
+
/** Dismiss is its own action — it must not also fire the chip's. */
|
|
105
|
+
protected onDismiss(event: Event): void;
|
|
106
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ChipComponent, never>;
|
|
107
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ChipComponent, "compsych-chip", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "usage": { "alias": "usage"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": true; "isSignal": true; }; "leadingIcon": { "alias": "leadingIcon"; "required": false; "isSignal": true; }; "dismissible": { "alias": "dismissible"; "required": false; "isSignal": true; }; "badge": { "alias": "badge"; "required": false; "isSignal": true; }; "dismissLabel": { "alias": "dismissLabel"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "dismiss": "dismiss"; }, never, never, true, [{ directive: typeof i1.RippleDirective; inputs: {}; outputs: {}; }]>;
|
|
108
|
+
}
|