@ni/nimble-components 11.0.2 → 11.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/all-components-bundle.js +2375 -1488
  2. package/dist/all-components-bundle.js.map +1 -1
  3. package/dist/all-components-bundle.min.js +1476 -1234
  4. package/dist/all-components-bundle.min.js.map +1 -1
  5. package/dist/esm/all-components.d.ts +1 -0
  6. package/dist/esm/all-components.js +1 -0
  7. package/dist/esm/all-components.js.map +1 -1
  8. package/dist/esm/combobox/index.d.ts +41 -0
  9. package/dist/esm/combobox/index.js +136 -0
  10. package/dist/esm/combobox/index.js.map +1 -0
  11. package/dist/esm/combobox/styles.d.ts +1 -0
  12. package/dist/esm/combobox/styles.js +87 -0
  13. package/dist/esm/combobox/styles.js.map +1 -0
  14. package/dist/esm/combobox/types.d.ts +2 -0
  15. package/dist/esm/combobox/types.js +3 -0
  16. package/dist/esm/combobox/types.js.map +1 -0
  17. package/dist/esm/menu-button/template.js +1 -1
  18. package/dist/esm/menu-button/template.js.map +1 -1
  19. package/dist/esm/number-field/styles.js +1 -0
  20. package/dist/esm/number-field/styles.js.map +1 -1
  21. package/dist/esm/patterns/dropdown/styles.d.ts +1 -0
  22. package/dist/esm/patterns/dropdown/styles.js +193 -0
  23. package/dist/esm/patterns/dropdown/styles.js.map +1 -0
  24. package/dist/esm/patterns/dropdown/types.d.ts +5 -0
  25. package/dist/esm/patterns/dropdown/types.js +6 -0
  26. package/dist/esm/patterns/dropdown/types.js.map +1 -0
  27. package/dist/esm/patterns/error/styles.d.ts +1 -0
  28. package/dist/esm/patterns/error/styles.js +40 -0
  29. package/dist/esm/patterns/error/styles.js.map +1 -0
  30. package/dist/esm/patterns/error/template.d.ts +2 -0
  31. package/dist/esm/patterns/error/template.js +7 -0
  32. package/dist/esm/patterns/error/template.js.map +1 -0
  33. package/dist/esm/patterns/error/types.d.ts +9 -0
  34. package/dist/esm/patterns/error/types.js +2 -0
  35. package/dist/esm/patterns/error/types.js.map +1 -0
  36. package/dist/esm/select/styles.js +2 -140
  37. package/dist/esm/select/styles.js.map +1 -1
  38. package/dist/esm/text-field/index.d.ts +4 -2
  39. package/dist/esm/text-field/index.js +6 -10
  40. package/dist/esm/text-field/index.js.map +1 -1
  41. package/dist/esm/text-field/styles.js +8 -55
  42. package/dist/esm/text-field/styles.js.map +1 -1
  43. package/package.json +1 -1
@@ -7502,833 +7502,1466 @@
7502
7502
  below: "below",
7503
7503
  };
7504
7504
 
7505
+ class _Combobox extends Listbox {
7506
+ }
7505
7507
  /**
7506
- * Retrieves the "composed parent" element of a node, ignoring DOM tree boundaries.
7507
- * When the parent of a node is a shadow-root, it will return the host
7508
- * element of the shadow root. Otherwise it will return the parent node or null if
7509
- * no parent node exists.
7510
- * @param element - The element for which to retrieve the composed parent
7508
+ * A form-associated base class for the {@link (Combobox:class)} component.
7511
7509
  *
7512
- * @public
7510
+ * @internal
7513
7511
  */
7514
- function composedParent(element) {
7515
- const parentNode = element.parentElement;
7516
- if (parentNode) {
7517
- return parentNode;
7518
- }
7519
- else {
7520
- const rootNode = element.getRootNode();
7521
- if (rootNode.host instanceof HTMLElement) {
7522
- // this is shadow-root
7523
- return rootNode.host;
7524
- }
7512
+ class FormAssociatedCombobox extends FormAssociated(_Combobox) {
7513
+ constructor() {
7514
+ super(...arguments);
7515
+ this.proxy = document.createElement("input");
7525
7516
  }
7526
- return null;
7527
7517
  }
7528
7518
 
7529
7519
  /**
7530
- * Determines if the reference element contains the test element in a "composed" DOM tree that
7531
- * ignores shadow DOM boundaries.
7520
+ * Autocomplete values for combobox.
7521
+ * @public
7522
+ */
7523
+ const ComboboxAutocomplete = {
7524
+ inline: "inline",
7525
+ list: "list",
7526
+ both: "both",
7527
+ none: "none",
7528
+ };
7529
+
7530
+ /**
7531
+ * A Combobox Custom HTML Element.
7532
+ * Implements the {@link https://w3c.github.io/aria-practices/#combobox | ARIA combobox }.
7532
7533
  *
7533
- * Returns true of the test element is a descendent of the reference, or exist in
7534
- * a shadow DOM that is a logical descendent of the reference. Otherwise returns false.
7535
- * @param reference - The element to test for containment against.
7536
- * @param test - The element being tested for containment.
7534
+ * @slot start - Content which can be provided before the input
7535
+ * @slot end - Content which can be provided after the input
7536
+ * @slot control - Used to replace the input element representing the combobox
7537
+ * @slot indicator - The visual indicator representing the expanded state
7538
+ * @slot - The default slot for the options
7539
+ * @csspart control - The wrapper element containing the input area, including start and end
7540
+ * @csspart selected-value - The input element representing the selected value
7541
+ * @csspart indicator - The element wrapping the indicator slot
7542
+ * @csspart listbox - The wrapper for the listbox slotted options
7543
+ * @fires change - Fires a custom 'change' event when the value updates
7537
7544
  *
7538
7545
  * @public
7539
7546
  */
7540
- function composedContains(reference, test) {
7541
- let current = test;
7542
- while (current !== null) {
7543
- if (current === reference) {
7544
- return true;
7545
- }
7546
- current = composedParent(current);
7547
+ class Combobox$1 extends FormAssociatedCombobox {
7548
+ constructor() {
7549
+ super(...arguments);
7550
+ /**
7551
+ * The internal value property.
7552
+ *
7553
+ * @internal
7554
+ */
7555
+ this._value = "";
7556
+ /**
7557
+ * The collection of currently filtered options.
7558
+ *
7559
+ * @public
7560
+ */
7561
+ this.filteredOptions = [];
7562
+ /**
7563
+ * The current filter value.
7564
+ *
7565
+ * @internal
7566
+ */
7567
+ this.filter = "";
7568
+ /**
7569
+ * The initial state of the position attribute.
7570
+ *
7571
+ * @internal
7572
+ */
7573
+ this.forcedPosition = false;
7574
+ /**
7575
+ * The unique id for the internal listbox element.
7576
+ *
7577
+ * @internal
7578
+ */
7579
+ this.listboxId = uniqueId("listbox-");
7580
+ /**
7581
+ * The max height for the listbox when opened.
7582
+ *
7583
+ * @internal
7584
+ */
7585
+ this.maxHeight = 0;
7586
+ /**
7587
+ * The open attribute.
7588
+ *
7589
+ * @public
7590
+ * @remarks
7591
+ * HTML Attribute: open
7592
+ */
7593
+ this.open = false;
7547
7594
  }
7548
- return false;
7549
- }
7550
-
7551
- const defaultElement = document.createElement("div");
7552
- function isFastElement(element) {
7553
- return element instanceof FASTElement;
7554
- }
7555
- class QueuedStyleSheetTarget {
7556
- setProperty(name, value) {
7557
- DOM.queueUpdate(() => this.target.setProperty(name, value));
7595
+ /**
7596
+ * Reset the element to its first selectable option when its parent form is reset.
7597
+ *
7598
+ * @internal
7599
+ */
7600
+ formResetCallback() {
7601
+ super.formResetCallback();
7602
+ this.setDefaultSelectedOption();
7603
+ this.updateValue();
7558
7604
  }
7559
- removeProperty(name) {
7560
- DOM.queueUpdate(() => this.target.removeProperty(name));
7605
+ get isAutocompleteInline() {
7606
+ return (this.autocomplete === ComboboxAutocomplete.inline || this.isAutocompleteBoth);
7561
7607
  }
7562
- }
7563
- /**
7564
- * Handles setting properties for a FASTElement using Constructable Stylesheets
7565
- */
7566
- class ConstructableStyleSheetTarget extends QueuedStyleSheetTarget {
7567
- constructor(source) {
7568
- super();
7569
- const sheet = new CSSStyleSheet();
7570
- this.target = sheet.cssRules[sheet.insertRule(":host{}")].style;
7571
- source.$fastController.addStyles(ElementStyles.create([sheet]));
7608
+ get isAutocompleteList() {
7609
+ return this.autocomplete === ComboboxAutocomplete.list || this.isAutocompleteBoth;
7572
7610
  }
7573
- }
7574
- class DocumentStyleSheetTarget extends QueuedStyleSheetTarget {
7575
- constructor() {
7576
- super();
7577
- const sheet = new CSSStyleSheet();
7578
- this.target = sheet.cssRules[sheet.insertRule(":root{}")].style;
7579
- document.adoptedStyleSheets = [
7580
- ...document.adoptedStyleSheets,
7581
- sheet,
7582
- ];
7611
+ get isAutocompleteBoth() {
7612
+ return this.autocomplete === ComboboxAutocomplete.both;
7583
7613
  }
7584
- }
7585
- class HeadStyleElementStyleSheetTarget extends QueuedStyleSheetTarget {
7586
- constructor() {
7587
- super();
7588
- this.style = document.createElement("style");
7589
- document.head.appendChild(this.style);
7590
- const { sheet } = this.style;
7591
- // Because the HTMLStyleElement has been appended,
7592
- // there shouldn't exist a case where `sheet` is null,
7593
- // but if-check it just in case.
7594
- if (sheet) {
7595
- // https://github.com/jsdom/jsdom uses https://github.com/NV/CSSOM for it's CSSOM implementation,
7596
- // which implements the DOM Level 2 spec for CSSStyleSheet where insertRule() requires an index argument.
7597
- const index = sheet.insertRule(":root{}", sheet.cssRules.length);
7598
- this.target = sheet.cssRules[index].style;
7614
+ /**
7615
+ * Sets focus and synchronize ARIA attributes when the open property changes.
7616
+ *
7617
+ * @param prev - the previous open value
7618
+ * @param next - the current open value
7619
+ *
7620
+ * @internal
7621
+ */
7622
+ openChanged() {
7623
+ if (this.open) {
7624
+ this.ariaControls = this.listboxId;
7625
+ this.ariaExpanded = "true";
7626
+ this.setPositioning();
7627
+ this.focusAndScrollOptionIntoView();
7628
+ // focus is directed to the element when `open` is changed programmatically
7629
+ DOM.queueUpdate(() => this.focus());
7630
+ return;
7599
7631
  }
7632
+ this.ariaControls = "";
7633
+ this.ariaExpanded = "false";
7600
7634
  }
7601
- }
7602
- /**
7603
- * Handles setting properties for a FASTElement using an HTMLStyleElement
7604
- */
7605
- class StyleElementStyleSheetTarget {
7606
- constructor(target) {
7607
- this.store = new Map();
7608
- this.target = null;
7609
- const controller = target.$fastController;
7610
- this.style = document.createElement("style");
7611
- controller.addStyles(this.style);
7612
- Observable.getNotifier(controller).subscribe(this, "isConnected");
7613
- this.handleChange(controller, "isConnected");
7635
+ /**
7636
+ * The list of options.
7637
+ *
7638
+ * @public
7639
+ * @remarks
7640
+ * Overrides `Listbox.options`.
7641
+ */
7642
+ get options() {
7643
+ Observable.track(this, "options");
7644
+ return this.filteredOptions.length ? this.filteredOptions : this._options;
7614
7645
  }
7615
- targetChanged() {
7616
- if (this.target !== null) {
7617
- for (const [key, value] of this.store.entries()) {
7618
- this.target.setProperty(key, value);
7619
- }
7646
+ set options(value) {
7647
+ this._options = value;
7648
+ Observable.notify(this, "options");
7649
+ }
7650
+ /**
7651
+ * Updates the placeholder on the proxy element.
7652
+ * @internal
7653
+ */
7654
+ placeholderChanged() {
7655
+ if (this.proxy instanceof HTMLInputElement) {
7656
+ this.proxy.placeholder = this.placeholder;
7620
7657
  }
7621
7658
  }
7622
- setProperty(name, value) {
7623
- this.store.set(name, value);
7624
- DOM.queueUpdate(() => {
7625
- if (this.target !== null) {
7626
- this.target.setProperty(name, value);
7627
- }
7628
- });
7659
+ positionChanged(prev, next) {
7660
+ this.positionAttribute = next;
7661
+ this.setPositioning();
7629
7662
  }
7630
- removeProperty(name) {
7631
- this.store.delete(name);
7632
- DOM.queueUpdate(() => {
7633
- if (this.target !== null) {
7634
- this.target.removeProperty(name);
7635
- }
7636
- });
7663
+ /**
7664
+ * The value property.
7665
+ *
7666
+ * @public
7667
+ */
7668
+ get value() {
7669
+ Observable.track(this, "value");
7670
+ return this._value;
7637
7671
  }
7638
- handleChange(source, key) {
7639
- // HTMLStyleElement.sheet is null if the element isn't connected to the DOM,
7640
- // so this method reacts to changes in DOM connection for the element hosting
7641
- // the HTMLStyleElement.
7642
- //
7643
- // All rules applied via the CSSOM also get cleared when the element disconnects,
7644
- // so we need to add a new rule each time and populate it with the stored properties
7645
- const { sheet } = this.style;
7646
- if (sheet) {
7647
- // Safari will throw if we try to use the return result of insertRule()
7648
- // to index the rule inline, so store as a const prior to indexing.
7649
- // https://github.com/jsdom/jsdom uses https://github.com/NV/CSSOM for it's CSSOM implementation,
7650
- // which implements the DOM Level 2 spec for CSSStyleSheet where insertRule() requires an index argument.
7651
- const index = sheet.insertRule(":host{}", sheet.cssRules.length);
7652
- this.target = sheet.cssRules[index].style;
7672
+ set value(next) {
7673
+ var _a, _b, _c;
7674
+ const prev = `${this._value}`;
7675
+ if (this.$fastController.isConnected && this.options) {
7676
+ const selectedIndex = this.options.findIndex(el => el.text.toLowerCase() === next.toLowerCase());
7677
+ const prevSelectedValue = (_a = this.options[this.selectedIndex]) === null || _a === void 0 ? void 0 : _a.text;
7678
+ const nextSelectedValue = (_b = this.options[selectedIndex]) === null || _b === void 0 ? void 0 : _b.text;
7679
+ this.selectedIndex =
7680
+ prevSelectedValue !== nextSelectedValue
7681
+ ? selectedIndex
7682
+ : this.selectedIndex;
7683
+ next = ((_c = this.firstSelectedOption) === null || _c === void 0 ? void 0 : _c.text) || next;
7653
7684
  }
7654
- else {
7655
- this.target = null;
7685
+ if (prev !== next) {
7686
+ this._value = next;
7687
+ super.valueChanged(prev, next);
7688
+ Observable.notify(this, "value");
7656
7689
  }
7657
7690
  }
7658
- }
7659
- __decorate$1([
7660
- observable
7661
- ], StyleElementStyleSheetTarget.prototype, "target", void 0);
7662
- /**
7663
- * Handles setting properties for a normal HTMLElement
7664
- */
7665
- class ElementStyleSheetTarget {
7666
- constructor(source) {
7667
- this.target = source.style;
7668
- }
7669
- setProperty(name, value) {
7670
- DOM.queueUpdate(() => this.target.setProperty(name, value));
7671
- }
7672
- removeProperty(name) {
7673
- DOM.queueUpdate(() => this.target.removeProperty(name));
7674
- }
7675
- }
7676
- /**
7677
- * Controls emission for default values. This control is capable
7678
- * of emitting to multiple {@link PropertyTarget | PropertyTargets},
7679
- * and only emits if it has at least one root.
7680
- *
7681
- * @internal
7682
- */
7683
- class RootStyleSheetTarget {
7684
- setProperty(name, value) {
7685
- RootStyleSheetTarget.properties[name] = value;
7686
- for (const target of RootStyleSheetTarget.roots.values()) {
7687
- PropertyTargetManager.getOrCreate(RootStyleSheetTarget.normalizeRoot(target)).setProperty(name, value);
7688
- }
7689
- }
7690
- removeProperty(name) {
7691
- delete RootStyleSheetTarget.properties[name];
7692
- for (const target of RootStyleSheetTarget.roots.values()) {
7693
- PropertyTargetManager.getOrCreate(RootStyleSheetTarget.normalizeRoot(target)).removeProperty(name);
7691
+ /**
7692
+ * Handle opening and closing the listbox when the combobox is clicked.
7693
+ *
7694
+ * @param e - the mouse event
7695
+ * @internal
7696
+ */
7697
+ clickHandler(e) {
7698
+ if (this.disabled) {
7699
+ return;
7694
7700
  }
7695
- }
7696
- static registerRoot(root) {
7697
- const { roots } = RootStyleSheetTarget;
7698
- if (!roots.has(root)) {
7699
- roots.add(root);
7700
- const target = PropertyTargetManager.getOrCreate(this.normalizeRoot(root));
7701
- for (const key in RootStyleSheetTarget.properties) {
7702
- target.setProperty(key, RootStyleSheetTarget.properties[key]);
7701
+ if (this.open) {
7702
+ const captured = e.target.closest(`option,[role=option]`);
7703
+ if (!captured || captured.disabled) {
7704
+ return;
7703
7705
  }
7706
+ this.selectedOptions = [captured];
7707
+ this.control.value = captured.text;
7708
+ this.clearSelectionRange();
7709
+ this.updateValue(true);
7704
7710
  }
7711
+ this.open = !this.open;
7712
+ if (this.open) {
7713
+ this.control.focus();
7714
+ }
7715
+ return true;
7705
7716
  }
7706
- static unregisterRoot(root) {
7707
- const { roots } = RootStyleSheetTarget;
7708
- if (roots.has(root)) {
7709
- roots.delete(root);
7710
- const target = PropertyTargetManager.getOrCreate(RootStyleSheetTarget.normalizeRoot(root));
7711
- for (const key in RootStyleSheetTarget.properties) {
7712
- target.removeProperty(key);
7713
- }
7717
+ connectedCallback() {
7718
+ super.connectedCallback();
7719
+ this.forcedPosition = !!this.positionAttribute;
7720
+ if (this.value) {
7721
+ this.initialValue = this.value;
7714
7722
  }
7715
7723
  }
7716
7724
  /**
7717
- * Returns the document when provided the default element,
7718
- * otherwise is a no-op
7719
- * @param root - the root to normalize
7725
+ * Synchronize the `aria-disabled` property when the `disabled` property changes.
7726
+ *
7727
+ * @param prev - The previous disabled value
7728
+ * @param next - The next disabled value
7729
+ *
7730
+ * @internal
7720
7731
  */
7721
- static normalizeRoot(root) {
7722
- return root === defaultElement ? document : root;
7723
- }
7724
- }
7725
- RootStyleSheetTarget.roots = new Set();
7726
- RootStyleSheetTarget.properties = {};
7727
- // Caches PropertyTarget instances
7728
- const propertyTargetCache = new WeakMap();
7729
- // Use Constructable StyleSheets for FAST elements when supported, otherwise use
7730
- // HTMLStyleElement instances
7731
- const propertyTargetCtor = DOM.supportsAdoptedStyleSheets
7732
- ? ConstructableStyleSheetTarget
7733
- : StyleElementStyleSheetTarget;
7734
- /**
7735
- * Manages creation and caching of PropertyTarget instances.
7736
- *
7737
- * @internal
7738
- */
7739
- const PropertyTargetManager = Object.freeze({
7740
- getOrCreate(source) {
7741
- if (propertyTargetCache.has(source)) {
7742
- /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
7743
- return propertyTargetCache.get(source);
7744
- }
7745
- let target;
7746
- if (source === defaultElement) {
7747
- target = new RootStyleSheetTarget();
7748
- }
7749
- else if (source instanceof Document) {
7750
- target = DOM.supportsAdoptedStyleSheets
7751
- ? new DocumentStyleSheetTarget()
7752
- : new HeadStyleElementStyleSheetTarget();
7753
- }
7754
- else if (isFastElement(source)) {
7755
- target = new propertyTargetCtor(source);
7756
- }
7757
- else {
7758
- target = new ElementStyleSheetTarget(source);
7759
- }
7760
- propertyTargetCache.set(source, target);
7761
- return target;
7762
- },
7763
- });
7764
-
7765
- /**
7766
- * Implementation of {@link (DesignToken:interface)}
7767
- */
7768
- class DesignTokenImpl extends CSSDirective {
7769
- constructor(configuration) {
7770
- super();
7771
- this.subscribers = new WeakMap();
7772
- this._appliedTo = new Set();
7773
- this.name = configuration.name;
7774
- if (configuration.cssCustomPropertyName !== null) {
7775
- this.cssCustomProperty = `--${configuration.cssCustomPropertyName}`;
7776
- this.cssVar = `var(${this.cssCustomProperty})`;
7732
+ disabledChanged(prev, next) {
7733
+ if (super.disabledChanged) {
7734
+ super.disabledChanged(prev, next);
7777
7735
  }
7778
- this.id = DesignTokenImpl.uniqueId();
7779
- DesignTokenImpl.tokensById.set(this.id, this);
7780
- }
7781
- get appliedTo() {
7782
- return [...this._appliedTo];
7783
- }
7784
- static from(nameOrConfig) {
7785
- return new DesignTokenImpl({
7786
- name: typeof nameOrConfig === "string" ? nameOrConfig : nameOrConfig.name,
7787
- cssCustomPropertyName: typeof nameOrConfig === "string"
7788
- ? nameOrConfig
7789
- : nameOrConfig.cssCustomPropertyName === void 0
7790
- ? nameOrConfig.name
7791
- : nameOrConfig.cssCustomPropertyName,
7792
- });
7793
- }
7794
- static isCSSDesignToken(token) {
7795
- return typeof token.cssCustomProperty === "string";
7796
- }
7797
- static isDerivedDesignTokenValue(value) {
7798
- return typeof value === "function";
7736
+ this.ariaDisabled = this.disabled ? "true" : "false";
7799
7737
  }
7800
7738
  /**
7801
- * Gets a token by ID. Returns undefined if the token was not found.
7802
- * @param id - The ID of the token
7803
- * @returns
7739
+ * Filter available options by text value.
7740
+ *
7741
+ * @public
7804
7742
  */
7805
- static getTokenById(id) {
7806
- return DesignTokenImpl.tokensById.get(id);
7807
- }
7808
- getOrCreateSubscriberSet(target = this) {
7809
- return (this.subscribers.get(target) ||
7810
- (this.subscribers.set(target, new Set()) && this.subscribers.get(target)));
7811
- }
7812
- createCSS() {
7813
- return this.cssVar || "";
7814
- }
7815
- getValueFor(element) {
7816
- const value = DesignTokenNode.getOrCreate(element).get(this);
7817
- if (value !== undefined) {
7818
- return value;
7743
+ filterOptions() {
7744
+ if (!this.autocomplete || this.autocomplete === ComboboxAutocomplete.none) {
7745
+ this.filter = "";
7819
7746
  }
7820
- throw new Error(`Value could not be retrieved for token named "${this.name}". Ensure the value is set for ${element} or an ancestor of ${element}.`);
7821
- }
7822
- setValueFor(element, value) {
7823
- this._appliedTo.add(element);
7824
- if (value instanceof DesignTokenImpl) {
7825
- value = this.alias(value);
7747
+ const filter = this.filter.toLowerCase();
7748
+ this.filteredOptions = this._options.filter(o => o.text.toLowerCase().startsWith(this.filter.toLowerCase()));
7749
+ if (this.isAutocompleteList) {
7750
+ if (!this.filteredOptions.length && !filter) {
7751
+ this.filteredOptions = this._options;
7752
+ }
7753
+ this._options.forEach(o => {
7754
+ o.hidden = !this.filteredOptions.includes(o);
7755
+ });
7826
7756
  }
7827
- DesignTokenNode.getOrCreate(element).set(this, value);
7828
- return this;
7829
7757
  }
7830
- deleteValueFor(element) {
7831
- this._appliedTo.delete(element);
7832
- if (DesignTokenNode.existsFor(element)) {
7833
- DesignTokenNode.getOrCreate(element).delete(this);
7758
+ /**
7759
+ * Focus the control and scroll the first selected option into view.
7760
+ *
7761
+ * @internal
7762
+ * @remarks
7763
+ * Overrides: `Listbox.focusAndScrollOptionIntoView`
7764
+ */
7765
+ focusAndScrollOptionIntoView() {
7766
+ if (this.contains(document.activeElement)) {
7767
+ this.control.focus();
7768
+ if (this.firstSelectedOption) {
7769
+ requestAnimationFrame(() => {
7770
+ var _a;
7771
+ (_a = this.firstSelectedOption) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ block: "nearest" });
7772
+ });
7773
+ }
7834
7774
  }
7835
- return this;
7836
- }
7837
- withDefault(value) {
7838
- this.setValueFor(defaultElement, value);
7839
- return this;
7840
7775
  }
7841
- subscribe(subscriber, target) {
7842
- const subscriberSet = this.getOrCreateSubscriberSet(target);
7843
- if (target && !DesignTokenNode.existsFor(target)) {
7844
- DesignTokenNode.getOrCreate(target);
7776
+ /**
7777
+ * Handle focus state when the element or its children lose focus.
7778
+ *
7779
+ * @param e - The focus event
7780
+ * @internal
7781
+ */
7782
+ focusoutHandler(e) {
7783
+ this.updateValue();
7784
+ if (!this.open) {
7785
+ return true;
7845
7786
  }
7846
- if (!subscriberSet.has(subscriber)) {
7847
- subscriberSet.add(subscriber);
7787
+ const focusTarget = e.relatedTarget;
7788
+ if (this.isSameNode(focusTarget)) {
7789
+ this.focus();
7790
+ return;
7848
7791
  }
7849
- }
7850
- unsubscribe(subscriber, target) {
7851
- const list = this.subscribers.get(target || this);
7852
- if (list && list.has(subscriber)) {
7853
- list.delete(subscriber);
7792
+ if (!this.options || !this.options.includes(focusTarget)) {
7793
+ this.open = false;
7854
7794
  }
7855
7795
  }
7856
7796
  /**
7857
- * Notifies subscribers that the value for an element has changed.
7858
- * @param element - The element to emit a notification for
7797
+ * Handle content changes on the control input.
7798
+ *
7799
+ * @param e - the input event
7800
+ * @internal
7859
7801
  */
7860
- notify(element) {
7861
- const record = Object.freeze({ token: this, target: element });
7862
- if (this.subscribers.has(this)) {
7863
- this.subscribers.get(this).forEach(sub => sub.handleChange(record));
7802
+ inputHandler(e) {
7803
+ this.filter = this.control.value;
7804
+ this.filterOptions();
7805
+ if (e.inputType === "deleteContentBackward" || !this.filter.length) {
7806
+ return true;
7864
7807
  }
7865
- if (this.subscribers.has(element)) {
7866
- this.subscribers.get(element).forEach(sub => sub.handleChange(record));
7808
+ if (this.isAutocompleteList && !this.open) {
7809
+ this.open = true;
7867
7810
  }
7811
+ if (this.isAutocompleteInline && this.filteredOptions.length) {
7812
+ this.selectedOptions = [this.filteredOptions[0]];
7813
+ this.selectedIndex = this.options.indexOf(this.firstSelectedOption);
7814
+ this.setInlineSelection();
7815
+ }
7816
+ return;
7868
7817
  }
7869
7818
  /**
7870
- * Alias the token to the provided token.
7871
- * @param token - the token to alias to
7819
+ * Handle keydown actions for listbox navigation.
7820
+ *
7821
+ * @param e - the keyboard event
7822
+ * @internal
7872
7823
  */
7873
- alias(token) {
7874
- return ((target) => token.getValueFor(target));
7875
- }
7876
- }
7877
- DesignTokenImpl.uniqueId = (() => {
7878
- let id = 0;
7879
- return () => {
7880
- id++;
7881
- return id.toString(16);
7882
- };
7883
- })();
7884
- /**
7885
- * Token storage by token ID
7886
- */
7887
- DesignTokenImpl.tokensById = new Map();
7888
- class CustomPropertyReflector {
7889
- startReflection(token, target) {
7890
- token.subscribe(this, target);
7891
- this.handleChange({ token, target });
7892
- }
7893
- stopReflection(token, target) {
7894
- token.unsubscribe(this, target);
7895
- this.remove(token, target);
7896
- }
7897
- handleChange(record) {
7898
- const { token, target } = record;
7899
- this.add(token, target);
7900
- }
7901
- add(token, target) {
7902
- PropertyTargetManager.getOrCreate(target).setProperty(token.cssCustomProperty, this.resolveCSSValue(DesignTokenNode.getOrCreate(target).get(token)));
7903
- }
7904
- remove(token, target) {
7905
- PropertyTargetManager.getOrCreate(target).removeProperty(token.cssCustomProperty);
7906
- }
7907
- resolveCSSValue(value) {
7908
- return value && typeof value.createCSS === "function" ? value.createCSS() : value;
7909
- }
7910
- }
7911
- /**
7912
- * A light wrapper around BindingObserver to handle value caching and
7913
- * token notification
7914
- */
7915
- class DesignTokenBindingObserver {
7916
- constructor(source, token, node) {
7917
- this.source = source;
7918
- this.token = token;
7919
- this.node = node;
7920
- this.dependencies = new Set();
7921
- this.observer = Observable.binding(source, this, false);
7922
- // This is a little bit hacky because it's using internal APIs of BindingObserverImpl.
7923
- // BindingObserverImpl queues updates to batch it's notifications which doesn't work for this
7924
- // scenario because the DesignToken.getValueFor API is not async. Without this, using DesignToken.getValueFor()
7925
- // after DesignToken.setValueFor() when setting a dependency of the value being retrieved can return a stale
7926
- // value. Assigning .handleChange to .call forces immediate invocation of this classes handleChange() method,
7927
- // allowing resolution of values synchronously.
7928
- // TODO: https://github.com/microsoft/fast/issues/5110
7929
- this.observer.handleChange = this.observer.call;
7930
- this.handleChange();
7931
- }
7932
- disconnect() {
7933
- this.observer.disconnect();
7824
+ keydownHandler(e) {
7825
+ const key = e.key;
7826
+ if (e.ctrlKey || e.shiftKey) {
7827
+ return true;
7828
+ }
7829
+ switch (key) {
7830
+ case "Enter": {
7831
+ this.updateValue(true);
7832
+ if (this.isAutocompleteInline) {
7833
+ this.filter = this.value;
7834
+ }
7835
+ this.open = false;
7836
+ this.clearSelectionRange();
7837
+ break;
7838
+ }
7839
+ case "Escape": {
7840
+ if (!this.isAutocompleteInline) {
7841
+ this.selectedIndex = -1;
7842
+ }
7843
+ if (this.open) {
7844
+ this.open = false;
7845
+ break;
7846
+ }
7847
+ this.value = "";
7848
+ this.control.value = "";
7849
+ this.filter = "";
7850
+ this.filterOptions();
7851
+ break;
7852
+ }
7853
+ case "Tab": {
7854
+ this.updateValue();
7855
+ if (!this.open) {
7856
+ return true;
7857
+ }
7858
+ e.preventDefault();
7859
+ this.open = false;
7860
+ break;
7861
+ }
7862
+ case "ArrowUp":
7863
+ case "ArrowDown": {
7864
+ this.filterOptions();
7865
+ if (!this.open) {
7866
+ this.open = true;
7867
+ break;
7868
+ }
7869
+ if (this.filteredOptions.length > 0) {
7870
+ super.keydownHandler(e);
7871
+ }
7872
+ if (this.isAutocompleteInline) {
7873
+ this.updateValue();
7874
+ this.setInlineSelection();
7875
+ }
7876
+ break;
7877
+ }
7878
+ default: {
7879
+ return true;
7880
+ }
7881
+ }
7934
7882
  }
7935
7883
  /**
7884
+ * Handle keyup actions for value input and text field manipulations.
7885
+ *
7886
+ * @param e - the keyboard event
7936
7887
  * @internal
7937
7888
  */
7938
- handleChange() {
7939
- this.node.store.set(this.token, this.observer.observe(this.node.target, defaultExecutionContext));
7940
- }
7941
- }
7942
- /**
7943
- * Stores resolved token/value pairs and notifies on changes
7944
- */
7945
- class Store {
7946
- constructor() {
7947
- this.values = new Map();
7948
- }
7949
- set(token, value) {
7950
- if (this.values.get(token) !== value) {
7951
- this.values.set(token, value);
7952
- Observable.getNotifier(this).notify(token.id);
7953
- }
7954
- }
7955
- get(token) {
7956
- Observable.track(this, token.id);
7957
- return this.values.get(token);
7958
- }
7959
- delete(token) {
7960
- this.values.delete(token);
7961
- }
7962
- all() {
7963
- return this.values.entries();
7964
- }
7965
- }
7966
- const nodeCache = new WeakMap();
7967
- const childToParent = new WeakMap();
7968
- /**
7969
- * A node responsible for setting and getting token values,
7970
- * emitting values to CSS custom properties, and maintaining
7971
- * inheritance structures.
7972
- */
7973
- class DesignTokenNode {
7974
- constructor(target) {
7975
- this.target = target;
7976
- /**
7977
- * Stores all resolved token values for a node
7978
- */
7979
- this.store = new Store();
7980
- /**
7981
- * All children assigned to the node
7982
- */
7983
- this.children = [];
7984
- /**
7985
- * All values explicitly assigned to the node in their raw form
7986
- */
7987
- this.assignedValues = new Map();
7988
- /**
7989
- * Tokens currently being reflected to CSS custom properties
7990
- */
7991
- this.reflecting = new Set();
7992
- /**
7993
- * Binding observers for assigned and inherited derived values.
7994
- */
7995
- this.bindingObservers = new Map();
7996
- /**
7997
- * Emits notifications to token when token values
7998
- * change the DesignTokenNode
7999
- */
8000
- this.tokenValueChangeHandler = {
8001
- handleChange: (source, arg) => {
8002
- const token = DesignTokenImpl.getTokenById(arg);
8003
- if (token) {
8004
- // Notify any token subscribers
8005
- token.notify(this.target);
8006
- if (DesignTokenImpl.isCSSDesignToken(token)) {
8007
- const parent = this.parent;
8008
- const reflecting = this.isReflecting(token);
8009
- if (parent) {
8010
- const parentValue = parent.get(token);
8011
- const sourceValue = source.get(token);
8012
- if (parentValue !== sourceValue && !reflecting) {
8013
- this.reflectToCSS(token);
8014
- }
8015
- else if (parentValue === sourceValue && reflecting) {
8016
- this.stopReflectToCSS(token);
8017
- }
8018
- }
8019
- else if (!reflecting) {
8020
- this.reflectToCSS(token);
8021
- }
8022
- }
8023
- }
8024
- },
8025
- };
8026
- nodeCache.set(target, this);
8027
- // Map store change notifications to token change notifications
8028
- Observable.getNotifier(this.store).subscribe(this.tokenValueChangeHandler);
8029
- if (target instanceof FASTElement) {
8030
- target.$fastController.addBehaviors([this]);
8031
- }
8032
- else if (target.isConnected) {
8033
- this.bind();
7889
+ keyupHandler(e) {
7890
+ const key = e.key;
7891
+ switch (key) {
7892
+ case "ArrowLeft":
7893
+ case "ArrowRight":
7894
+ case "Backspace":
7895
+ case "Delete":
7896
+ case "Home":
7897
+ case "End": {
7898
+ this.filter = this.control.value;
7899
+ this.selectedIndex = -1;
7900
+ this.filterOptions();
7901
+ break;
7902
+ }
8034
7903
  }
8035
7904
  }
8036
7905
  /**
8037
- * Returns a DesignTokenNode for an element.
8038
- * Creates a new instance if one does not already exist for a node,
8039
- * otherwise returns the cached instance
7906
+ * Ensure that the selectedIndex is within the current allowable filtered range.
8040
7907
  *
8041
- * @param target - The HTML element to retrieve a DesignTokenNode for
7908
+ * @param prev - the previous selected index value
7909
+ * @param next - the current selected index value
7910
+ *
7911
+ * @internal
8042
7912
  */
8043
- static getOrCreate(target) {
8044
- return nodeCache.get(target) || new DesignTokenNode(target);
7913
+ selectedIndexChanged(prev, next) {
7914
+ if (this.$fastController.isConnected) {
7915
+ next = limit(-1, this.options.length - 1, next);
7916
+ // we only want to call the super method when the selectedIndex is in range
7917
+ if (next !== this.selectedIndex) {
7918
+ this.selectedIndex = next;
7919
+ return;
7920
+ }
7921
+ super.selectedIndexChanged(prev, next);
7922
+ }
8045
7923
  }
8046
7924
  /**
8047
- * Determines if a DesignTokenNode has been created for a target
8048
- * @param target - The element to test
7925
+ * Move focus to the previous selectable option.
7926
+ *
7927
+ * @internal
7928
+ * @remarks
7929
+ * Overrides `Listbox.selectPreviousOption`
8049
7930
  */
8050
- static existsFor(target) {
8051
- return nodeCache.has(target);
7931
+ selectPreviousOption() {
7932
+ if (!this.disabled && this.selectedIndex >= 0) {
7933
+ this.selectedIndex = this.selectedIndex - 1;
7934
+ }
8052
7935
  }
8053
7936
  /**
8054
- * Searches for and return the nearest parent DesignTokenNode.
8055
- * Null is returned if no node is found or the node provided is for a default element.
7937
+ * Set the default selected options at initialization or reset.
7938
+ *
7939
+ * @internal
7940
+ * @remarks
7941
+ * Overrides `Listbox.setDefaultSelectedOption`
8056
7942
  */
8057
- static findParent(node) {
8058
- if (!(defaultElement === node.target)) {
8059
- let parent = composedParent(node.target);
8060
- while (parent !== null) {
8061
- if (nodeCache.has(parent)) {
8062
- return nodeCache.get(parent);
8063
- }
8064
- parent = composedParent(parent);
7943
+ setDefaultSelectedOption() {
7944
+ if (this.$fastController.isConnected && this.options) {
7945
+ const selectedIndex = this.options.findIndex(el => el.getAttribute("selected") !== null || el.selected);
7946
+ this.selectedIndex = selectedIndex;
7947
+ if (!this.dirtyValue && this.firstSelectedOption) {
7948
+ this.value = this.firstSelectedOption.text;
8065
7949
  }
8066
- return DesignTokenNode.getOrCreate(defaultElement);
7950
+ this.setSelectedOptions();
8067
7951
  }
8068
- return null;
8069
7952
  }
8070
7953
  /**
8071
- * Finds the closest node with a value explicitly assigned for a token, otherwise null.
8072
- * @param token - The token to look for
8073
- * @param start - The node to start looking for value assignment
8074
- * @returns
7954
+ * Focus and select the content of the control based on the first selected option.
7955
+ *
7956
+ * @param start - The index for the starting range
7957
+ * @internal
8075
7958
  */
8076
- static findClosestAssignedNode(token, start) {
8077
- let current = start;
8078
- do {
8079
- if (current.has(token)) {
8080
- return current;
8081
- }
8082
- current = current.parent
8083
- ? current.parent
8084
- : current.target !== defaultElement
8085
- ? DesignTokenNode.getOrCreate(defaultElement)
8086
- : null;
8087
- } while (current !== null);
8088
- return null;
7959
+ setInlineSelection() {
7960
+ if (this.firstSelectedOption) {
7961
+ this.control.value = this.firstSelectedOption.text;
7962
+ this.control.focus();
7963
+ this.control.setSelectionRange(this.filter.length, this.control.value.length, "backward");
7964
+ }
8089
7965
  }
8090
7966
  /**
8091
- * The parent DesignTokenNode, or null.
7967
+ * Calculate and apply listbox positioning based on available viewport space.
7968
+ *
7969
+ * @param force - direction to force the listbox to display
7970
+ * @public
8092
7971
  */
8093
- get parent() {
8094
- return childToParent.get(this) || null;
7972
+ setPositioning() {
7973
+ const currentBox = this.getBoundingClientRect();
7974
+ const viewportHeight = window.innerHeight;
7975
+ const availableBottom = viewportHeight - currentBox.bottom;
7976
+ this.position = this.forcedPosition
7977
+ ? this.positionAttribute
7978
+ : currentBox.top > availableBottom
7979
+ ? SelectPosition.above
7980
+ : SelectPosition.below;
7981
+ this.positionAttribute = this.forcedPosition
7982
+ ? this.positionAttribute
7983
+ : this.position;
7984
+ this.maxHeight =
7985
+ this.position === SelectPosition.above ? ~~currentBox.top : ~~availableBottom;
8095
7986
  }
8096
7987
  /**
8097
- * Checks if a token has been assigned an explicit value the node.
8098
- * @param token - the token to check.
8099
- */
8100
- has(token) {
8101
- return this.assignedValues.has(token);
8102
- }
8103
- /**
8104
- * Gets the value of a token for a node
8105
- * @param token - The token to retrieve the value for
8106
- * @returns
8107
- */
8108
- get(token) {
8109
- const value = this.store.get(token);
8110
- if (value !== undefined) {
8111
- return value;
8112
- }
8113
- const raw = this.getRaw(token);
8114
- if (raw !== undefined) {
8115
- this.hydrate(token, raw);
8116
- return this.get(token);
8117
- }
8118
- }
8119
- /**
8120
- * Retrieves the raw assigned value of a token from the nearest assigned node.
8121
- * @param token - The token to retrieve a raw value for
8122
- * @returns
8123
- */
8124
- getRaw(token) {
8125
- var _a;
8126
- if (this.assignedValues.has(token)) {
8127
- return this.assignedValues.get(token);
8128
- }
8129
- return (_a = DesignTokenNode.findClosestAssignedNode(token, this)) === null || _a === void 0 ? void 0 : _a.getRaw(token);
8130
- }
8131
- /**
8132
- * Sets a token to a value for a node
8133
- * @param token - The token to set
8134
- * @param value - The value to set the token to
8135
- */
8136
- set(token, value) {
8137
- if (DesignTokenImpl.isDerivedDesignTokenValue(this.assignedValues.get(token))) {
8138
- this.tearDownBindingObserver(token);
8139
- }
8140
- this.assignedValues.set(token, value);
8141
- if (DesignTokenImpl.isDerivedDesignTokenValue(value)) {
8142
- this.setupBindingObserver(token, value);
8143
- }
8144
- else {
8145
- this.store.set(token, value);
8146
- }
8147
- }
8148
- /**
8149
- * Deletes a token value for the node.
8150
- * @param token - The token to delete the value for
8151
- */
8152
- delete(token) {
8153
- this.assignedValues.delete(token);
8154
- this.tearDownBindingObserver(token);
8155
- const upstream = this.getRaw(token);
8156
- if (upstream) {
8157
- this.hydrate(token, upstream);
8158
- }
8159
- else {
8160
- this.store.delete(token);
8161
- }
8162
- }
8163
- /**
8164
- * Invoked when the DesignTokenNode.target is attached to the document
8165
- */
8166
- bind() {
8167
- const parent = DesignTokenNode.findParent(this);
8168
- if (parent) {
8169
- parent.appendChild(this);
8170
- }
8171
- for (const key of this.assignedValues.keys()) {
8172
- key.notify(this.target);
8173
- }
8174
- }
8175
- /**
8176
- * Invoked when the DesignTokenNode.target is detached from the document
8177
- */
8178
- unbind() {
8179
- if (this.parent) {
8180
- const parent = childToParent.get(this);
8181
- parent.removeChild(this);
8182
- }
8183
- }
8184
- /**
8185
- * Appends a child to a parent DesignTokenNode.
8186
- * @param child - The child to append to the node
8187
- */
8188
- appendChild(child) {
8189
- if (child.parent) {
8190
- childToParent.get(child).removeChild(child);
8191
- }
8192
- const reParent = this.children.filter(x => child.contains(x));
8193
- childToParent.set(child, this);
8194
- this.children.push(child);
8195
- reParent.forEach(x => child.appendChild(x));
8196
- Observable.getNotifier(this.store).subscribe(child);
8197
- // How can we not notify *every* subscriber?
8198
- for (const [token, value] of this.store.all()) {
8199
- child.hydrate(token, this.bindingObservers.has(token) ? this.getRaw(token) : value);
8200
- }
8201
- }
8202
- /**
8203
- * Removes a child from a node.
8204
- * @param child - The child to remove.
8205
- */
8206
- removeChild(child) {
8207
- const childIndex = this.children.indexOf(child);
8208
- if (childIndex !== -1) {
8209
- this.children.splice(childIndex, 1);
8210
- }
8211
- Observable.getNotifier(this.store).unsubscribe(child);
8212
- return child.parent === this ? childToParent.delete(child) : false;
8213
- }
8214
- /**
8215
- * Tests whether a provided node is contained by
8216
- * the calling node.
8217
- * @param test - The node to test
8218
- */
8219
- contains(test) {
8220
- return composedContains(this.target, test.target);
8221
- }
8222
- /**
8223
- * Instructs the node to reflect a design token for the provided token.
8224
- * @param token - The design token to reflect
8225
- */
8226
- reflectToCSS(token) {
8227
- if (!this.isReflecting(token)) {
8228
- this.reflecting.add(token);
8229
- DesignTokenNode.cssCustomPropertyReflector.startReflection(token, this.target);
8230
- }
8231
- }
8232
- /**
8233
- * Stops reflecting a DesignToken to CSS
8234
- * @param token - The design token to stop reflecting
7988
+ * Ensure that the entire list of options is used when setting the selected property.
7989
+ *
7990
+ * @param prev - the previous list of selected options
7991
+ * @param next - the current list of selected options
7992
+ *
7993
+ * @internal
7994
+ * @remarks
7995
+ * Overrides: `Listbox.selectedOptionsChanged`
8235
7996
  */
8236
- stopReflectToCSS(token) {
8237
- if (this.isReflecting(token)) {
8238
- this.reflecting.delete(token);
8239
- DesignTokenNode.cssCustomPropertyReflector.stopReflection(token, this.target);
7997
+ selectedOptionsChanged(prev, next) {
7998
+ if (this.$fastController.isConnected) {
7999
+ this._options.forEach(o => {
8000
+ o.selected = next.includes(o);
8001
+ });
8240
8002
  }
8241
8003
  }
8242
8004
  /**
8243
- * Determines if a token is being reflected to CSS for a node.
8244
- * @param token - The token to check for reflection
8245
- * @returns
8005
+ * Synchronize the form-associated proxy and update the value property of the element.
8006
+ *
8007
+ * @param prev - the previous collection of slotted option elements
8008
+ * @param next - the next collection of slotted option elements
8009
+ *
8010
+ * @internal
8246
8011
  */
8247
- isReflecting(token) {
8248
- return this.reflecting.has(token);
8012
+ slottedOptionsChanged(prev, next) {
8013
+ super.slottedOptionsChanged(prev, next);
8014
+ this.updateValue();
8249
8015
  }
8250
8016
  /**
8251
- * Handle changes to upstream tokens
8252
- * @param source - The parent DesignTokenNode
8253
- * @param property - The token ID that changed
8017
+ * Sets the value and to match the first selected option.
8018
+ *
8019
+ * @param shouldEmit - if true, the change event will be emitted
8020
+ *
8021
+ * @internal
8254
8022
  */
8255
- handleChange(source, property) {
8256
- const token = DesignTokenImpl.getTokenById(property);
8257
- if (!token) {
8258
- return;
8023
+ updateValue(shouldEmit) {
8024
+ var _a;
8025
+ if (this.$fastController.isConnected) {
8026
+ this.value = ((_a = this.firstSelectedOption) === null || _a === void 0 ? void 0 : _a.text) || this.control.value;
8259
8027
  }
8260
- this.hydrate(token, this.getRaw(token));
8261
- }
8262
- /**
8263
- * Hydrates a token with a DesignTokenValue, making retrieval available.
8264
- * @param token - The token to hydrate
8265
- * @param value - The value to hydrate
8266
- */
8267
- hydrate(token, value) {
8268
- if (!this.has(token)) {
8269
- const observer = this.bindingObservers.get(token);
8270
- if (DesignTokenImpl.isDerivedDesignTokenValue(value)) {
8271
- if (observer) {
8272
- // If the binding source doesn't match, we need
8273
- // to update the binding
8274
- if (observer.source !== value) {
8275
- this.tearDownBindingObserver(token);
8276
- this.setupBindingObserver(token, value);
8277
- }
8278
- }
8279
- else {
8280
- this.setupBindingObserver(token, value);
8281
- }
8282
- }
8283
- else {
8284
- if (observer) {
8285
- this.tearDownBindingObserver(token);
8286
- }
8287
- this.store.set(token, value);
8288
- }
8028
+ if (shouldEmit) {
8029
+ this.$emit("change");
8289
8030
  }
8290
8031
  }
8291
8032
  /**
8292
- * Sets up a binding observer for a derived token value that notifies token
8293
- * subscribers on change.
8294
- *
8295
- * @param token - The token to notify when the binding updates
8296
- * @param source - The binding source
8297
- */
8298
- setupBindingObserver(token, source) {
8299
- const binding = new DesignTokenBindingObserver(source, token, this);
8300
- this.bindingObservers.set(token, binding);
8301
- return binding;
8302
- }
8303
- /**
8304
- * Tear down a binding observer for a token.
8033
+ * @internal
8305
8034
  */
8306
- tearDownBindingObserver(token) {
8307
- if (this.bindingObservers.has(token)) {
8308
- this.bindingObservers.get(token).disconnect();
8309
- this.bindingObservers.delete(token);
8310
- return true;
8311
- }
8312
- return false;
8035
+ clearSelectionRange() {
8036
+ const controlValueLength = this.control.value.length;
8037
+ this.control.setSelectionRange(controlValueLength, controlValueLength);
8313
8038
  }
8314
8039
  }
8040
+ __decorate$1([
8041
+ attr({ attribute: "autocomplete", mode: "fromView" })
8042
+ ], Combobox$1.prototype, "autocomplete", void 0);
8043
+ __decorate$1([
8044
+ observable
8045
+ ], Combobox$1.prototype, "maxHeight", void 0);
8046
+ __decorate$1([
8047
+ attr({ attribute: "open", mode: "boolean" })
8048
+ ], Combobox$1.prototype, "open", void 0);
8049
+ __decorate$1([
8050
+ attr
8051
+ ], Combobox$1.prototype, "placeholder", void 0);
8052
+ __decorate$1([
8053
+ attr({ attribute: "position" })
8054
+ ], Combobox$1.prototype, "positionAttribute", void 0);
8055
+ __decorate$1([
8056
+ observable
8057
+ ], Combobox$1.prototype, "position", void 0);
8315
8058
  /**
8316
- * Responsible for reflecting tokens to CSS custom properties
8059
+ * Includes ARIA states and properties relating to the ARIA combobox role.
8060
+ *
8061
+ * @public
8317
8062
  */
8318
- DesignTokenNode.cssCustomPropertyReflector = new CustomPropertyReflector();
8063
+ class DelegatesARIACombobox {
8064
+ }
8319
8065
  __decorate$1([
8320
8066
  observable
8321
- ], DesignTokenNode.prototype, "children", void 0);
8322
- function create(nameOrConfig) {
8323
- return DesignTokenImpl.from(nameOrConfig);
8324
- }
8325
- /* eslint-enable @typescript-eslint/no-unused-vars */
8067
+ ], DelegatesARIACombobox.prototype, "ariaAutoComplete", void 0);
8068
+ __decorate$1([
8069
+ observable
8070
+ ], DelegatesARIACombobox.prototype, "ariaControls", void 0);
8071
+ applyMixins(DelegatesARIACombobox, DelegatesARIAListbox);
8072
+ applyMixins(Combobox$1, StartEnd, DelegatesARIACombobox);
8073
+
8326
8074
  /**
8327
- * Factory object for creating {@link (DesignToken:interface)} instances.
8075
+ * The template for the {@link @microsoft/fast-foundation#(Combobox:class)} component.
8328
8076
  * @public
8329
8077
  */
8330
- const DesignToken = Object.freeze({
8331
- create,
8078
+ const comboboxTemplate = (context, definition) => html `
8079
+ <template
8080
+ aria-disabled="${x => x.ariaDisabled}"
8081
+ autocomplete="${x => x.autocomplete}"
8082
+ class="${x => (x.open ? "open" : "")} ${x => x.disabled ? "disabled" : ""} ${x => x.position}"
8083
+ ?open="${x => x.open}"
8084
+ tabindex="${x => (!x.disabled ? "0" : null)}"
8085
+ @click="${(x, c) => x.clickHandler(c.event)}"
8086
+ @focusout="${(x, c) => x.focusoutHandler(c.event)}"
8087
+ @keydown="${(x, c) => x.keydownHandler(c.event)}"
8088
+ >
8089
+ <div class="control" part="control">
8090
+ ${startSlotTemplate(context, definition)}
8091
+ <slot name="control">
8092
+ <input
8093
+ aria-activedescendant="${x => x.open ? x.ariaActiveDescendant : null}"
8094
+ aria-autocomplete="${x => x.ariaAutoComplete}"
8095
+ aria-controls="${x => x.ariaControls}"
8096
+ aria-disabled="${x => x.ariaDisabled}"
8097
+ aria-expanded="${x => x.ariaExpanded}"
8098
+ aria-haspopup="listbox"
8099
+ class="selected-value"
8100
+ part="selected-value"
8101
+ placeholder="${x => x.placeholder}"
8102
+ role="combobox"
8103
+ type="text"
8104
+ ?disabled="${x => x.disabled}"
8105
+ :value="${x => x.value}"
8106
+ @input="${(x, c) => x.inputHandler(c.event)}"
8107
+ @keyup="${(x, c) => x.keyupHandler(c.event)}"
8108
+ ${ref("control")}
8109
+ />
8110
+ <div class="indicator" part="indicator" aria-hidden="true">
8111
+ <slot name="indicator">
8112
+ ${definition.indicator || ""}
8113
+ </slot>
8114
+ </div>
8115
+ </slot>
8116
+ ${endSlotTemplate(context, definition)}
8117
+ </div>
8118
+ <div
8119
+ class="listbox"
8120
+ id="${x => x.listboxId}"
8121
+ part="listbox"
8122
+ role="listbox"
8123
+ ?disabled="${x => x.disabled}"
8124
+ ?hidden="${x => !x.open}"
8125
+ ${ref("listbox")}
8126
+ >
8127
+ <slot
8128
+ ${slotted({
8129
+ filter: Listbox.slottedOptionFilter,
8130
+ flatten: true,
8131
+ property: "slottedOptions",
8132
+ })}
8133
+ ></slot>
8134
+ </div>
8135
+ </template>
8136
+ `;
8137
+
8138
+ /**
8139
+ * Retrieves the "composed parent" element of a node, ignoring DOM tree boundaries.
8140
+ * When the parent of a node is a shadow-root, it will return the host
8141
+ * element of the shadow root. Otherwise it will return the parent node or null if
8142
+ * no parent node exists.
8143
+ * @param element - The element for which to retrieve the composed parent
8144
+ *
8145
+ * @public
8146
+ */
8147
+ function composedParent(element) {
8148
+ const parentNode = element.parentElement;
8149
+ if (parentNode) {
8150
+ return parentNode;
8151
+ }
8152
+ else {
8153
+ const rootNode = element.getRootNode();
8154
+ if (rootNode.host instanceof HTMLElement) {
8155
+ // this is shadow-root
8156
+ return rootNode.host;
8157
+ }
8158
+ }
8159
+ return null;
8160
+ }
8161
+
8162
+ /**
8163
+ * Determines if the reference element contains the test element in a "composed" DOM tree that
8164
+ * ignores shadow DOM boundaries.
8165
+ *
8166
+ * Returns true of the test element is a descendent of the reference, or exist in
8167
+ * a shadow DOM that is a logical descendent of the reference. Otherwise returns false.
8168
+ * @param reference - The element to test for containment against.
8169
+ * @param test - The element being tested for containment.
8170
+ *
8171
+ * @public
8172
+ */
8173
+ function composedContains(reference, test) {
8174
+ let current = test;
8175
+ while (current !== null) {
8176
+ if (current === reference) {
8177
+ return true;
8178
+ }
8179
+ current = composedParent(current);
8180
+ }
8181
+ return false;
8182
+ }
8183
+
8184
+ const defaultElement = document.createElement("div");
8185
+ function isFastElement(element) {
8186
+ return element instanceof FASTElement;
8187
+ }
8188
+ class QueuedStyleSheetTarget {
8189
+ setProperty(name, value) {
8190
+ DOM.queueUpdate(() => this.target.setProperty(name, value));
8191
+ }
8192
+ removeProperty(name) {
8193
+ DOM.queueUpdate(() => this.target.removeProperty(name));
8194
+ }
8195
+ }
8196
+ /**
8197
+ * Handles setting properties for a FASTElement using Constructable Stylesheets
8198
+ */
8199
+ class ConstructableStyleSheetTarget extends QueuedStyleSheetTarget {
8200
+ constructor(source) {
8201
+ super();
8202
+ const sheet = new CSSStyleSheet();
8203
+ this.target = sheet.cssRules[sheet.insertRule(":host{}")].style;
8204
+ source.$fastController.addStyles(ElementStyles.create([sheet]));
8205
+ }
8206
+ }
8207
+ class DocumentStyleSheetTarget extends QueuedStyleSheetTarget {
8208
+ constructor() {
8209
+ super();
8210
+ const sheet = new CSSStyleSheet();
8211
+ this.target = sheet.cssRules[sheet.insertRule(":root{}")].style;
8212
+ document.adoptedStyleSheets = [
8213
+ ...document.adoptedStyleSheets,
8214
+ sheet,
8215
+ ];
8216
+ }
8217
+ }
8218
+ class HeadStyleElementStyleSheetTarget extends QueuedStyleSheetTarget {
8219
+ constructor() {
8220
+ super();
8221
+ this.style = document.createElement("style");
8222
+ document.head.appendChild(this.style);
8223
+ const { sheet } = this.style;
8224
+ // Because the HTMLStyleElement has been appended,
8225
+ // there shouldn't exist a case where `sheet` is null,
8226
+ // but if-check it just in case.
8227
+ if (sheet) {
8228
+ // https://github.com/jsdom/jsdom uses https://github.com/NV/CSSOM for it's CSSOM implementation,
8229
+ // which implements the DOM Level 2 spec for CSSStyleSheet where insertRule() requires an index argument.
8230
+ const index = sheet.insertRule(":root{}", sheet.cssRules.length);
8231
+ this.target = sheet.cssRules[index].style;
8232
+ }
8233
+ }
8234
+ }
8235
+ /**
8236
+ * Handles setting properties for a FASTElement using an HTMLStyleElement
8237
+ */
8238
+ class StyleElementStyleSheetTarget {
8239
+ constructor(target) {
8240
+ this.store = new Map();
8241
+ this.target = null;
8242
+ const controller = target.$fastController;
8243
+ this.style = document.createElement("style");
8244
+ controller.addStyles(this.style);
8245
+ Observable.getNotifier(controller).subscribe(this, "isConnected");
8246
+ this.handleChange(controller, "isConnected");
8247
+ }
8248
+ targetChanged() {
8249
+ if (this.target !== null) {
8250
+ for (const [key, value] of this.store.entries()) {
8251
+ this.target.setProperty(key, value);
8252
+ }
8253
+ }
8254
+ }
8255
+ setProperty(name, value) {
8256
+ this.store.set(name, value);
8257
+ DOM.queueUpdate(() => {
8258
+ if (this.target !== null) {
8259
+ this.target.setProperty(name, value);
8260
+ }
8261
+ });
8262
+ }
8263
+ removeProperty(name) {
8264
+ this.store.delete(name);
8265
+ DOM.queueUpdate(() => {
8266
+ if (this.target !== null) {
8267
+ this.target.removeProperty(name);
8268
+ }
8269
+ });
8270
+ }
8271
+ handleChange(source, key) {
8272
+ // HTMLStyleElement.sheet is null if the element isn't connected to the DOM,
8273
+ // so this method reacts to changes in DOM connection for the element hosting
8274
+ // the HTMLStyleElement.
8275
+ //
8276
+ // All rules applied via the CSSOM also get cleared when the element disconnects,
8277
+ // so we need to add a new rule each time and populate it with the stored properties
8278
+ const { sheet } = this.style;
8279
+ if (sheet) {
8280
+ // Safari will throw if we try to use the return result of insertRule()
8281
+ // to index the rule inline, so store as a const prior to indexing.
8282
+ // https://github.com/jsdom/jsdom uses https://github.com/NV/CSSOM for it's CSSOM implementation,
8283
+ // which implements the DOM Level 2 spec for CSSStyleSheet where insertRule() requires an index argument.
8284
+ const index = sheet.insertRule(":host{}", sheet.cssRules.length);
8285
+ this.target = sheet.cssRules[index].style;
8286
+ }
8287
+ else {
8288
+ this.target = null;
8289
+ }
8290
+ }
8291
+ }
8292
+ __decorate$1([
8293
+ observable
8294
+ ], StyleElementStyleSheetTarget.prototype, "target", void 0);
8295
+ /**
8296
+ * Handles setting properties for a normal HTMLElement
8297
+ */
8298
+ class ElementStyleSheetTarget {
8299
+ constructor(source) {
8300
+ this.target = source.style;
8301
+ }
8302
+ setProperty(name, value) {
8303
+ DOM.queueUpdate(() => this.target.setProperty(name, value));
8304
+ }
8305
+ removeProperty(name) {
8306
+ DOM.queueUpdate(() => this.target.removeProperty(name));
8307
+ }
8308
+ }
8309
+ /**
8310
+ * Controls emission for default values. This control is capable
8311
+ * of emitting to multiple {@link PropertyTarget | PropertyTargets},
8312
+ * and only emits if it has at least one root.
8313
+ *
8314
+ * @internal
8315
+ */
8316
+ class RootStyleSheetTarget {
8317
+ setProperty(name, value) {
8318
+ RootStyleSheetTarget.properties[name] = value;
8319
+ for (const target of RootStyleSheetTarget.roots.values()) {
8320
+ PropertyTargetManager.getOrCreate(RootStyleSheetTarget.normalizeRoot(target)).setProperty(name, value);
8321
+ }
8322
+ }
8323
+ removeProperty(name) {
8324
+ delete RootStyleSheetTarget.properties[name];
8325
+ for (const target of RootStyleSheetTarget.roots.values()) {
8326
+ PropertyTargetManager.getOrCreate(RootStyleSheetTarget.normalizeRoot(target)).removeProperty(name);
8327
+ }
8328
+ }
8329
+ static registerRoot(root) {
8330
+ const { roots } = RootStyleSheetTarget;
8331
+ if (!roots.has(root)) {
8332
+ roots.add(root);
8333
+ const target = PropertyTargetManager.getOrCreate(this.normalizeRoot(root));
8334
+ for (const key in RootStyleSheetTarget.properties) {
8335
+ target.setProperty(key, RootStyleSheetTarget.properties[key]);
8336
+ }
8337
+ }
8338
+ }
8339
+ static unregisterRoot(root) {
8340
+ const { roots } = RootStyleSheetTarget;
8341
+ if (roots.has(root)) {
8342
+ roots.delete(root);
8343
+ const target = PropertyTargetManager.getOrCreate(RootStyleSheetTarget.normalizeRoot(root));
8344
+ for (const key in RootStyleSheetTarget.properties) {
8345
+ target.removeProperty(key);
8346
+ }
8347
+ }
8348
+ }
8349
+ /**
8350
+ * Returns the document when provided the default element,
8351
+ * otherwise is a no-op
8352
+ * @param root - the root to normalize
8353
+ */
8354
+ static normalizeRoot(root) {
8355
+ return root === defaultElement ? document : root;
8356
+ }
8357
+ }
8358
+ RootStyleSheetTarget.roots = new Set();
8359
+ RootStyleSheetTarget.properties = {};
8360
+ // Caches PropertyTarget instances
8361
+ const propertyTargetCache = new WeakMap();
8362
+ // Use Constructable StyleSheets for FAST elements when supported, otherwise use
8363
+ // HTMLStyleElement instances
8364
+ const propertyTargetCtor = DOM.supportsAdoptedStyleSheets
8365
+ ? ConstructableStyleSheetTarget
8366
+ : StyleElementStyleSheetTarget;
8367
+ /**
8368
+ * Manages creation and caching of PropertyTarget instances.
8369
+ *
8370
+ * @internal
8371
+ */
8372
+ const PropertyTargetManager = Object.freeze({
8373
+ getOrCreate(source) {
8374
+ if (propertyTargetCache.has(source)) {
8375
+ /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
8376
+ return propertyTargetCache.get(source);
8377
+ }
8378
+ let target;
8379
+ if (source === defaultElement) {
8380
+ target = new RootStyleSheetTarget();
8381
+ }
8382
+ else if (source instanceof Document) {
8383
+ target = DOM.supportsAdoptedStyleSheets
8384
+ ? new DocumentStyleSheetTarget()
8385
+ : new HeadStyleElementStyleSheetTarget();
8386
+ }
8387
+ else if (isFastElement(source)) {
8388
+ target = new propertyTargetCtor(source);
8389
+ }
8390
+ else {
8391
+ target = new ElementStyleSheetTarget(source);
8392
+ }
8393
+ propertyTargetCache.set(source, target);
8394
+ return target;
8395
+ },
8396
+ });
8397
+
8398
+ /**
8399
+ * Implementation of {@link (DesignToken:interface)}
8400
+ */
8401
+ class DesignTokenImpl extends CSSDirective {
8402
+ constructor(configuration) {
8403
+ super();
8404
+ this.subscribers = new WeakMap();
8405
+ this._appliedTo = new Set();
8406
+ this.name = configuration.name;
8407
+ if (configuration.cssCustomPropertyName !== null) {
8408
+ this.cssCustomProperty = `--${configuration.cssCustomPropertyName}`;
8409
+ this.cssVar = `var(${this.cssCustomProperty})`;
8410
+ }
8411
+ this.id = DesignTokenImpl.uniqueId();
8412
+ DesignTokenImpl.tokensById.set(this.id, this);
8413
+ }
8414
+ get appliedTo() {
8415
+ return [...this._appliedTo];
8416
+ }
8417
+ static from(nameOrConfig) {
8418
+ return new DesignTokenImpl({
8419
+ name: typeof nameOrConfig === "string" ? nameOrConfig : nameOrConfig.name,
8420
+ cssCustomPropertyName: typeof nameOrConfig === "string"
8421
+ ? nameOrConfig
8422
+ : nameOrConfig.cssCustomPropertyName === void 0
8423
+ ? nameOrConfig.name
8424
+ : nameOrConfig.cssCustomPropertyName,
8425
+ });
8426
+ }
8427
+ static isCSSDesignToken(token) {
8428
+ return typeof token.cssCustomProperty === "string";
8429
+ }
8430
+ static isDerivedDesignTokenValue(value) {
8431
+ return typeof value === "function";
8432
+ }
8433
+ /**
8434
+ * Gets a token by ID. Returns undefined if the token was not found.
8435
+ * @param id - The ID of the token
8436
+ * @returns
8437
+ */
8438
+ static getTokenById(id) {
8439
+ return DesignTokenImpl.tokensById.get(id);
8440
+ }
8441
+ getOrCreateSubscriberSet(target = this) {
8442
+ return (this.subscribers.get(target) ||
8443
+ (this.subscribers.set(target, new Set()) && this.subscribers.get(target)));
8444
+ }
8445
+ createCSS() {
8446
+ return this.cssVar || "";
8447
+ }
8448
+ getValueFor(element) {
8449
+ const value = DesignTokenNode.getOrCreate(element).get(this);
8450
+ if (value !== undefined) {
8451
+ return value;
8452
+ }
8453
+ throw new Error(`Value could not be retrieved for token named "${this.name}". Ensure the value is set for ${element} or an ancestor of ${element}.`);
8454
+ }
8455
+ setValueFor(element, value) {
8456
+ this._appliedTo.add(element);
8457
+ if (value instanceof DesignTokenImpl) {
8458
+ value = this.alias(value);
8459
+ }
8460
+ DesignTokenNode.getOrCreate(element).set(this, value);
8461
+ return this;
8462
+ }
8463
+ deleteValueFor(element) {
8464
+ this._appliedTo.delete(element);
8465
+ if (DesignTokenNode.existsFor(element)) {
8466
+ DesignTokenNode.getOrCreate(element).delete(this);
8467
+ }
8468
+ return this;
8469
+ }
8470
+ withDefault(value) {
8471
+ this.setValueFor(defaultElement, value);
8472
+ return this;
8473
+ }
8474
+ subscribe(subscriber, target) {
8475
+ const subscriberSet = this.getOrCreateSubscriberSet(target);
8476
+ if (target && !DesignTokenNode.existsFor(target)) {
8477
+ DesignTokenNode.getOrCreate(target);
8478
+ }
8479
+ if (!subscriberSet.has(subscriber)) {
8480
+ subscriberSet.add(subscriber);
8481
+ }
8482
+ }
8483
+ unsubscribe(subscriber, target) {
8484
+ const list = this.subscribers.get(target || this);
8485
+ if (list && list.has(subscriber)) {
8486
+ list.delete(subscriber);
8487
+ }
8488
+ }
8489
+ /**
8490
+ * Notifies subscribers that the value for an element has changed.
8491
+ * @param element - The element to emit a notification for
8492
+ */
8493
+ notify(element) {
8494
+ const record = Object.freeze({ token: this, target: element });
8495
+ if (this.subscribers.has(this)) {
8496
+ this.subscribers.get(this).forEach(sub => sub.handleChange(record));
8497
+ }
8498
+ if (this.subscribers.has(element)) {
8499
+ this.subscribers.get(element).forEach(sub => sub.handleChange(record));
8500
+ }
8501
+ }
8502
+ /**
8503
+ * Alias the token to the provided token.
8504
+ * @param token - the token to alias to
8505
+ */
8506
+ alias(token) {
8507
+ return ((target) => token.getValueFor(target));
8508
+ }
8509
+ }
8510
+ DesignTokenImpl.uniqueId = (() => {
8511
+ let id = 0;
8512
+ return () => {
8513
+ id++;
8514
+ return id.toString(16);
8515
+ };
8516
+ })();
8517
+ /**
8518
+ * Token storage by token ID
8519
+ */
8520
+ DesignTokenImpl.tokensById = new Map();
8521
+ class CustomPropertyReflector {
8522
+ startReflection(token, target) {
8523
+ token.subscribe(this, target);
8524
+ this.handleChange({ token, target });
8525
+ }
8526
+ stopReflection(token, target) {
8527
+ token.unsubscribe(this, target);
8528
+ this.remove(token, target);
8529
+ }
8530
+ handleChange(record) {
8531
+ const { token, target } = record;
8532
+ this.add(token, target);
8533
+ }
8534
+ add(token, target) {
8535
+ PropertyTargetManager.getOrCreate(target).setProperty(token.cssCustomProperty, this.resolveCSSValue(DesignTokenNode.getOrCreate(target).get(token)));
8536
+ }
8537
+ remove(token, target) {
8538
+ PropertyTargetManager.getOrCreate(target).removeProperty(token.cssCustomProperty);
8539
+ }
8540
+ resolveCSSValue(value) {
8541
+ return value && typeof value.createCSS === "function" ? value.createCSS() : value;
8542
+ }
8543
+ }
8544
+ /**
8545
+ * A light wrapper around BindingObserver to handle value caching and
8546
+ * token notification
8547
+ */
8548
+ class DesignTokenBindingObserver {
8549
+ constructor(source, token, node) {
8550
+ this.source = source;
8551
+ this.token = token;
8552
+ this.node = node;
8553
+ this.dependencies = new Set();
8554
+ this.observer = Observable.binding(source, this, false);
8555
+ // This is a little bit hacky because it's using internal APIs of BindingObserverImpl.
8556
+ // BindingObserverImpl queues updates to batch it's notifications which doesn't work for this
8557
+ // scenario because the DesignToken.getValueFor API is not async. Without this, using DesignToken.getValueFor()
8558
+ // after DesignToken.setValueFor() when setting a dependency of the value being retrieved can return a stale
8559
+ // value. Assigning .handleChange to .call forces immediate invocation of this classes handleChange() method,
8560
+ // allowing resolution of values synchronously.
8561
+ // TODO: https://github.com/microsoft/fast/issues/5110
8562
+ this.observer.handleChange = this.observer.call;
8563
+ this.handleChange();
8564
+ }
8565
+ disconnect() {
8566
+ this.observer.disconnect();
8567
+ }
8568
+ /**
8569
+ * @internal
8570
+ */
8571
+ handleChange() {
8572
+ this.node.store.set(this.token, this.observer.observe(this.node.target, defaultExecutionContext));
8573
+ }
8574
+ }
8575
+ /**
8576
+ * Stores resolved token/value pairs and notifies on changes
8577
+ */
8578
+ class Store {
8579
+ constructor() {
8580
+ this.values = new Map();
8581
+ }
8582
+ set(token, value) {
8583
+ if (this.values.get(token) !== value) {
8584
+ this.values.set(token, value);
8585
+ Observable.getNotifier(this).notify(token.id);
8586
+ }
8587
+ }
8588
+ get(token) {
8589
+ Observable.track(this, token.id);
8590
+ return this.values.get(token);
8591
+ }
8592
+ delete(token) {
8593
+ this.values.delete(token);
8594
+ }
8595
+ all() {
8596
+ return this.values.entries();
8597
+ }
8598
+ }
8599
+ const nodeCache = new WeakMap();
8600
+ const childToParent = new WeakMap();
8601
+ /**
8602
+ * A node responsible for setting and getting token values,
8603
+ * emitting values to CSS custom properties, and maintaining
8604
+ * inheritance structures.
8605
+ */
8606
+ class DesignTokenNode {
8607
+ constructor(target) {
8608
+ this.target = target;
8609
+ /**
8610
+ * Stores all resolved token values for a node
8611
+ */
8612
+ this.store = new Store();
8613
+ /**
8614
+ * All children assigned to the node
8615
+ */
8616
+ this.children = [];
8617
+ /**
8618
+ * All values explicitly assigned to the node in their raw form
8619
+ */
8620
+ this.assignedValues = new Map();
8621
+ /**
8622
+ * Tokens currently being reflected to CSS custom properties
8623
+ */
8624
+ this.reflecting = new Set();
8625
+ /**
8626
+ * Binding observers for assigned and inherited derived values.
8627
+ */
8628
+ this.bindingObservers = new Map();
8629
+ /**
8630
+ * Emits notifications to token when token values
8631
+ * change the DesignTokenNode
8632
+ */
8633
+ this.tokenValueChangeHandler = {
8634
+ handleChange: (source, arg) => {
8635
+ const token = DesignTokenImpl.getTokenById(arg);
8636
+ if (token) {
8637
+ // Notify any token subscribers
8638
+ token.notify(this.target);
8639
+ if (DesignTokenImpl.isCSSDesignToken(token)) {
8640
+ const parent = this.parent;
8641
+ const reflecting = this.isReflecting(token);
8642
+ if (parent) {
8643
+ const parentValue = parent.get(token);
8644
+ const sourceValue = source.get(token);
8645
+ if (parentValue !== sourceValue && !reflecting) {
8646
+ this.reflectToCSS(token);
8647
+ }
8648
+ else if (parentValue === sourceValue && reflecting) {
8649
+ this.stopReflectToCSS(token);
8650
+ }
8651
+ }
8652
+ else if (!reflecting) {
8653
+ this.reflectToCSS(token);
8654
+ }
8655
+ }
8656
+ }
8657
+ },
8658
+ };
8659
+ nodeCache.set(target, this);
8660
+ // Map store change notifications to token change notifications
8661
+ Observable.getNotifier(this.store).subscribe(this.tokenValueChangeHandler);
8662
+ if (target instanceof FASTElement) {
8663
+ target.$fastController.addBehaviors([this]);
8664
+ }
8665
+ else if (target.isConnected) {
8666
+ this.bind();
8667
+ }
8668
+ }
8669
+ /**
8670
+ * Returns a DesignTokenNode for an element.
8671
+ * Creates a new instance if one does not already exist for a node,
8672
+ * otherwise returns the cached instance
8673
+ *
8674
+ * @param target - The HTML element to retrieve a DesignTokenNode for
8675
+ */
8676
+ static getOrCreate(target) {
8677
+ return nodeCache.get(target) || new DesignTokenNode(target);
8678
+ }
8679
+ /**
8680
+ * Determines if a DesignTokenNode has been created for a target
8681
+ * @param target - The element to test
8682
+ */
8683
+ static existsFor(target) {
8684
+ return nodeCache.has(target);
8685
+ }
8686
+ /**
8687
+ * Searches for and return the nearest parent DesignTokenNode.
8688
+ * Null is returned if no node is found or the node provided is for a default element.
8689
+ */
8690
+ static findParent(node) {
8691
+ if (!(defaultElement === node.target)) {
8692
+ let parent = composedParent(node.target);
8693
+ while (parent !== null) {
8694
+ if (nodeCache.has(parent)) {
8695
+ return nodeCache.get(parent);
8696
+ }
8697
+ parent = composedParent(parent);
8698
+ }
8699
+ return DesignTokenNode.getOrCreate(defaultElement);
8700
+ }
8701
+ return null;
8702
+ }
8703
+ /**
8704
+ * Finds the closest node with a value explicitly assigned for a token, otherwise null.
8705
+ * @param token - The token to look for
8706
+ * @param start - The node to start looking for value assignment
8707
+ * @returns
8708
+ */
8709
+ static findClosestAssignedNode(token, start) {
8710
+ let current = start;
8711
+ do {
8712
+ if (current.has(token)) {
8713
+ return current;
8714
+ }
8715
+ current = current.parent
8716
+ ? current.parent
8717
+ : current.target !== defaultElement
8718
+ ? DesignTokenNode.getOrCreate(defaultElement)
8719
+ : null;
8720
+ } while (current !== null);
8721
+ return null;
8722
+ }
8723
+ /**
8724
+ * The parent DesignTokenNode, or null.
8725
+ */
8726
+ get parent() {
8727
+ return childToParent.get(this) || null;
8728
+ }
8729
+ /**
8730
+ * Checks if a token has been assigned an explicit value the node.
8731
+ * @param token - the token to check.
8732
+ */
8733
+ has(token) {
8734
+ return this.assignedValues.has(token);
8735
+ }
8736
+ /**
8737
+ * Gets the value of a token for a node
8738
+ * @param token - The token to retrieve the value for
8739
+ * @returns
8740
+ */
8741
+ get(token) {
8742
+ const value = this.store.get(token);
8743
+ if (value !== undefined) {
8744
+ return value;
8745
+ }
8746
+ const raw = this.getRaw(token);
8747
+ if (raw !== undefined) {
8748
+ this.hydrate(token, raw);
8749
+ return this.get(token);
8750
+ }
8751
+ }
8752
+ /**
8753
+ * Retrieves the raw assigned value of a token from the nearest assigned node.
8754
+ * @param token - The token to retrieve a raw value for
8755
+ * @returns
8756
+ */
8757
+ getRaw(token) {
8758
+ var _a;
8759
+ if (this.assignedValues.has(token)) {
8760
+ return this.assignedValues.get(token);
8761
+ }
8762
+ return (_a = DesignTokenNode.findClosestAssignedNode(token, this)) === null || _a === void 0 ? void 0 : _a.getRaw(token);
8763
+ }
8764
+ /**
8765
+ * Sets a token to a value for a node
8766
+ * @param token - The token to set
8767
+ * @param value - The value to set the token to
8768
+ */
8769
+ set(token, value) {
8770
+ if (DesignTokenImpl.isDerivedDesignTokenValue(this.assignedValues.get(token))) {
8771
+ this.tearDownBindingObserver(token);
8772
+ }
8773
+ this.assignedValues.set(token, value);
8774
+ if (DesignTokenImpl.isDerivedDesignTokenValue(value)) {
8775
+ this.setupBindingObserver(token, value);
8776
+ }
8777
+ else {
8778
+ this.store.set(token, value);
8779
+ }
8780
+ }
8781
+ /**
8782
+ * Deletes a token value for the node.
8783
+ * @param token - The token to delete the value for
8784
+ */
8785
+ delete(token) {
8786
+ this.assignedValues.delete(token);
8787
+ this.tearDownBindingObserver(token);
8788
+ const upstream = this.getRaw(token);
8789
+ if (upstream) {
8790
+ this.hydrate(token, upstream);
8791
+ }
8792
+ else {
8793
+ this.store.delete(token);
8794
+ }
8795
+ }
8796
+ /**
8797
+ * Invoked when the DesignTokenNode.target is attached to the document
8798
+ */
8799
+ bind() {
8800
+ const parent = DesignTokenNode.findParent(this);
8801
+ if (parent) {
8802
+ parent.appendChild(this);
8803
+ }
8804
+ for (const key of this.assignedValues.keys()) {
8805
+ key.notify(this.target);
8806
+ }
8807
+ }
8808
+ /**
8809
+ * Invoked when the DesignTokenNode.target is detached from the document
8810
+ */
8811
+ unbind() {
8812
+ if (this.parent) {
8813
+ const parent = childToParent.get(this);
8814
+ parent.removeChild(this);
8815
+ }
8816
+ }
8817
+ /**
8818
+ * Appends a child to a parent DesignTokenNode.
8819
+ * @param child - The child to append to the node
8820
+ */
8821
+ appendChild(child) {
8822
+ if (child.parent) {
8823
+ childToParent.get(child).removeChild(child);
8824
+ }
8825
+ const reParent = this.children.filter(x => child.contains(x));
8826
+ childToParent.set(child, this);
8827
+ this.children.push(child);
8828
+ reParent.forEach(x => child.appendChild(x));
8829
+ Observable.getNotifier(this.store).subscribe(child);
8830
+ // How can we not notify *every* subscriber?
8831
+ for (const [token, value] of this.store.all()) {
8832
+ child.hydrate(token, this.bindingObservers.has(token) ? this.getRaw(token) : value);
8833
+ }
8834
+ }
8835
+ /**
8836
+ * Removes a child from a node.
8837
+ * @param child - The child to remove.
8838
+ */
8839
+ removeChild(child) {
8840
+ const childIndex = this.children.indexOf(child);
8841
+ if (childIndex !== -1) {
8842
+ this.children.splice(childIndex, 1);
8843
+ }
8844
+ Observable.getNotifier(this.store).unsubscribe(child);
8845
+ return child.parent === this ? childToParent.delete(child) : false;
8846
+ }
8847
+ /**
8848
+ * Tests whether a provided node is contained by
8849
+ * the calling node.
8850
+ * @param test - The node to test
8851
+ */
8852
+ contains(test) {
8853
+ return composedContains(this.target, test.target);
8854
+ }
8855
+ /**
8856
+ * Instructs the node to reflect a design token for the provided token.
8857
+ * @param token - The design token to reflect
8858
+ */
8859
+ reflectToCSS(token) {
8860
+ if (!this.isReflecting(token)) {
8861
+ this.reflecting.add(token);
8862
+ DesignTokenNode.cssCustomPropertyReflector.startReflection(token, this.target);
8863
+ }
8864
+ }
8865
+ /**
8866
+ * Stops reflecting a DesignToken to CSS
8867
+ * @param token - The design token to stop reflecting
8868
+ */
8869
+ stopReflectToCSS(token) {
8870
+ if (this.isReflecting(token)) {
8871
+ this.reflecting.delete(token);
8872
+ DesignTokenNode.cssCustomPropertyReflector.stopReflection(token, this.target);
8873
+ }
8874
+ }
8875
+ /**
8876
+ * Determines if a token is being reflected to CSS for a node.
8877
+ * @param token - The token to check for reflection
8878
+ * @returns
8879
+ */
8880
+ isReflecting(token) {
8881
+ return this.reflecting.has(token);
8882
+ }
8883
+ /**
8884
+ * Handle changes to upstream tokens
8885
+ * @param source - The parent DesignTokenNode
8886
+ * @param property - The token ID that changed
8887
+ */
8888
+ handleChange(source, property) {
8889
+ const token = DesignTokenImpl.getTokenById(property);
8890
+ if (!token) {
8891
+ return;
8892
+ }
8893
+ this.hydrate(token, this.getRaw(token));
8894
+ }
8895
+ /**
8896
+ * Hydrates a token with a DesignTokenValue, making retrieval available.
8897
+ * @param token - The token to hydrate
8898
+ * @param value - The value to hydrate
8899
+ */
8900
+ hydrate(token, value) {
8901
+ if (!this.has(token)) {
8902
+ const observer = this.bindingObservers.get(token);
8903
+ if (DesignTokenImpl.isDerivedDesignTokenValue(value)) {
8904
+ if (observer) {
8905
+ // If the binding source doesn't match, we need
8906
+ // to update the binding
8907
+ if (observer.source !== value) {
8908
+ this.tearDownBindingObserver(token);
8909
+ this.setupBindingObserver(token, value);
8910
+ }
8911
+ }
8912
+ else {
8913
+ this.setupBindingObserver(token, value);
8914
+ }
8915
+ }
8916
+ else {
8917
+ if (observer) {
8918
+ this.tearDownBindingObserver(token);
8919
+ }
8920
+ this.store.set(token, value);
8921
+ }
8922
+ }
8923
+ }
8924
+ /**
8925
+ * Sets up a binding observer for a derived token value that notifies token
8926
+ * subscribers on change.
8927
+ *
8928
+ * @param token - The token to notify when the binding updates
8929
+ * @param source - The binding source
8930
+ */
8931
+ setupBindingObserver(token, source) {
8932
+ const binding = new DesignTokenBindingObserver(source, token, this);
8933
+ this.bindingObservers.set(token, binding);
8934
+ return binding;
8935
+ }
8936
+ /**
8937
+ * Tear down a binding observer for a token.
8938
+ */
8939
+ tearDownBindingObserver(token) {
8940
+ if (this.bindingObservers.has(token)) {
8941
+ this.bindingObservers.get(token).disconnect();
8942
+ this.bindingObservers.delete(token);
8943
+ return true;
8944
+ }
8945
+ return false;
8946
+ }
8947
+ }
8948
+ /**
8949
+ * Responsible for reflecting tokens to CSS custom properties
8950
+ */
8951
+ DesignTokenNode.cssCustomPropertyReflector = new CustomPropertyReflector();
8952
+ __decorate$1([
8953
+ observable
8954
+ ], DesignTokenNode.prototype, "children", void 0);
8955
+ function create(nameOrConfig) {
8956
+ return DesignTokenImpl.from(nameOrConfig);
8957
+ }
8958
+ /* eslint-enable @typescript-eslint/no-unused-vars */
8959
+ /**
8960
+ * Factory object for creating {@link (DesignToken:interface)} instances.
8961
+ * @public
8962
+ */
8963
+ const DesignToken = Object.freeze({
8964
+ create,
8332
8965
  /**
8333
8966
  * Informs DesignToken that an HTMLElement for which tokens have
8334
8967
  * been set has been connected to the document.
@@ -13059,7 +13692,7 @@
13059
13692
  */
13060
13693
  const focusVisible$1 = canUseFocusVisible() ? "focus-visible" : "focus";
13061
13694
 
13062
- const styles$p = css `
13695
+ const styles$s = css `
13063
13696
  :host {
13064
13697
  contain: layout;
13065
13698
  display: block;
@@ -13083,7 +13716,7 @@
13083
13716
  baseName: 'anchored-region',
13084
13717
  baseClass: AnchoredRegion$1,
13085
13718
  template: anchoredRegionTemplate,
13086
- styles: styles$p
13719
+ styles: styles$s
13087
13720
  });
13088
13721
  DesignSystem.getOrCreate()
13089
13722
  .withPrefix('nimble')
@@ -13420,7 +14053,7 @@
13420
14053
 
13421
14054
  const template$5 = html `<slot></slot>`;
13422
14055
 
13423
- const styles$o = css `
14056
+ const styles$r = css `
13424
14057
  :host {
13425
14058
  display: contents;
13426
14059
  }
@@ -13476,7 +14109,7 @@
13476
14109
  ], ThemeProvider.prototype, "theme", void 0);
13477
14110
  const nimbleDesignSystemProvider = ThemeProvider.compose({
13478
14111
  baseName: 'theme-provider',
13479
- styles: styles$o,
14112
+ styles: styles$r,
13480
14113
  template: template$5
13481
14114
  });
13482
14115
  DesignSystem.getOrCreate()
@@ -13725,7 +14358,7 @@
13725
14358
  */
13726
14359
  const themeBehavior = (lightStyle, darkStyleOrAlias, colorStyleOrAlias) => new ThemeStyleSheetBehavior(lightStyle, darkStyleOrAlias, colorStyleOrAlias);
13727
14360
 
13728
- const styles$n = css `
14361
+ const styles$q = css `
13729
14362
  ${display('inline-block')}
13730
14363
 
13731
14364
  :host {
@@ -13788,7 +14421,7 @@
13788
14421
  baseName: 'breadcrumb',
13789
14422
  baseClass: Breadcrumb$1,
13790
14423
  template: breadcrumbTemplate,
13791
- styles: styles$n
14424
+ styles: styles$q
13792
14425
  });
13793
14426
  DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleBreadcrumb());
13794
14427
 
@@ -14363,578 +14996,1102 @@
14363
14996
  };
14364
14997
 
14365
14998
  /**
14366
- * This file is a workaround for: https://github.com/prettier/prettier/issues/11400
14999
+ * This file is a workaround for: https://github.com/prettier/prettier/issues/11400
15000
+ */
15001
+ /**
15002
+ * The string representing the focus selector to be used. Value
15003
+ * will be ":focus-visible" when https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo
15004
+ * is supported and ":focus" when it is not.
15005
+ *
15006
+ * @public
15007
+ */
15008
+ const focusVisible = `:${focusVisible$1}`;
15009
+
15010
+ const styles$p = css `
15011
+ ${display('inline-flex')}
15012
+
15013
+ :host {
15014
+ height: ${controlHeight};
15015
+ box-sizing: border-box;
15016
+ font: ${bodyFont};
15017
+ color: ${bodyFontColor};
15018
+ padding-left: calc(4px - ${borderWidth});
15019
+ }
15020
+
15021
+ .listitem {
15022
+ display: flex;
15023
+ align-items: center;
15024
+ }
15025
+
15026
+ .control {
15027
+ color: var(--ni-private-breadcrumb-link-font-color);
15028
+ cursor: default;
15029
+ display: flex;
15030
+ align-items: center;
15031
+ justify-content: center;
15032
+ border: ${borderWidth} solid transparent;
15033
+ padding-right: calc(4px - ${borderWidth});
15034
+ }
15035
+
15036
+ .control:link {
15037
+ cursor: pointer;
15038
+ text-decoration: none;
15039
+ }
15040
+
15041
+ .control:hover {
15042
+ text-decoration: underline;
15043
+ }
15044
+
15045
+ .control:active {
15046
+ color: var(--ni-private-breadcrumb-link-active-font-color);
15047
+ text-decoration: underline;
15048
+ }
15049
+
15050
+ .control:link${focusVisible} {
15051
+ border: ${borderWidth} solid ${borderHoverColor};
15052
+ outline: 2px solid ${borderHoverColor};
15053
+ outline-offset: 1px;
15054
+ }
15055
+
15056
+ .start,
15057
+ .end {
15058
+ display: flex;
15059
+ align-items: center;
15060
+ }
15061
+
15062
+ .start {
15063
+ margin-inline-end: 4px;
15064
+ }
15065
+
15066
+ slot[name='separator'] {
15067
+ display: flex;
15068
+ align-items: center;
15069
+ }
15070
+
15071
+ slot[name='separator'] svg {
15072
+ width: ${iconSize};
15073
+ height: ${iconSize};
15074
+ }
15075
+
15076
+ slot[name='separator'] path {
15077
+ fill: ${placeholderFontColor};
15078
+ }
15079
+ `;
15080
+
15081
+ /**
15082
+ * A nimble-styled breadcrumb item
14367
15083
  */
15084
+ class BreadcrumbItem extends BreadcrumbItem$1 {
15085
+ }
15086
+ const nimbleBreadcrumbItem = BreadcrumbItem.compose({
15087
+ baseName: 'breadcrumb-item',
15088
+ baseClass: BreadcrumbItem$1,
15089
+ template: breadcrumbItemTemplate,
15090
+ styles: styles$p,
15091
+ separator: forwardSlash16X16.data
15092
+ });
15093
+ DesignSystem.getOrCreate()
15094
+ .withPrefix('nimble')
15095
+ .register(nimbleBreadcrumbItem());
15096
+
14368
15097
  /**
14369
- * The string representing the focus selector to be used. Value
14370
- * will be ":focus-visible" when https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo
14371
- * is supported and ":focus" when it is not.
15098
+ * Behavior that will conditionally apply a stylesheet based on the element's
15099
+ * appearance property
15100
+ *
15101
+ * @param value - The value of the appearance property
15102
+ * @param styles - The styles to be applied when condition matches
14372
15103
  *
14373
15104
  * @public
14374
15105
  */
14375
- const focusVisible = `:${focusVisible$1}`;
15106
+ function appearanceBehavior(value, styles) {
15107
+ return new PropertyStyleSheetBehavior('appearance', value, styles);
15108
+ }
14376
15109
 
14377
- const styles$m = css `
15110
+ /**
15111
+ * Types of button appearance.
15112
+ * @public
15113
+ */
15114
+ const ButtonAppearance = {
15115
+ outline: 'outline',
15116
+ ghost: 'ghost',
15117
+ block: 'block'
15118
+ };
15119
+
15120
+ const styles$o = css `
14378
15121
  ${display('inline-flex')}
14379
15122
 
14380
15123
  :host {
15124
+ background-color: transparent;
14381
15125
  height: ${controlHeight};
15126
+ color: ${buttonLabelFontColor};
15127
+ font: ${buttonLabelFont};
15128
+ cursor: pointer;
15129
+ outline: none;
15130
+ border: none;
14382
15131
  box-sizing: border-box;
14383
- font: ${bodyFont};
14384
- color: ${bodyFontColor};
14385
- padding-left: calc(4px - ${borderWidth});
15132
+ ${
15133
+ /*
15134
+ Not sure why but this is needed to get buttons with icons and buttons
15135
+ without icons to align on the same line when the button is inline-flex
15136
+ See: https://github.com/microsoft/fast/issues/5414
15137
+ */ ''}
15138
+ vertical-align: middle;
15139
+ }
15140
+
15141
+ :host([disabled]) {
15142
+ color: ${buttonLabelDisabledFontColor};
15143
+ cursor: default;
15144
+ }
15145
+
15146
+ .control {
15147
+ background-color: transparent;
15148
+ height: 100%;
15149
+ width: 100%;
15150
+ border: ${borderWidth} solid transparent;
15151
+ box-sizing: border-box;
15152
+ color: inherit;
15153
+ border-radius: inherit;
15154
+ fill: inherit;
15155
+ display: inline-flex;
15156
+ align-items: center;
15157
+ justify-content: center;
15158
+ gap: 4px;
15159
+ cursor: inherit;
15160
+ font: inherit;
15161
+ outline: none;
15162
+ margin: 0;
15163
+ padding: 0 ${standardPadding};
15164
+ transition: box-shadow ${smallDelay};
15165
+ }
15166
+
15167
+ :host([content-hidden]) .control {
15168
+ width: ${controlHeight};
15169
+ padding: 0px;
15170
+ }
15171
+
15172
+ @media (prefers-reduced-motion) {
15173
+ .control {
15174
+ transition-duration: 0s;
15175
+ }
15176
+ }
15177
+
15178
+ .control:hover {
15179
+ box-shadow: 0px 0px 0px ${borderWidth} ${borderHoverColor} inset;
15180
+ outline: none;
15181
+ }
15182
+
15183
+ .control${focusVisible} {
15184
+ box-shadow: 0px 0px 0px ${borderWidth} ${borderHoverColor} inset;
15185
+ outline: ${borderWidth} solid ${borderHoverColor};
15186
+ outline-offset: -4px;
15187
+ }
15188
+
15189
+ .control:active {
15190
+ box-shadow: none;
15191
+ outline: none;
15192
+ }
15193
+
15194
+ .control[disabled] {
15195
+ box-shadow: none;
15196
+ outline: none;
15197
+ }
15198
+
15199
+ .content {
15200
+ display: contents;
14386
15201
  }
14387
15202
 
14388
- .listitem {
14389
- display: flex;
14390
- align-items: center;
14391
- }
15203
+ :host([content-hidden]) .content {
15204
+ ${
15205
+ /**
15206
+ * Hide content visually while keeping it screen reader-accessible.
15207
+ * Source: https://webaim.org/techniques/css/invisiblecontent/#techniques
15208
+ * See discussion here: https://github.com/microsoft/fast/issues/5740#issuecomment-1068195035
15209
+ */
15210
+ ''}
15211
+ display: inline-block;
15212
+ height: 1px;
15213
+ width: 1px;
15214
+ position: absolute;
15215
+ margin: -1px;
15216
+ clip: rect(1px, 1px, 1px, 1px);
15217
+ clip-path: inset(50%);
15218
+ overflow: hidden;
15219
+ padding: 0;
15220
+ }
15221
+
15222
+ [part='start'] {
15223
+ display: contents;
15224
+ ${iconColor.cssCustomProperty}: ${buttonLabelFontColor};
15225
+ }
15226
+
15227
+ :host([disabled]) slot[name='start']::slotted(*) {
15228
+ opacity: 0.3;
15229
+ }
15230
+
15231
+ [part='end'] {
15232
+ display: contents;
15233
+ ${iconColor.cssCustomProperty}: ${buttonLabelFontColor};
15234
+ }
15235
+
15236
+ :host([disabled]) slot[name='end']::slotted(*) {
15237
+ opacity: 0.3;
15238
+ }
15239
+
15240
+ :host([content-hidden]) [part='end'] {
15241
+ display: none;
15242
+ }
15243
+ `
15244
+ // prettier-ignore
15245
+ .withBehaviors(appearanceBehavior(ButtonAppearance.outline, css `
15246
+ .control {
15247
+ background-color: transparent;
15248
+ border-color: rgba(${actionRgbPartialColor}, 0.3);
15249
+ }
15250
+
15251
+ .control:hover {
15252
+ background-color: transparent;
15253
+ border-color: ${borderHoverColor};
15254
+ }
15255
+
15256
+ .control${focusVisible} {
15257
+ background-color: transparent;
15258
+ border-color: ${borderHoverColor};
15259
+ }
15260
+
15261
+ .control:active {
15262
+ background-color: ${fillSelectedColor};
15263
+ border-color: ${fillSelectedColor};
15264
+ }
15265
+
15266
+ .control[disabled] {
15267
+ background-color: transparent;
15268
+ border-color: rgba(${borderRgbPartialColor}, 0.2);
15269
+ }
15270
+ `), appearanceBehavior(ButtonAppearance.ghost, css `
15271
+ .control {
15272
+ background-color: transparent;
15273
+ border-color: transparent;
15274
+ }
15275
+
15276
+ .control:hover {
15277
+ background-color: transparent;
15278
+ border-color: ${borderHoverColor};
15279
+ }
15280
+
15281
+ .control${focusVisible} {
15282
+ background-color: transparent;
15283
+ border-color: ${borderHoverColor};
15284
+ }
15285
+
15286
+ .control:active {
15287
+ background-color: ${fillSelectedColor};
15288
+ border-color: ${fillSelectedColor};
15289
+ }
15290
+
15291
+ .control[disabled] {
15292
+ background-color: transparent;
15293
+ border-color: transparent;
15294
+ }
15295
+ `), appearanceBehavior(ButtonAppearance.block, css `
15296
+ .control {
15297
+ background-color: rgba(${borderRgbPartialColor}, 0.1);
15298
+ border-color: transparent;
15299
+ }
15300
+
15301
+ .control:hover {
15302
+ background-color: transparent;
15303
+ border-color: ${borderHoverColor};
15304
+ }
15305
+
15306
+ .control${focusVisible} {
15307
+ background-color: rgba(${borderRgbPartialColor}, 0.1);
15308
+ border-color: ${borderHoverColor};
15309
+ }
15310
+
15311
+ .control${focusVisible}:hover {
15312
+ background-color: transparent;
15313
+ }
15314
+
15315
+ .control:active {
15316
+ background-color: ${fillSelectedColor};
15317
+ border-color: ${fillSelectedColor};
15318
+ }
15319
+
15320
+ .control[disabled] {
15321
+ background-color: rgba(${borderRgbPartialColor}, 0.1);
15322
+ border-color: transparent;
15323
+ }
15324
+ `));
15325
+
15326
+ // prettier-ignore
15327
+ const styles$n = styles$o
15328
+ .withBehaviors(appearanceBehavior(ButtonAppearance.outline, css `
15329
+ :host(.primary) .control {
15330
+ box-shadow: 0px 0px 0px ${borderWidth} rgba(${actionRgbPartialColor}, 0.3) inset;
15331
+ }
15332
+
15333
+ :host(.primary) .control:hover {
15334
+ box-shadow: 0px 0px 0px ${borderWidth} ${borderHoverColor} inset;
15335
+ }
14392
15336
 
14393
- .control {
14394
- color: var(--ni-private-breadcrumb-link-font-color);
14395
- cursor: default;
14396
- display: flex;
14397
- align-items: center;
14398
- justify-content: center;
14399
- border: ${borderWidth} solid transparent;
14400
- padding-right: calc(4px - ${borderWidth});
14401
- }
15337
+ :host(.primary) .control${focusVisible} {
15338
+ box-shadow: 0px 0px 0px ${borderWidth} ${borderHoverColor} inset;
15339
+ }
14402
15340
 
14403
- .control:link {
14404
- cursor: pointer;
14405
- text-decoration: none;
14406
- }
15341
+ :host(.primary) .control:active {
15342
+ box-shadow: none;
15343
+ }
14407
15344
 
14408
- .control:hover {
14409
- text-decoration: underline;
14410
- }
15345
+ :host(.primary) .control[disabled] {
15346
+ box-shadow: none;
15347
+ }
15348
+ `), appearanceBehavior(ButtonAppearance.block, css `
15349
+ :host(.primary) .control {
15350
+ background-clip: padding-box;
15351
+ border-color: rgba(${actionRgbPartialColor}, 0.3);
15352
+ border-width: calc(2 * ${borderWidth});
15353
+ padding: 0 calc(${standardPadding} - ${borderWidth});
15354
+ }
14411
15355
 
14412
- .control:active {
14413
- color: var(--ni-private-breadcrumb-link-active-font-color);
14414
- text-decoration: underline;
14415
- }
15356
+ :host(.primary[content-hidden]) .control {
15357
+ padding: 0px;
15358
+ }
14416
15359
 
14417
- .control:link${focusVisible} {
14418
- border: ${borderWidth} solid ${borderHoverColor};
14419
- outline: 2px solid ${borderHoverColor};
14420
- outline-offset: 1px;
14421
- }
15360
+ :host(.primary) .control:hover {
15361
+ border-color: ${borderHoverColor};
15362
+ box-shadow: none;
15363
+ }
14422
15364
 
14423
- .start,
14424
- .end {
14425
- display: flex;
14426
- align-items: center;
14427
- }
15365
+ :host(.primary) .control${focusVisible} {
15366
+ background-clip: border-box;
15367
+ border-color: ${borderHoverColor};
15368
+ border-width: ${borderWidth};
15369
+ box-shadow: 0px 0px 0px ${borderWidth} ${borderHoverColor} inset;
15370
+ padding: 0 ${standardPadding};
15371
+ }
14428
15372
 
14429
- .start {
14430
- margin-inline-end: 4px;
14431
- }
15373
+ :host(.primary[content-hidden]) .control${focusVisible} {
15374
+ padding: 0px;
15375
+ }
14432
15376
 
14433
- slot[name='separator'] {
14434
- display: flex;
14435
- align-items: center;
14436
- }
15377
+ :host(.primary) .control:active {
15378
+ background-clip: border-box;
15379
+ border-color: ${fillSelectedColor};
15380
+ border-width: ${borderWidth};
15381
+ box-shadow: none;
15382
+ padding: 0 ${standardPadding};
15383
+ }
14437
15384
 
14438
- slot[name='separator'] svg {
14439
- width: ${iconSize};
14440
- height: ${iconSize};
14441
- }
15385
+ :host(.primary[content-hidden]) .control:active {
15386
+ padding: 0px;
15387
+ }
14442
15388
 
14443
- slot[name='separator'] path {
14444
- fill: ${placeholderFontColor};
14445
- }
14446
- `;
15389
+ :host(.primary) .control[disabled] {
15390
+ background-clip: border-box;
15391
+ border-color: transparent;
15392
+ border-width: ${borderWidth};
15393
+ box-shadow: none;
15394
+ padding: 0 ${standardPadding};
15395
+ }
14447
15396
 
14448
- /**
14449
- * A nimble-styled breadcrumb item
14450
- */
14451
- class BreadcrumbItem extends BreadcrumbItem$1 {
14452
- }
14453
- const nimbleBreadcrumbItem = BreadcrumbItem.compose({
14454
- baseName: 'breadcrumb-item',
14455
- baseClass: BreadcrumbItem$1,
14456
- template: breadcrumbItemTemplate,
14457
- styles: styles$m,
14458
- separator: forwardSlash16X16.data
14459
- });
14460
- DesignSystem.getOrCreate()
14461
- .withPrefix('nimble')
14462
- .register(nimbleBreadcrumbItem());
15397
+ :host(.primary[content-hidden]) .control[disabled] {
15398
+ padding: 0px;
15399
+ }
15400
+ `));
14463
15401
 
14464
15402
  /**
14465
- * Behavior that will conditionally apply a stylesheet based on the element's
14466
- * appearance property
14467
- *
14468
- * @param value - The value of the appearance property
14469
- * @param styles - The styles to be applied when condition matches
14470
- *
14471
- * @public
15403
+ * A nimble-styled HTML button
14472
15404
  */
14473
- function appearanceBehavior(value, styles) {
14474
- return new PropertyStyleSheetBehavior('appearance', value, styles);
15405
+ class Button extends Button$1 {
15406
+ constructor() {
15407
+ super(...arguments);
15408
+ /**
15409
+ * @public
15410
+ * @remarks
15411
+ * HTML Attribute: appearance
15412
+ */
15413
+ this.appearance = ButtonAppearance.outline;
15414
+ /**
15415
+ * @public
15416
+ * @remarks
15417
+ * HTML Attribute: content-hidden
15418
+ */
15419
+ this.contentHidden = false;
15420
+ }
14475
15421
  }
14476
-
15422
+ __decorate([
15423
+ attr
15424
+ ], Button.prototype, "appearance", void 0);
15425
+ __decorate([
15426
+ attr({ attribute: 'content-hidden', mode: 'boolean' })
15427
+ ], Button.prototype, "contentHidden", void 0);
14477
15428
  /**
14478
- * Types of button appearance.
15429
+ * A function that returns a nimble-button registration for configuring the component with a DesignSystem.
15430
+ * Implements {@link @microsoft/fast-foundation#buttonTemplate}
15431
+ *
14479
15432
  * @public
15433
+ * @remarks
15434
+ * Generates HTML Element: \<nimble-button\>
15435
+ *
14480
15436
  */
14481
- const ButtonAppearance = {
14482
- outline: 'outline',
14483
- ghost: 'ghost',
14484
- block: 'block'
14485
- };
15437
+ const nimbleButton = Button.compose({
15438
+ baseName: 'button',
15439
+ baseClass: Button$1,
15440
+ template: buttonTemplate,
15441
+ styles: styles$n,
15442
+ shadowOptions: {
15443
+ delegatesFocus: true
15444
+ }
15445
+ });
15446
+ DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleButton());
14486
15447
 
14487
- const styles$l = css `
15448
+ const styles$m = css `
14488
15449
  ${display('inline-flex')}
14489
15450
 
14490
15451
  :host {
14491
- background-color: transparent;
14492
- height: ${controlHeight};
14493
- color: ${buttonLabelFontColor};
14494
15452
  font: ${buttonLabelFont};
15453
+ align-items: center;
14495
15454
  cursor: pointer;
14496
15455
  outline: none;
14497
- border: none;
14498
- box-sizing: border-box;
14499
- ${
14500
- /*
14501
- Not sure why but this is needed to get buttons with icons and buttons
14502
- without icons to align on the same line when the button is inline-flex
14503
- See: https://github.com/microsoft/fast/issues/5414
14504
- */ ''}
14505
- vertical-align: middle;
15456
+ user-select: none;
14506
15457
  }
14507
15458
 
14508
15459
  :host([disabled]) {
14509
- color: ${buttonLabelDisabledFontColor};
14510
15460
  cursor: default;
14511
15461
  }
14512
15462
 
14513
15463
  .control {
14514
- background-color: transparent;
14515
- height: 100%;
14516
- width: 100%;
14517
- border: ${borderWidth} solid transparent;
15464
+ width: calc(${controlHeight} / 2);
15465
+ height: calc(${controlHeight} / 2);
14518
15466
  box-sizing: border-box;
14519
- color: inherit;
14520
- border-radius: inherit;
14521
- fill: inherit;
15467
+ flex-shrink: 0;
15468
+ border: ${borderWidth} solid ${borderColor};
15469
+ padding: 2px;
14522
15470
  display: inline-flex;
14523
15471
  align-items: center;
14524
15472
  justify-content: center;
14525
- gap: 4px;
14526
- cursor: inherit;
14527
- font: inherit;
14528
- outline: none;
14529
- margin: 0;
14530
- padding: 0 ${standardPadding};
14531
15473
  transition: box-shadow ${smallDelay};
15474
+ ${
15475
+ /*
15476
+ * Firefox includes the line height in the outline height calculation (not sure if intended or accidental).
15477
+ * Set it to 0 to ensure the outline is just as high as the control.
15478
+ */ ''}
15479
+ line-height: 0;
15480
+ }
15481
+
15482
+ @media (prefers-reduced-motion) {
15483
+ .control {
15484
+ transition-duration: 0s;
15485
+ }
15486
+ }
15487
+
15488
+ :host([disabled]) .control {
15489
+ background-color: rgba(${borderRgbPartialColor}, 0.1);
15490
+ border-color: rgba(${borderRgbPartialColor}, 0.2);
15491
+ }
15492
+
15493
+ :host(:not([disabled]):not(:active):hover) .control {
15494
+ border-color: ${borderHoverColor};
15495
+ box-shadow: 0px 0px 0px ${borderWidth} ${borderHoverColor} inset;
15496
+ }
15497
+
15498
+ :host(${focusVisible}) .control {
15499
+ border-color: ${borderHoverColor};
15500
+ outline: 2px solid ${borderHoverColor};
15501
+ outline-offset: 2px;
15502
+ }
15503
+
15504
+ .label {
15505
+ font: inherit;
15506
+ color: ${bodyFontColor};
15507
+ padding-left: 1ch;
15508
+ cursor: inherit;
15509
+ }
15510
+
15511
+ :host([disabled]) .label {
15512
+ color: ${bodyDisabledFontColor};
15513
+ }
15514
+
15515
+ slot[name='checked-indicator'],
15516
+ slot[name='indeterminate-indicator'] {
15517
+ display: none;
15518
+ }
15519
+
15520
+ slot[name='checked-indicator'] svg {
15521
+ height: ${iconSize};
15522
+ width: ${iconSize};
15523
+ overflow: visible;
14532
15524
  }
14533
15525
 
14534
- :host([content-hidden]) .control {
14535
- width: ${controlHeight};
14536
- padding: 0px;
15526
+ :host(.checked:not(.indeterminate)) slot[name='checked-indicator'] {
15527
+ display: contents;
14537
15528
  }
14538
15529
 
14539
- @media (prefers-reduced-motion) {
14540
- .control {
14541
- transition-duration: 0s;
14542
- }
15530
+ slot[name='checked-indicator'] path {
15531
+ fill: ${borderColor};
14543
15532
  }
14544
15533
 
14545
- .control:hover {
14546
- box-shadow: 0px 0px 0px ${borderWidth} ${borderHoverColor} inset;
14547
- outline: none;
15534
+ :host([disabled]) slot[name='checked-indicator'] path {
15535
+ fill: rgba(${borderRgbPartialColor}, 0.3);
14548
15536
  }
14549
15537
 
14550
- .control${focusVisible} {
14551
- box-shadow: 0px 0px 0px ${borderWidth} ${borderHoverColor} inset;
14552
- outline: ${borderWidth} solid ${borderHoverColor};
14553
- outline-offset: -4px;
15538
+ slot[name='indeterminate-indicator'] svg {
15539
+ height: ${iconSize};
15540
+ width: ${iconSize};
15541
+ overflow: visible;
14554
15542
  }
14555
15543
 
14556
- .control:active {
14557
- box-shadow: none;
14558
- outline: none;
15544
+ :host(.indeterminate) slot[name='indeterminate-indicator'] {
15545
+ display: contents;
14559
15546
  }
14560
15547
 
14561
- .control[disabled] {
14562
- box-shadow: none;
14563
- outline: none;
15548
+ slot[name='indeterminate-indicator'] path {
15549
+ fill: ${borderColor};
14564
15550
  }
14565
15551
 
14566
- .content {
14567
- display: contents;
15552
+ :host([disabled]) slot[name='indeterminate-indicator'] path {
15553
+ fill: rgba(${borderRgbPartialColor}, 0.3);
14568
15554
  }
15555
+ `;
14569
15556
 
14570
- :host([content-hidden]) .content {
14571
- ${
14572
- /**
14573
- * Hide content visually while keeping it screen reader-accessible.
14574
- * Source: https://webaim.org/techniques/css/invisiblecontent/#techniques
14575
- * See discussion here: https://github.com/microsoft/fast/issues/5740#issuecomment-1068195035
14576
- */
14577
- ''}
14578
- display: inline-block;
14579
- height: 1px;
14580
- width: 1px;
14581
- position: absolute;
14582
- margin: -1px;
14583
- clip: rect(1px, 1px, 1px, 1px);
14584
- clip-path: inset(50%);
14585
- overflow: hidden;
14586
- padding: 0;
15557
+ /**
15558
+ * A nimble-styled checkbox control.
15559
+ */
15560
+ class Checkbox extends Checkbox$1 {
14587
15561
  }
15562
+ const nimbleCheckbox = Checkbox.compose({
15563
+ baseName: 'checkbox',
15564
+ baseClass: Checkbox$1,
15565
+ template: checkboxTemplate,
15566
+ styles: styles$m,
15567
+ checkedIndicator: check16X16.data,
15568
+ indeterminateIndicator: minus16X16.data
15569
+ });
15570
+ DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleCheckbox());
14588
15571
 
14589
- [part='start'] {
14590
- display: contents;
14591
- ${iconColor.cssCustomProperty}: ${buttonLabelFontColor};
15572
+ const errorTextTemplate = html `
15573
+ <div class="error-text" title="${x => x.errorText}" aria-live="polite">
15574
+ ${x => x.errorText}
15575
+ </div>
15576
+ `;
15577
+
15578
+ const template$4 = html `
15579
+ <template>
15580
+ <div class="icon" :innerHTML=${x => x.icon.data}></div>
15581
+ </template
15582
+ `;
15583
+
15584
+ const styles$l = css `
15585
+ ${display('inline-flex')}
15586
+
15587
+ :host {
15588
+ align-items: center;
15589
+ user-select: none;
15590
+ width: ${iconSize};
15591
+ height: ${iconSize};
14592
15592
  }
14593
15593
 
14594
- :host([disabled]) slot[name='start']::slotted(*) {
14595
- opacity: 0.3;
15594
+ .icon {
15595
+ width: 100%;
15596
+ height: 100%;
14596
15597
  }
14597
15598
 
14598
- [part='end'] {
14599
- display: contents;
14600
- ${iconColor.cssCustomProperty}: ${buttonLabelFontColor};
15599
+ :host(.fail) {
15600
+ ${iconColor.cssCustomProperty}: ${failColor};
14601
15601
  }
14602
15602
 
14603
- :host([disabled]) slot[name='end']::slotted(*) {
14604
- opacity: 0.3;
15603
+ :host(.warning) {
15604
+ ${iconColor.cssCustomProperty}: ${warningColor};
14605
15605
  }
14606
15606
 
14607
- :host([content-hidden]) [part='end'] {
14608
- display: none;
15607
+ :host(.pass) {
15608
+ ${iconColor.cssCustomProperty}: ${passColor};
14609
15609
  }
14610
- `
14611
- // prettier-ignore
14612
- .withBehaviors(appearanceBehavior(ButtonAppearance.outline, css `
14613
- .control {
14614
- background-color: transparent;
14615
- border-color: rgba(${actionRgbPartialColor}, 0.3);
14616
- }
14617
15610
 
14618
- .control:hover {
14619
- background-color: transparent;
14620
- border-color: ${borderHoverColor};
14621
- }
15611
+ .icon svg {
15612
+ fill: ${iconColor};
15613
+ width: 100%;
15614
+ height: 100%;
15615
+ }
15616
+ `;
14622
15617
 
14623
- .control${focusVisible} {
14624
- background-color: transparent;
14625
- border-color: ${borderHoverColor};
14626
- }
15618
+ /**
15619
+ * The base class for icon components
15620
+ */
15621
+ class Icon extends FoundationElement {
15622
+ constructor(icon) {
15623
+ super();
15624
+ this.icon = icon;
15625
+ }
15626
+ }
15627
+ const registerIcon = (baseName, iconClass) => {
15628
+ const composedIcon = iconClass.compose({
15629
+ baseName,
15630
+ template: template$4,
15631
+ styles: styles$l,
15632
+ baseClass: iconClass
15633
+ });
15634
+ DesignSystem.getOrCreate().withPrefix('nimble').register(composedIcon());
15635
+ };
14627
15636
 
14628
- .control:active {
14629
- background-color: ${fillSelectedColor};
14630
- border-color: ${fillSelectedColor};
14631
- }
15637
+ // AUTO-GENERATED FILE - DO NOT EDIT DIRECTLY
15638
+ /**
15639
+ * The icon component for the 'exclamationMark' icon
15640
+ */
15641
+ class IconExclamationMark extends Icon {
15642
+ constructor() {
15643
+ super(exclamationMark16X16);
15644
+ }
15645
+ }
15646
+ registerIcon('icon-exclamation-mark', IconExclamationMark);
14632
15647
 
14633
- .control[disabled] {
14634
- background-color: transparent;
14635
- border-color: rgba(${borderRgbPartialColor}, 0.2);
14636
- }
14637
- `), appearanceBehavior(ButtonAppearance.ghost, css `
14638
- .control {
14639
- background-color: transparent;
14640
- border-color: transparent;
14641
- }
15648
+ // AUTO-GENERATED FILE - DO NOT EDIT DIRECTLY
15649
+ /**
15650
+ * The icon component for the 'arrowExpanderDown' icon
15651
+ */
15652
+ class IconArrowExpanderDown extends Icon {
15653
+ constructor() {
15654
+ super(arrowExpanderDown16X16);
15655
+ }
15656
+ }
15657
+ registerIcon('icon-arrow-expander-down', IconArrowExpanderDown);
14642
15658
 
14643
- .control:hover {
14644
- background-color: transparent;
14645
- border-color: ${borderHoverColor};
14646
- }
15659
+ const styles$k = css `
15660
+ ${display('inline-flex')}
14647
15661
 
14648
- .control${focusVisible} {
14649
- background-color: transparent;
14650
- border-color: ${borderHoverColor};
14651
- }
15662
+ :host {
15663
+ box-sizing: border-box;
15664
+ color: ${bodyFontColor};
15665
+ font: ${bodyFont};
15666
+ height: ${controlHeight};
15667
+ position: relative;
15668
+ justify-content: center;
15669
+ user-select: none;
15670
+ min-width: 250px;
15671
+ outline: none;
15672
+ vertical-align: top;
15673
+ --ni-private-hover-indicator-width: calc(${borderWidth} + 1px);
15674
+ --ni-private-focus-indicator-width: 1px;
15675
+ --ni-private-indicator-lines-gap: 1px;
15676
+ }
14652
15677
 
14653
- .control:active {
14654
- background-color: ${fillSelectedColor};
14655
- border-color: ${fillSelectedColor};
14656
- }
15678
+ :host::before {
15679
+ content: '';
15680
+ position: absolute;
15681
+ bottom: calc(
15682
+ var(--ni-private-hover-indicator-width) +
15683
+ var(--ni-private-indicator-lines-gap)
15684
+ );
15685
+ width: 0px;
15686
+ height: 0px;
15687
+ justify-self: center;
15688
+ border-bottom: ${borderHoverColor}
15689
+ var(--ni-private-focus-indicator-width) solid;
15690
+ transition: width ${smallDelay} ease-in;
15691
+ }
14657
15692
 
14658
- .control[disabled] {
14659
- background-color: transparent;
14660
- border-color: transparent;
14661
- }
14662
- `), appearanceBehavior(ButtonAppearance.block, css `
14663
- .control {
14664
- background-color: rgba(${borderRgbPartialColor}, 0.1);
14665
- border-color: transparent;
14666
- }
15693
+ @media (prefers-reduced-motion) {
15694
+ :host::before {
15695
+ transition-duration: 0s;
15696
+ }
15697
+ }
14667
15698
 
14668
- .control:hover {
14669
- background-color: transparent;
14670
- border-color: ${borderHoverColor};
14671
- }
15699
+ :host(${focusVisible})::before {
15700
+ width: calc(100% - 8px);
15701
+ }
15702
+
15703
+ :host::after {
15704
+ content: '';
15705
+ position: absolute;
15706
+ bottom: calc(-1 * ${borderWidth});
15707
+ width: 0px;
15708
+ height: 0px;
15709
+ justify-self: center;
15710
+ border-bottom: ${borderHoverColor}
15711
+ var(--ni-private-hover-indicator-width) solid;
15712
+ transition: width ${smallDelay} ease-in;
15713
+ }
15714
+
15715
+ :host(.invalid)::after {
15716
+ border-bottom-color: ${failColor};
15717
+ }
14672
15718
 
14673
- .control${focusVisible} {
14674
- background-color: rgba(${borderRgbPartialColor}, 0.1);
14675
- border-color: ${borderHoverColor};
14676
- }
15719
+ @media (prefers-reduced-motion) {
15720
+ :host::after {
15721
+ transition-duration: 0s;
15722
+ }
15723
+ }
14677
15724
 
14678
- .control${focusVisible}:hover {
14679
- background-color: transparent;
14680
- }
15725
+ :host(:hover)::after,
15726
+ :host(${focusVisible})::after {
15727
+ width: 100%;
15728
+ }
14681
15729
 
14682
- .control:active {
14683
- background-color: ${fillSelectedColor};
14684
- border-color: ${fillSelectedColor};
14685
- }
15730
+ :host([disabled]:hover)::after {
15731
+ width: 0px;
15732
+ }
14686
15733
 
14687
- .control[disabled] {
14688
- background-color: rgba(${borderRgbPartialColor}, 0.1);
14689
- border-color: transparent;
14690
- }
14691
- `));
15734
+ .control {
15735
+ align-items: center;
15736
+ box-sizing: border-box;
15737
+ cursor: pointer;
15738
+ display: flex;
15739
+ min-height: 100%;
15740
+ width: 100%;
15741
+ border-bottom: ${borderWidth} solid ${bodyDisabledFontColor};
15742
+ background-color: transparent;
15743
+ padding-left: 8px;
15744
+ padding-bottom: 1px;
15745
+ }
14692
15746
 
14693
- // prettier-ignore
14694
- const styles$k = styles$l
14695
- .withBehaviors(appearanceBehavior(ButtonAppearance.outline, css `
14696
- :host(.primary) .control {
14697
- box-shadow: 0px 0px 0px ${borderWidth} rgba(${actionRgbPartialColor}, 0.3) inset;
14698
- }
15747
+ :host(.open:not(:hover)) .control {
15748
+ border-bottom-color: ${borderHoverColor};
15749
+ }
14699
15750
 
14700
- :host(.primary) .control:hover {
14701
- box-shadow: 0px 0px 0px ${borderWidth} ${borderHoverColor} inset;
14702
- }
15751
+ :host([disabled]) .control {
15752
+ cursor: default;
15753
+ color: ${bodyDisabledFontColor};
15754
+ border-color: rgba(${borderRgbPartialColor}, 0.1);
15755
+ }
14703
15756
 
14704
- :host(.primary) .control${focusVisible} {
14705
- box-shadow: 0px 0px 0px ${borderWidth} ${borderHoverColor} inset;
14706
- }
15757
+ .listbox {
15758
+ box-sizing: border-box;
15759
+ display: inline-flex;
15760
+ flex-direction: column;
15761
+ left: 0;
15762
+ overflow-y: auto;
15763
+ position: absolute;
15764
+ width: 100%;
15765
+ --ni-private-listbox-padding: ${smallPadding};
15766
+ max-height: calc(
15767
+ var(--ni-private-select-max-height) - 2 *
15768
+ var(--ni-private-listbox-padding)
15769
+ );
15770
+ z-index: 1;
15771
+ padding: var(--ni-private-listbox-padding);
15772
+ box-shadow: 0px 3px 3px ${popupBoxShadowColor};
15773
+ border: 1px solid ${popupBorderColor};
15774
+ background-color: ${applicationBackgroundColor};
15775
+ background-clip: padding-box;
15776
+ }
14707
15777
 
14708
- :host(.primary) .control:active {
14709
- box-shadow: none;
14710
- }
15778
+ .listbox[hidden] {
15779
+ display: none;
15780
+ }
14711
15781
 
14712
- :host(.primary) .control[disabled] {
14713
- box-shadow: none;
14714
- }
14715
- `), appearanceBehavior(ButtonAppearance.block, css `
14716
- :host(.primary) .control {
14717
- background-clip: padding-box;
14718
- border-color: rgba(${actionRgbPartialColor}, 0.3);
14719
- border-width: calc(2 * ${borderWidth});
14720
- padding: 0 calc(${standardPadding} - ${borderWidth});
14721
- }
15782
+ :host([open][position='above']) .listbox {
15783
+ border-bottom-left-radius: 0;
15784
+ border-bottom-right-radius: 0;
15785
+ }
14722
15786
 
14723
- :host(.primary[content-hidden]) .control {
14724
- padding: 0px;
14725
- }
15787
+ :host([open][position='below']) .listbox {
15788
+ border-top-left-radius: 0;
15789
+ border-top-right-radius: 0;
15790
+ }
14726
15791
 
14727
- :host(.primary) .control:hover {
14728
- border-color: ${borderHoverColor};
14729
- box-shadow: none;
14730
- }
15792
+ :host([open][position='above']) .listbox {
15793
+ bottom: ${controlHeight};
15794
+ }
14731
15795
 
14732
- :host(.primary) .control${focusVisible} {
14733
- background-clip: border-box;
14734
- border-color: ${borderHoverColor};
14735
- border-width: ${borderWidth};
14736
- box-shadow: 0px 0px 0px ${borderWidth} ${borderHoverColor} inset;
14737
- padding: 0 ${standardPadding};
14738
- }
15796
+ :host([open][position='below']) .listbox {
15797
+ top: calc(${controlHeight} + ${smallPadding});
15798
+ }
14739
15799
 
14740
- :host(.primary[content-hidden]) .control${focusVisible} {
14741
- padding: 0px;
14742
- }
15800
+ .selected-value {
15801
+ flex: auto;
15802
+ font-family: inherit;
15803
+ text-align: start;
15804
+ white-space: nowrap;
15805
+ text-overflow: ellipsis;
15806
+ overflow: hidden;
15807
+ }
14743
15808
 
14744
- :host(.primary) .control:active {
14745
- background-clip: border-box;
14746
- border-color: ${fillSelectedColor};
14747
- border-width: ${borderWidth};
14748
- box-shadow: none;
14749
- padding: 0 ${standardPadding};
14750
- }
15809
+ .indicator {
15810
+ flex: none;
15811
+ margin-inline-start: 1em;
15812
+ padding-right: 8px;
15813
+ display: flex;
15814
+ justify-content: center;
15815
+ align-items: center;
15816
+ }
14751
15817
 
14752
- :host(.primary[content-hidden]) .control:active {
14753
- padding: 0px;
14754
- }
15818
+ .indicator slot[name='indicator'] svg {
15819
+ width: ${iconSize};
15820
+ height: ${iconSize};
15821
+ fill: ${bodyFontColor};
15822
+ }
14755
15823
 
14756
- :host(.primary) .control[disabled] {
14757
- background-clip: border-box;
14758
- border-color: transparent;
14759
- border-width: ${borderWidth};
14760
- box-shadow: none;
14761
- padding: 0 ${standardPadding};
14762
- }
15824
+ :host([disabled]) .indicator slot[name='indicator'] svg {
15825
+ fill: ${bodyDisabledFontColor};
15826
+ }
14763
15827
 
14764
- :host(.primary[content-hidden]) .control[disabled] {
14765
- padding: 0px;
14766
- }
14767
- `));
15828
+ slot[name='listbox'] {
15829
+ display: none;
15830
+ width: 100%;
15831
+ }
14768
15832
 
14769
- /**
14770
- * A nimble-styled HTML button
14771
- */
14772
- class Button extends Button$1 {
14773
- constructor() {
14774
- super(...arguments);
14775
- /**
14776
- * @public
14777
- * @remarks
14778
- * HTML Attribute: appearance
14779
- */
14780
- this.appearance = ButtonAppearance.outline;
14781
- /**
14782
- * @public
14783
- * @remarks
14784
- * HTML Attribute: content-hidden
14785
- */
14786
- this.contentHidden = false;
14787
- }
15833
+ :host([open]) slot[name='listbox'] {
15834
+ display: flex;
15835
+ position: absolute;
14788
15836
  }
14789
- __decorate([
14790
- attr
14791
- ], Button.prototype, "appearance", void 0);
14792
- __decorate([
14793
- attr({ attribute: 'content-hidden', mode: 'boolean' })
14794
- ], Button.prototype, "contentHidden", void 0);
14795
- /**
14796
- * A function that returns a nimble-button registration for configuring the component with a DesignSystem.
14797
- * Implements {@link @microsoft/fast-foundation#buttonTemplate}
14798
- *
14799
- * @public
14800
- * @remarks
14801
- * Generates HTML Element: \<nimble-button\>
14802
- *
14803
- */
14804
- const nimbleButton = Button.compose({
14805
- baseName: 'button',
14806
- baseClass: Button$1,
14807
- template: buttonTemplate,
14808
- styles: styles$k,
14809
- shadowOptions: {
14810
- delegatesFocus: true
14811
- }
14812
- });
14813
- DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleButton());
14814
15837
 
14815
- const styles$j = css `
14816
- ${display('inline-flex')}
15838
+ .end {
15839
+ margin-inline-start: auto;
15840
+ }
14817
15841
 
14818
- :host {
14819
- font: ${buttonLabelFont};
14820
- align-items: center;
14821
- cursor: pointer;
14822
- outline: none;
14823
- user-select: none;
15842
+ ::slotted([role='option']),
15843
+ ::slotted(option) {
15844
+ flex: none;
14824
15845
  }
15846
+ `;
14825
15847
 
14826
- :host([disabled]) {
14827
- cursor: default;
15848
+ const styles$j = css `
15849
+ .error-icon {
15850
+ display: none;
14828
15851
  }
14829
15852
 
14830
- .control {
14831
- width: calc(${controlHeight} / 2);
14832
- height: calc(${controlHeight} / 2);
14833
- box-sizing: border-box;
14834
- flex-shrink: 0;
14835
- border: ${borderWidth} solid ${borderColor};
14836
- padding: 2px;
15853
+ :host(.invalid) .error-icon {
14837
15854
  display: inline-flex;
14838
- align-items: center;
14839
- justify-content: center;
14840
- transition: box-shadow ${smallDelay};
14841
- ${
14842
- /*
14843
- * Firefox includes the line height in the outline height calculation (not sure if intended or accidental).
14844
- * Set it to 0 to ensure the outline is just as high as the control.
14845
- */ ''}
14846
- line-height: 0;
15855
+ width: ${iconSize};
15856
+ height: ${iconSize};
15857
+ flex: none;
14847
15858
  }
14848
15859
 
14849
- @media (prefers-reduced-motion) {
14850
- .control {
14851
- transition-duration: 0s;
14852
- }
15860
+ .error-text {
15861
+ display: none;
14853
15862
  }
14854
15863
 
14855
- :host([disabled]) .control {
14856
- background-color: rgba(${borderRgbPartialColor}, 0.1);
14857
- border-color: rgba(${borderRgbPartialColor}, 0.2);
15864
+ :host(.invalid) .error-text {
15865
+ display: block;
15866
+ font: ${errorTextFont};
15867
+ color: ${failColor};
15868
+ width: 100%;
15869
+ position: absolute;
15870
+ top: ${controlHeight};
15871
+ left: 0px;
15872
+ overflow: hidden;
15873
+ text-overflow: ellipsis;
15874
+ white-space: nowrap;
14858
15875
  }
14859
15876
 
14860
- :host(:not([disabled]):not(:active):hover) .control {
14861
- border-color: ${borderHoverColor};
14862
- box-shadow: 0px 0px 0px ${borderWidth} ${borderHoverColor} inset;
15877
+ :host(.invalid[readonly]:not([disabled])) .error-text {
15878
+ top: calc(${controlHeight} - ${borderWidth});
14863
15879
  }
14864
15880
 
14865
- :host(${focusVisible}) .control {
14866
- border-color: ${borderHoverColor};
14867
- outline: 2px solid ${borderHoverColor};
14868
- outline-offset: 2px;
15881
+ :host(.invalid) .error-text:empty {
15882
+ display: none;
14869
15883
  }
15884
+ `;
14870
15885
 
14871
- .label {
14872
- font: inherit;
14873
- color: ${bodyFontColor};
14874
- padding-left: 1ch;
14875
- cursor: inherit;
15886
+ const styles$i = css `
15887
+ ${styles$k}
15888
+ ${styles$j}
15889
+
15890
+ :host {
15891
+ --ni-private-hover-bottom-border-width: 2px;
15892
+ --ni-private-bottom-border-width: 1px;
14876
15893
  }
14877
15894
 
14878
- :host([disabled]) .label {
15895
+ :host([disabled]) *,
15896
+ :host([disabled]) {
15897
+ user-select: none;
14879
15898
  color: ${bodyDisabledFontColor};
14880
15899
  }
14881
15900
 
14882
- slot[name='checked-indicator'],
14883
- slot[name='indeterminate-indicator'] {
14884
- display: none;
15901
+ .control {
15902
+ bottom-border-width: var(--ni-private-bottom-border-width);
14885
15903
  }
14886
15904
 
14887
- slot[name='checked-indicator'] svg {
14888
- height: ${iconSize};
14889
- width: ${iconSize};
14890
- overflow: visible;
15905
+ :host(.invalid) .control {
15906
+ border-bottom: var(--ni-private-bottom-border-width) solid ${failColor};
14891
15907
  }
14892
15908
 
14893
- :host(.checked:not(.indeterminate)) slot[name='checked-indicator'] {
14894
- display: contents;
15909
+ :host([disabled]) .control {
15910
+ border-color: rgba(${borderRgbPartialColor}, 0.1);
14895
15911
  }
14896
15912
 
14897
- slot[name='checked-indicator'] path {
14898
- fill: ${borderColor};
15913
+ :host(.invalid[disabled]) .control {
15914
+ border-color: ${failColor};
14899
15915
  }
14900
15916
 
14901
- :host([disabled]) slot[name='checked-indicator'] path {
14902
- fill: rgba(${borderRgbPartialColor}, 0.3);
15917
+ .selected-value {
15918
+ -webkit-appearance: none;
15919
+ background: transparent;
15920
+ border: none;
15921
+ color: inherit;
15922
+ margin: auto 0;
15923
+ width: 100%;
15924
+ font-size: inherit;
15925
+ padding-left: 0px;
14903
15926
  }
14904
15927
 
14905
- slot[name='indeterminate-indicator'] svg {
14906
- height: ${iconSize};
14907
- width: ${iconSize};
14908
- overflow: visible;
15928
+ .selected-value:hover,
15929
+ .selected-value:disabled,
15930
+ .selected-value:active,
15931
+ .selected-value${focusVisible} {
15932
+ outline: none;
14909
15933
  }
14910
15934
 
14911
- :host(.indeterminate) slot[name='indeterminate-indicator'] {
14912
- display: contents;
15935
+ [part='indicator'] {
15936
+ display: none;
14913
15937
  }
14914
15938
 
14915
- slot[name='indeterminate-indicator'] path {
14916
- fill: ${borderColor};
15939
+ .end-slot-container {
15940
+ display: flex;
15941
+ align-items: baseline;
15942
+ padding-right: ${smallPadding};
14917
15943
  }
14918
15944
 
14919
- :host([disabled]) slot[name='indeterminate-indicator'] path {
14920
- fill: rgba(${borderRgbPartialColor}, 0.3);
15945
+ .separator {
15946
+ display: inline;
15947
+ width: 2px;
15948
+ border-right: 2px solid rgba(${borderRgbPartialColor}, 0.15);
15949
+ height: calc(${controlHeight} - 12px);
15950
+ align-self: center;
15951
+ padding-left: 4px;
15952
+ }
15953
+
15954
+ .dropdown-button {
15955
+ ${controlHeight.cssCustomProperty}: 24px;
15956
+ margin-left: ${smallPadding};
15957
+ }
15958
+
15959
+ :host([disabled]) .dropdown-icon {
15960
+ fill: ${bodyDisabledFontColor};
15961
+ }
15962
+
15963
+ :host(:empty) .listbox {
15964
+ display: none;
14921
15965
  }
14922
15966
  `;
14923
15967
 
14924
15968
  /**
14925
- * A nimble-styled checkbox control.
15969
+ * A nimble-styed HTML combobox
14926
15970
  */
14927
- class Checkbox extends Checkbox$1 {
15971
+ class Combobox extends Combobox$1 {
15972
+ constructor() {
15973
+ super(...arguments);
15974
+ this.focusOutHandler = () => {
15975
+ this.open = false;
15976
+ };
15977
+ }
15978
+ // Workaround for https://github.com/microsoft/fast/issues/5123
15979
+ setPositioning() {
15980
+ if (!this.$fastController.isConnected) {
15981
+ // Don't call setPositioning() until we're connected,
15982
+ // since this.forcedPosition isn't initialized yet.
15983
+ return;
15984
+ }
15985
+ super.setPositioning();
15986
+ }
15987
+ // Workaround for https://github.com/microsoft/fast/issues/5773
15988
+ slottedOptionsChanged(prev, next) {
15989
+ const value = this.value;
15990
+ super.slottedOptionsChanged(prev, next);
15991
+ if (value) {
15992
+ this.value = value;
15993
+ }
15994
+ }
15995
+ connectedCallback() {
15996
+ super.connectedCallback();
15997
+ // Call setPositioning() after this.forcedPosition is initialized.
15998
+ this.setPositioning();
15999
+ this.updateInputAriaLabel();
16000
+ this.addEventListener('focusout', this.focusOutHandler);
16001
+ }
16002
+ disconnectedCallback() {
16003
+ this.removeEventListener('focusout', this.focusOutHandler);
16004
+ }
16005
+ toggleButtonClickHandler(e) {
16006
+ e.stopImmediatePropagation();
16007
+ }
16008
+ toggleButtonChangeHandler(e) {
16009
+ this.open = this.dropdownButton.checked;
16010
+ e.stopImmediatePropagation();
16011
+ }
16012
+ toggleButtonKeyDownHandler(e) {
16013
+ switch (e.key) {
16014
+ case keyArrowUp:
16015
+ case keyArrowDown:
16016
+ case keySpace:
16017
+ case keyEnter:
16018
+ this.open = true;
16019
+ this.stopPropagation(e);
16020
+ return false;
16021
+ default:
16022
+ return true;
16023
+ }
16024
+ }
16025
+ filterOptions() {
16026
+ super.filterOptions();
16027
+ const enabledOptions = this.filteredOptions.filter(o => !o.disabled);
16028
+ this.filteredOptions = enabledOptions;
16029
+ }
16030
+ openChanged() {
16031
+ super.openChanged();
16032
+ if (this.dropdownButton) {
16033
+ this.dropdownButton.checked = this.open;
16034
+ }
16035
+ }
16036
+ // Workaround for https://github.com/microsoft/fast/issues/6041.
16037
+ ariaLabelChanged(_oldValue, _newValue) {
16038
+ this.updateInputAriaLabel();
16039
+ }
16040
+ updateInputAriaLabel() {
16041
+ const inputElement = this.shadowRoot?.querySelector('.selected-value');
16042
+ if (this.ariaLabel) {
16043
+ inputElement?.setAttribute('aria-label', this.ariaLabel);
16044
+ }
16045
+ else {
16046
+ inputElement?.removeAttribute('aria-label');
16047
+ }
16048
+ }
14928
16049
  }
14929
- const nimbleCheckbox = Checkbox.compose({
14930
- baseName: 'checkbox',
14931
- baseClass: Checkbox$1,
14932
- template: checkboxTemplate,
14933
- styles: styles$j,
14934
- checkedIndicator: check16X16.data,
14935
- indeterminateIndicator: minus16X16.data
16050
+ __decorate([
16051
+ observable
16052
+ ], Combobox.prototype, "dropdownButton", void 0);
16053
+ __decorate([
16054
+ attr({ attribute: 'error-text' })
16055
+ ], Combobox.prototype, "errorText", void 0);
16056
+ const nimbleCombobox = Combobox.compose({
16057
+ baseName: 'combobox',
16058
+ baseClass: Combobox$1,
16059
+ template: comboboxTemplate,
16060
+ styles: styles$i,
16061
+ shadowOptions: {
16062
+ delegatesFocus: true
16063
+ },
16064
+ end: html `
16065
+ <div class="end-slot-container">
16066
+ <nimble-icon-exclamation-mark
16067
+ class="error-icon fail"
16068
+ ></nimble-icon-exclamation-mark>
16069
+ <div class="separator"></div>
16070
+ <nimble-toggle-button
16071
+ ${ref('dropdownButton')}
16072
+ appearance="ghost"
16073
+ ?checked="${x => x.open}"
16074
+ ?disabled="${x => x.disabled}"
16075
+ content-hidden="true"
16076
+ @click="${(x, c) => x.toggleButtonClickHandler(c.event)}"
16077
+ @change="${(x, c) => x.toggleButtonChangeHandler(c.event)}"
16078
+ @keydown="${(x, c) => x.toggleButtonKeyDownHandler(c.event)}"
16079
+ class="dropdown-button"
16080
+ part="button"
16081
+ aria-haspopup="true"
16082
+ aria-expanded="${x => x.open}"
16083
+ >
16084
+ <nimble-icon-arrow-expander-down
16085
+ slot="start"
16086
+ class="dropdown-icon"
16087
+ >
16088
+ </nimble-icon-arrow-expander-down>
16089
+ </nimble-toggle-button>
16090
+ </div>
16091
+ ${errorTextTemplate}
16092
+ `
14936
16093
  });
14937
- DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleCheckbox());
16094
+ DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleCombobox());
14938
16095
 
14939
16096
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
14940
16097
 
@@ -16025,7 +17182,7 @@
16025
17182
  slideOutOptions
16026
17183
  };
16027
17184
 
16028
- const styles$i = css `
17185
+ const styles$h = css `
16029
17186
  ${display('block')}
16030
17187
 
16031
17188
  :host {
@@ -16289,116 +17446,57 @@
16289
17446
  if (!this.hidden) {
16290
17447
  this.animateOpenClose(false);
16291
17448
  }
16292
- else {
16293
- this.state = DrawerState.closed;
16294
- }
16295
- }
16296
- animateOpenClose(drawerOpening) {
16297
- const options = {
16298
- ...(drawerOpening
16299
- ? animationConfig.slideInOptions
16300
- : animationConfig.slideOutOptions),
16301
- duration: this.animationDurationMilliseconds
16302
- };
16303
- const drawerKeyframes = this.location === DrawerLocation.right
16304
- ? animationConfig.slideRightKeyframes
16305
- : animationConfig.slideLeftKeyframes;
16306
- const dialogAnimation = new dist.AnimateTo(this.dialog, undefined, options);
16307
- dialogAnimation.addKeyframes(drawerKeyframes);
16308
- const animations = [dialogAnimation];
16309
- const overlay = this.shadowRoot?.querySelector('.overlay');
16310
- if (overlay) {
16311
- const overlayAnimation = new dist.AnimateTo(overlay, undefined, options);
16312
- overlayAnimation.addKeyframes(animationConfig.fadeOverlayKeyframes);
16313
- animations.push(overlayAnimation);
16314
- }
16315
- const animationGroup = new dist.AnimateGroup(animations);
16316
- animationGroup.onFinish = () => {
16317
- this.state = drawerOpening
16318
- ? DrawerState.opened
16319
- : DrawerState.closed;
16320
- };
16321
- this.animationGroup = animationGroup;
16322
- animationGroup.play();
16323
- }
16324
- cancelCurrentAnimation() {
16325
- this.animationGroup?.cancel();
16326
- }
16327
- }
16328
- __decorate([
16329
- attr
16330
- ], Drawer.prototype, "location", void 0);
16331
- __decorate([
16332
- attr
16333
- ], Drawer.prototype, "state", void 0);
16334
- __decorate([
16335
- attr({ attribute: 'prevent-dismiss', mode: 'boolean' })
16336
- ], Drawer.prototype, "preventDismiss", void 0);
16337
- const nimbleDrawer = Drawer.compose({
16338
- baseName: 'drawer',
16339
- template: dialogTemplate,
16340
- styles: styles$i
16341
- });
16342
- DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleDrawer());
16343
-
16344
- const template$4 = html `
16345
- <template>
16346
- <div class="icon" :innerHTML=${x => x.icon.data}></div>
16347
- </template
16348
- `;
16349
-
16350
- const styles$h = css `
16351
- ${display('inline-flex')}
16352
-
16353
- :host {
16354
- align-items: center;
16355
- user-select: none;
16356
- width: ${iconSize};
16357
- height: ${iconSize};
16358
- }
16359
-
16360
- .icon {
16361
- width: 100%;
16362
- height: 100%;
16363
- }
16364
-
16365
- :host(.fail) {
16366
- ${iconColor.cssCustomProperty}: ${failColor};
16367
- }
16368
-
16369
- :host(.warning) {
16370
- ${iconColor.cssCustomProperty}: ${warningColor};
16371
- }
16372
-
16373
- :host(.pass) {
16374
- ${iconColor.cssCustomProperty}: ${passColor};
16375
- }
16376
-
16377
- .icon svg {
16378
- fill: ${iconColor};
16379
- width: 100%;
16380
- height: 100%;
16381
- }
16382
- `;
16383
-
16384
- /**
16385
- * The base class for icon components
16386
- */
16387
- class Icon extends FoundationElement {
16388
- constructor(icon) {
16389
- super();
16390
- this.icon = icon;
17449
+ else {
17450
+ this.state = DrawerState.closed;
17451
+ }
17452
+ }
17453
+ animateOpenClose(drawerOpening) {
17454
+ const options = {
17455
+ ...(drawerOpening
17456
+ ? animationConfig.slideInOptions
17457
+ : animationConfig.slideOutOptions),
17458
+ duration: this.animationDurationMilliseconds
17459
+ };
17460
+ const drawerKeyframes = this.location === DrawerLocation.right
17461
+ ? animationConfig.slideRightKeyframes
17462
+ : animationConfig.slideLeftKeyframes;
17463
+ const dialogAnimation = new dist.AnimateTo(this.dialog, undefined, options);
17464
+ dialogAnimation.addKeyframes(drawerKeyframes);
17465
+ const animations = [dialogAnimation];
17466
+ const overlay = this.shadowRoot?.querySelector('.overlay');
17467
+ if (overlay) {
17468
+ const overlayAnimation = new dist.AnimateTo(overlay, undefined, options);
17469
+ overlayAnimation.addKeyframes(animationConfig.fadeOverlayKeyframes);
17470
+ animations.push(overlayAnimation);
17471
+ }
17472
+ const animationGroup = new dist.AnimateGroup(animations);
17473
+ animationGroup.onFinish = () => {
17474
+ this.state = drawerOpening
17475
+ ? DrawerState.opened
17476
+ : DrawerState.closed;
17477
+ };
17478
+ this.animationGroup = animationGroup;
17479
+ animationGroup.play();
17480
+ }
17481
+ cancelCurrentAnimation() {
17482
+ this.animationGroup?.cancel();
16391
17483
  }
16392
17484
  }
16393
- const registerIcon = (baseName, iconClass) => {
16394
- const composedIcon = iconClass.compose({
16395
- baseName,
16396
- template: template$4,
16397
- styles: styles$h,
16398
- baseClass: iconClass
16399
- });
16400
- DesignSystem.getOrCreate().withPrefix('nimble').register(composedIcon());
16401
- };
17485
+ __decorate([
17486
+ attr
17487
+ ], Drawer.prototype, "location", void 0);
17488
+ __decorate([
17489
+ attr
17490
+ ], Drawer.prototype, "state", void 0);
17491
+ __decorate([
17492
+ attr({ attribute: 'prevent-dismiss', mode: 'boolean' })
17493
+ ], Drawer.prototype, "preventDismiss", void 0);
17494
+ const nimbleDrawer = Drawer.compose({
17495
+ baseName: 'drawer',
17496
+ template: dialogTemplate,
17497
+ styles: styles$h
17498
+ });
17499
+ DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleDrawer());
16402
17500
 
16403
17501
  // AUTO-GENERATED FILE - DO NOT EDIT DIRECTLY
16404
17502
  /**
@@ -16422,17 +17520,6 @@
16422
17520
  }
16423
17521
  registerIcon('icon-arrow-down-right-and-arrow-up-left', IconArrowDownRightAndArrowUpLeft);
16424
17522
 
16425
- // AUTO-GENERATED FILE - DO NOT EDIT DIRECTLY
16426
- /**
16427
- * The icon component for the 'arrowExpanderDown' icon
16428
- */
16429
- class IconArrowExpanderDown extends Icon {
16430
- constructor() {
16431
- super(arrowExpanderDown16X16);
16432
- }
16433
- }
16434
- registerIcon('icon-arrow-expander-down', IconArrowExpanderDown);
16435
-
16436
17523
  // AUTO-GENERATED FILE - DO NOT EDIT DIRECTLY
16437
17524
  /**
16438
17525
  * The icon component for the 'arrowExpanderLeft' icon
@@ -17060,17 +18147,6 @@
17060
18147
  }
17061
18148
  registerIcon('icon-electronic-chip-zoomed', IconElectronicChipZoomed);
17062
18149
 
17063
- // AUTO-GENERATED FILE - DO NOT EDIT DIRECTLY
17064
- /**
17065
- * The icon component for the 'exclamationMark' icon
17066
- */
17067
- class IconExclamationMark extends Icon {
17068
- constructor() {
17069
- super(exclamationMark16X16);
17070
- }
17071
- }
17072
- registerIcon('icon-exclamation-mark', IconExclamationMark);
17073
-
17074
18150
  // AUTO-GENERATED FILE - DO NOT EDIT DIRECTLY
17075
18151
  /**
17076
18152
  * The icon component for the 'eye' icon
@@ -18123,7 +19199,7 @@
18123
19199
  `;
18124
19200
 
18125
19201
  const styles$d = css `
18126
- ${styles$l}
19202
+ ${styles$o}
18127
19203
 
18128
19204
  .control[aria-pressed='true'] {
18129
19205
  background-color: ${fillSelectedColor};
@@ -18241,7 +19317,7 @@
18241
19317
  <${context.tagFor(ToggleButton)}
18242
19318
  part="button"
18243
19319
  appearance="${x => x.appearance}"
18244
- content-hidden="${x => x.contentHidden}"
19320
+ ?content-hidden="${x => x.contentHidden}"
18245
19321
  ?checked="${x => x.open}"
18246
19322
  ?disabled="${x => x.disabled}"
18247
19323
  aria-haspopup="true"
@@ -18606,6 +19682,7 @@
18606
19682
  .root::after {
18607
19683
  content: '';
18608
19684
  position: absolute;
19685
+ left: 0px;
18609
19686
  bottom: calc(-1 * ${borderWidth});
18610
19687
  width: 0px;
18611
19688
  height: 0px;
@@ -18721,144 +19798,7 @@
18721
19798
  DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleNumberField());
18722
19799
 
18723
19800
  const styles$a = css `
18724
- ${display('inline-flex')}
18725
-
18726
- :host {
18727
- box-sizing: border-box;
18728
- color: ${bodyFontColor};
18729
- font: ${bodyFont};
18730
- height: ${controlHeight};
18731
- position: relative;
18732
- user-select: none;
18733
- min-width: 250px;
18734
- outline: none;
18735
- vertical-align: top;
18736
- }
18737
-
18738
- .listbox {
18739
- box-sizing: border-box;
18740
- display: inline-flex;
18741
- flex-direction: column;
18742
- left: 0;
18743
- overflow-y: auto;
18744
- position: absolute;
18745
- width: 100%;
18746
- --ni-private-listbox-padding: 4px;
18747
- max-height: calc(
18748
- var(--ni-private-select-max-height) - 2 *
18749
- var(--ni-private-listbox-padding)
18750
- );
18751
- z-index: 1;
18752
- padding: var(--ni-private-listbox-padding);
18753
- box-shadow: 0px 3px 3px ${popupBoxShadowColor};
18754
- border: 1px solid ${popupBorderColor};
18755
- background-color: ${applicationBackgroundColor};
18756
- background-clip: padding-box;
18757
- }
18758
-
18759
- .listbox[hidden] {
18760
- display: none;
18761
- }
18762
-
18763
- .control {
18764
- align-items: center;
18765
- box-sizing: border-box;
18766
- cursor: pointer;
18767
- display: flex;
18768
- min-height: 100%;
18769
- width: 100%;
18770
- border-bottom: ${borderWidth} solid ${bodyDisabledFontColor};
18771
- background-color: transparent;
18772
- padding-left: 8px;
18773
- padding-bottom: 1px;
18774
- }
18775
-
18776
- :host([disabled]) .control {
18777
- cursor: default;
18778
- }
18779
-
18780
- :host(.open:not(:hover)) .control {
18781
- border-bottom: ${borderWidth} solid ${borderHoverColor};
18782
- transition: border-bottom ${smallDelay}, padding-bottom ${smallDelay};
18783
- }
18784
-
18785
- :host(:hover) .control {
18786
- border-bottom: 2px solid ${borderHoverColor};
18787
- padding-bottom: 0px;
18788
- transition: border-bottom ${smallDelay}, padding-bottom ${smallDelay};
18789
- }
18790
-
18791
- :host([disabled]) .control,
18792
- :host([disabled]) .control:hover {
18793
- border-bottom: ${borderWidth} solid ${bodyDisabledFontColor};
18794
- padding-bottom: 1px;
18795
- color: ${bodyDisabledFontColor};
18796
- }
18797
-
18798
- :host([open][position='above']) .listbox {
18799
- border-bottom-left-radius: 0;
18800
- border-bottom-right-radius: 0;
18801
- }
18802
-
18803
- :host([open][position='below']) .listbox {
18804
- border-top-left-radius: 0;
18805
- border-top-right-radius: 0;
18806
- }
18807
-
18808
- :host([open][position='above']) .listbox {
18809
- bottom: ${controlHeight};
18810
- }
18811
-
18812
- :host([open][position='below']) .listbox {
18813
- top: calc(${controlHeight} + ${smallPadding});
18814
- }
18815
-
18816
- .selected-value {
18817
- flex: 1 1 auto;
18818
- font-family: inherit;
18819
- text-align: start;
18820
- white-space: nowrap;
18821
- text-overflow: ellipsis;
18822
- overflow: hidden;
18823
- }
18824
-
18825
- .indicator {
18826
- flex: 0 0 auto;
18827
- margin-inline-start: 1em;
18828
- padding-right: 8px;
18829
- display: flex;
18830
- justify-content: center;
18831
- align-items: center;
18832
- }
18833
-
18834
- .indicator slot[name='indicator'] svg {
18835
- width: ${iconSize};
18836
- height: ${iconSize};
18837
- fill: ${bodyFontColor};
18838
- }
18839
-
18840
- :host([disabled]) .indicator slot[name='indicator'] svg {
18841
- fill: ${bodyDisabledFontColor};
18842
- }
18843
-
18844
- slot[name='listbox'] {
18845
- display: none;
18846
- width: 100%;
18847
- }
18848
-
18849
- :host([open]) slot[name='listbox'] {
18850
- display: flex;
18851
- position: absolute;
18852
- }
18853
-
18854
- .end {
18855
- margin-inline-start: auto;
18856
- }
18857
-
18858
- ::slotted([role='option']),
18859
- ::slotted(option) {
18860
- flex: 0 0 auto;
18861
- }
19801
+ ${styles$k}
18862
19802
  `;
18863
19803
 
18864
19804
  /**
@@ -19497,6 +20437,7 @@
19497
20437
 
19498
20438
  const styles$3 = css `
19499
20439
  ${display('inline-block')}
20440
+ ${styles$j}
19500
20441
 
19501
20442
  :host {
19502
20443
  font: ${bodyFont};
@@ -19538,10 +20479,6 @@
19538
20479
  gap: calc(${standardPadding} / 2);
19539
20480
  }
19540
20481
 
19541
- :host(.invalid) .root {
19542
- border-bottom-color: ${failColor};
19543
- }
19544
-
19545
20482
  :host([readonly]) .root {
19546
20483
  border-color: rgba(${borderRgbPartialColor}, 0.1);
19547
20484
  }
@@ -19550,6 +20487,10 @@
19550
20487
  border-color: rgba(${borderRgbPartialColor}, 0.1);
19551
20488
  }
19552
20489
 
20490
+ :host(.invalid) .root {
20491
+ border-bottom-color: ${failColor};
20492
+ }
20493
+
19553
20494
  .root:focus-within {
19554
20495
  border-bottom-color: ${borderHoverColor};
19555
20496
  }
@@ -19644,6 +20585,7 @@
19644
20585
  [part='end']::after {
19645
20586
  content: '';
19646
20587
  position: absolute;
20588
+ left: 0px;
19647
20589
  bottom: calc(-1 * ${borderWidth});
19648
20590
  width: 0px;
19649
20591
  height: 0px;
@@ -19671,52 +20613,6 @@
19671
20613
  width: 0px;
19672
20614
  }
19673
20615
 
19674
- .error-content {
19675
- display: none;
19676
- }
19677
-
19678
- :host(.invalid) .error-content {
19679
- display: contents;
19680
- }
19681
-
19682
- :host(.invalid) .error-content svg {
19683
- height: ${iconSize};
19684
- width: ${iconSize};
19685
- flex: none;
19686
- }
19687
-
19688
- :host(.invalid) .error-content path {
19689
- fill: ${failColor};
19690
- }
19691
-
19692
- :host([disabled]) .error-content path {
19693
- fill: ${bodyDisabledFontColor};
19694
- }
19695
-
19696
- .errortext {
19697
- display: none;
19698
- }
19699
-
19700
- :host(.invalid) .errortext {
19701
- display: block;
19702
- font: ${errorTextFont};
19703
- color: ${failColor};
19704
- width: 100%;
19705
- position: absolute;
19706
- top: ${controlHeight};
19707
- overflow: hidden;
19708
- text-overflow: ellipsis;
19709
- white-space: nowrap;
19710
- }
19711
-
19712
- :host(.invalid) .error-text:empty {
19713
- display: none;
19714
- }
19715
-
19716
- :host([disabled]) .errortext {
19717
- color: ${bodyDisabledFontColor};
19718
- }
19719
-
19720
20616
  [part='actions'] {
19721
20617
  display: contents;
19722
20618
  }
@@ -19771,10 +20667,6 @@
19771
20667
  .control {
19772
20668
  height: var(--ni-private-height-within-border);
19773
20669
  }
19774
-
19775
- :host(.invalid) .errortext {
19776
- top: calc(${controlHeight} - ${borderWidth});
19777
- }
19778
20670
  `), appearanceBehavior(TextFieldAppearance.frameless, css `
19779
20671
  .control {
19780
20672
  padding-left: ${borderWidth};
@@ -19833,18 +20725,13 @@
19833
20725
  delegatesFocus: true
19834
20726
  },
19835
20727
  end: html `
19836
- <span class="error-content">${exclamationMark16X16.data}</span>
20728
+ <nimble-icon-exclamation-mark
20729
+ class="error-icon fail"
20730
+ ></nimble-icon-exclamation-mark>
19837
20731
  <span part="actions">
19838
20732
  <slot name="actions"></slot>
19839
20733
  </span>
19840
- <div
19841
- id="errortext"
19842
- class="errortext error-content"
19843
- title="${x => x.errorText}"
19844
- aria-live="polite"
19845
- >
19846
- ${x => x.errorText}
19847
- </div>
20734
+ ${errorTextTemplate}
19848
20735
  `
19849
20736
  });
19850
20737
  DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleTextField());