@material/web 1.0.2-nightly.f7a66a8.0 → 1.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.
- package/checkbox/internal/checkbox.d.ts +1 -0
- package/checkbox/internal/checkbox.js +6 -1
- package/checkbox/internal/checkbox.js.map +1 -1
- package/chips/internal/_shared.scss +16 -3
- package/chips/internal/_trailing-icon.scss +2 -1
- package/chips/internal/assist-styles.css.js +1 -1
- package/chips/internal/assist-styles.css.js.map +1 -1
- package/chips/internal/chip-set.js +2 -6
- package/chips/internal/chip-set.js.map +1 -1
- package/chips/internal/chip.d.ts +8 -0
- package/chips/internal/chip.js +17 -2
- package/chips/internal/chip.js.map +1 -1
- package/chips/internal/filter-chip.d.ts +8 -0
- package/chips/internal/filter-chip.js +17 -4
- package/chips/internal/filter-chip.js.map +1 -1
- package/chips/internal/filter-styles.css.js +1 -1
- package/chips/internal/filter-styles.css.js.map +1 -1
- package/chips/internal/input-styles.css.js +1 -1
- package/chips/internal/input-styles.css.js.map +1 -1
- package/chips/internal/shared-styles.css.js +1 -1
- package/chips/internal/shared-styles.css.js.map +1 -1
- package/chips/internal/suggestion-styles.css.js +1 -1
- package/chips/internal/suggestion-styles.css.js.map +1 -1
- package/chips/internal/trailing-icon-styles.css.js +1 -1
- package/chips/internal/trailing-icon-styles.css.js.map +1 -1
- package/chips/internal/trailing-icons.js +8 -4
- package/chips/internal/trailing-icons.js.map +1 -1
- package/fab/internal/_shared.scss +1 -0
- package/fab/internal/shared-styles.css.js +1 -1
- package/fab/internal/shared-styles.css.js.map +1 -1
- package/field/internal/_content.scss +3 -1
- package/field/internal/shared-styles.css.js +1 -1
- package/field/internal/shared-styles.css.js.map +1 -1
- package/internal/aria/aria.d.ts +0 -29
- package/internal/aria/aria.js +0 -141
- package/internal/aria/aria.js.map +1 -1
- package/internal/controller/form-submitter.js +2 -2
- package/internal/controller/form-submitter.js.map +1 -1
- package/labs/behaviors/constraint-validation.d.ts +2 -2
- package/labs/behaviors/constraint-validation.js +18 -1
- package/labs/behaviors/constraint-validation.js.map +1 -1
- package/labs/behaviors/element-internals.js +1 -5
- package/labs/behaviors/element-internals.js.map +1 -1
- package/labs/behaviors/focusable.js +20 -7
- package/labs/behaviors/focusable.js.map +1 -1
- package/labs/behaviors/on-report-validity.d.ts +70 -0
- package/labs/behaviors/on-report-validity.js +185 -0
- package/labs/behaviors/on-report-validity.js.map +1 -0
- package/labs/behaviors/validators/checkbox-validator.d.ts +6 -3
- package/labs/behaviors/validators/checkbox-validator.js.map +1 -1
- package/labs/behaviors/validators/radio-validator.d.ts +38 -0
- package/labs/behaviors/validators/radio-validator.js +65 -0
- package/labs/behaviors/validators/radio-validator.js.map +1 -0
- package/labs/behaviors/validators/select-validator.d.ts +35 -0
- package/labs/behaviors/validators/select-validator.js +33 -0
- package/labs/behaviors/validators/select-validator.js.map +1 -0
- package/labs/behaviors/validators/text-field-validator.d.ts +110 -0
- package/labs/behaviors/validators/text-field-validator.js +146 -0
- package/labs/behaviors/validators/text-field-validator.js.map +1 -0
- package/labs/behaviors/validators/validator.d.ts +4 -0
- package/labs/behaviors/validators/validator.js.map +1 -1
- package/labs/card/internal/_outlined-card.scss +1 -1
- package/labs/card/internal/_shared.scss +10 -2
- package/labs/card/internal/outlined-styles.css.js +1 -1
- package/labs/card/internal/outlined-styles.css.js.map +1 -1
- package/labs/card/internal/shared-styles.css.js +1 -1
- package/labs/card/internal/shared-styles.css.js.map +1 -1
- package/labs/segmentedbutton/internal/_shared.scss +1 -0
- package/labs/segmentedbutton/internal/shared-styles.css.js +1 -1
- package/labs/segmentedbutton/internal/shared-styles.css.js.map +1 -1
- package/list/internal/list.js +2 -6
- package/list/internal/list.js.map +1 -1
- package/list/internal/listitem/_list-item.scss +2 -0
- package/list/internal/listitem/list-item-styles.css.js +1 -1
- package/list/internal/listitem/list-item-styles.css.js.map +1 -1
- package/menu/internal/controllers/menuItemController.d.ts +13 -1
- package/menu/internal/controllers/menuItemController.js +32 -6
- package/menu/internal/controllers/menuItemController.js.map +1 -1
- package/menu/internal/menu.js +7 -8
- package/menu/internal/menu.js.map +1 -1
- package/menu/internal/menuitem/menu-item.d.ts +2 -0
- package/menu/internal/menuitem/menu-item.js +13 -1
- package/menu/internal/menuitem/menu-item.js.map +1 -1
- package/package.json +1 -1
- package/radio/internal/radio.d.ts +11 -1
- package/radio/internal/radio.js +28 -2
- package/radio/internal/radio.js.map +1 -1
- package/radio/internal/single-selection-controller.d.ts +5 -5
- package/radio/internal/single-selection-controller.js +16 -14
- package/radio/internal/single-selection-controller.js.map +1 -1
- package/select/internal/_shared.scss +5 -0
- package/select/internal/select.d.ts +21 -72
- package/select/internal/select.js +106 -184
- package/select/internal/select.js.map +1 -1
- package/select/internal/selectoption/select-option.d.ts +2 -0
- package/select/internal/selectoption/select-option.js +13 -1
- package/select/internal/selectoption/select-option.js.map +1 -1
- package/select/internal/selectoption/selectOptionController.d.ts +7 -3
- package/select/internal/selectoption/selectOptionController.js +8 -11
- package/select/internal/selectoption/selectOptionController.js.map +1 -1
- package/select/internal/shared-styles.css.js +1 -1
- package/select/internal/shared-styles.css.js.map +1 -1
- package/switch/internal/_icon.scss +14 -10
- package/switch/internal/switch-styles.css.js +1 -1
- package/switch/internal/switch-styles.css.js.map +1 -1
- package/switch/internal/switch.js +12 -8
- package/switch/internal/switch.js.map +1 -1
- package/tabs/internal/tab.js +2 -6
- package/tabs/internal/tab.js.map +1 -1
- package/tabs/internal/tabs.js +2 -6
- package/tabs/internal/tabs.js.map +1 -1
- package/textfield/internal/text-field.d.ts +9 -74
- package/textfield/internal/text-field.js +34 -150
- package/textfield/internal/text-field.js.map +1 -1
- package/tokens/_md-comp-assist-chip.scss +11 -0
- package/tokens/_md-comp-filter-chip.scss +14 -0
- package/tokens/_md-comp-input-chip.scss +14 -0
- package/tokens/_md-comp-suggestion-chip.scss +11 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aria.js","sourceRoot":"","sources":["aria.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license\n * Copyright 2023 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {isServer, ReactiveElement} from 'lit';\n\n/**\n * Accessibility Object Model reflective aria property name types.\n */\nexport type ARIAProperty = Exclude<keyof ARIAMixin, 'role'>;\n\n/**\n * Accessibility Object Model reflective aria properties.\n */\nexport const ARIA_PROPERTIES: ARIAProperty[] = [\n 'ariaAtomic',\n 'ariaAutoComplete',\n 'ariaBusy',\n 'ariaChecked',\n 'ariaColCount',\n 'ariaColIndex',\n 'ariaColSpan',\n 'ariaCurrent',\n 'ariaDisabled',\n 'ariaExpanded',\n 'ariaHasPopup',\n 'ariaHidden',\n 'ariaInvalid',\n 'ariaKeyShortcuts',\n 'ariaLabel',\n 'ariaLevel',\n 'ariaLive',\n 'ariaModal',\n 'ariaMultiLine',\n 'ariaMultiSelectable',\n 'ariaOrientation',\n 'ariaPlaceholder',\n 'ariaPosInSet',\n 'ariaPressed',\n 'ariaReadOnly',\n 'ariaRequired',\n 'ariaRoleDescription',\n 'ariaRowCount',\n 'ariaRowIndex',\n 'ariaRowSpan',\n 'ariaSelected',\n 'ariaSetSize',\n 'ariaSort',\n 'ariaValueMax',\n 'ariaValueMin',\n 'ariaValueNow',\n 'ariaValueText',\n];\n\n/**\n * Accessibility Object Model aria attribute name types.\n */\nexport type ARIAAttribute = ARIAPropertyToAttribute<ARIAProperty>;\n\n/**\n * Accessibility Object Model aria attributes.\n */\nexport const ARIA_ATTRIBUTES = ARIA_PROPERTIES.map(ariaPropertyToAttribute);\n\n/**\n * Checks if an attribute is one of the AOM aria attributes.\n *\n * @example\n * isAriaAttribute('aria-label'); // true\n *\n * @param attribute The attribute to check.\n * @return True if the attribute is an aria attribute, or false if not.\n */\nexport function isAriaAttribute(attribute: string): attribute is ARIAAttribute {\n return attribute.startsWith('aria-');\n}\n\n/**\n * Converts an AOM aria property into its corresponding attribute.\n *\n * @example\n * ariaPropertyToAttribute('ariaLabel'); // 'aria-label'\n *\n * @param property The aria property.\n * @return The aria attribute.\n */\nexport function ariaPropertyToAttribute<K extends ARIAProperty | 'role'>(\n property: K,\n) {\n return (\n property\n .replace('aria', 'aria-')\n // IDREF attributes also include an \"Element\" or \"Elements\" suffix\n .replace(/Elements?/g, '')\n .toLowerCase() as ARIAPropertyToAttribute<K>\n );\n}\n\n// Converts an `ariaFoo` string type to an `aria-foo` string type.\ntype ARIAPropertyToAttribute<K extends string> =\n K extends `aria${infer Suffix}Element${infer OptS}`\n ? `aria-${Lowercase<Suffix>}`\n : K extends `aria${infer Suffix}`\n ? `aria-${Lowercase<Suffix>}`\n : K;\n\n/**\n * An extension of `ARIAMixin` that enforces strict value types for aria\n * properties.\n *\n * This is needed for correct typing in render functions with lit analyzer.\n *\n * @example\n * render() {\n * const {ariaLabel} = this as ARIAMixinStrict;\n * return html`\n * <button aria-label=${ariaLabel || nothing}>\n * <slot></slot>\n * </button>\n * `;\n * }\n */\nexport interface ARIAMixinStrict extends ARIAMixin {\n ariaAtomic: 'true' | 'false' | null;\n ariaAutoComplete: 'none' | 'inline' | 'list' | 'both' | null;\n ariaBusy: 'true' | 'false' | null;\n ariaChecked: 'true' | 'false' | null;\n ariaColCount: `${number}` | null;\n ariaColIndex: `${number}` | null;\n ariaColSpan: `${number}` | null;\n ariaCurrent:\n | 'page'\n | 'step'\n | 'location'\n | 'date'\n | 'time'\n | 'true'\n | 'false'\n | null;\n ariaDisabled: 'true' | 'false' | null;\n ariaExpanded: 'true' | 'false' | null;\n ariaHasPopup:\n | 'false'\n | 'true'\n | 'menu'\n | 'listbox'\n | 'tree'\n | 'grid'\n | 'dialog'\n | null;\n ariaHidden: 'true' | 'false' | null;\n ariaInvalid: 'true' | 'false' | null;\n ariaKeyShortcuts: string | null;\n ariaLabel: string | null;\n ariaLevel: `${number}` | null;\n ariaLive: 'assertive' | 'off' | 'polite' | null;\n ariaModal: 'true' | 'false' | null;\n ariaMultiLine: 'true' | 'false' | null;\n ariaMultiSelectable: 'true' | 'false' | null;\n ariaOrientation: 'horizontal' | 'vertical' | 'undefined' | null;\n ariaPlaceholder: string | null;\n ariaPosInSet: `${number}` | null;\n ariaPressed: 'true' | 'false' | null;\n ariaReadOnly: 'true' | 'false' | null;\n ariaRequired: 'true' | 'false' | null;\n ariaRoleDescription: string | null;\n ariaRowCount: `${number}` | null;\n ariaRowIndex: `${number}` | null;\n ariaRowSpan: `${number}` | null;\n ariaSelected: 'true' | 'false' | null;\n ariaSetSize: `${number}` | null;\n ariaSort: 'ascending' | 'descending' | 'none' | 'other' | null;\n ariaValueMax: `${number}` | null;\n ariaValueMin: `${number}` | null;\n ariaValueNow: `${number}` | null;\n ariaValueText: string | null;\n role: ARIARole | null;\n}\n\n/**\n * Valid values for `role`.\n */\nexport type ARIARole =\n | 'alert'\n | 'alertdialog'\n | 'button'\n | 'checkbox'\n | 'dialog'\n | 'gridcell'\n | 'link'\n | 'log'\n | 'marquee'\n | 'menuitem'\n | 'menuitemcheckbox'\n | 'menuitemradio'\n | 'option'\n | 'progressbar'\n | 'radio'\n | 'scrollbar'\n | 'searchbox'\n | 'slider'\n | 'spinbutton'\n | 'status'\n | 'switch'\n | 'tab'\n | 'tabpanel'\n | 'textbox'\n | 'timer'\n | 'tooltip'\n | 'treeitem'\n | 'combobox'\n | 'grid'\n | 'listbox'\n | 'menu'\n | 'menubar'\n | 'radiogroup'\n | 'tablist'\n | 'tree'\n | 'treegrid'\n | 'application'\n | 'article'\n | 'cell'\n | 'columnheader'\n | 'definition'\n | 'directory'\n | 'document'\n | 'feed'\n | 'figure'\n | 'group'\n | 'heading'\n | 'img'\n | 'list'\n | 'listitem'\n | 'math'\n | 'none'\n | 'note'\n | 'presentation'\n | 'region'\n | 'row'\n | 'rowgroup'\n | 'rowheader'\n | 'separator'\n | 'table'\n | 'term'\n | 'text'\n | 'toolbar'\n | 'banner'\n | 'complementary'\n | 'contentinfo'\n | 'form'\n | 'main'\n | 'navigation'\n | 'region'\n | 'search'\n | 'doc-abstract'\n | 'doc-acknowledgments'\n | 'doc-afterword'\n | 'doc-appendix'\n | 'doc-backlink'\n | 'doc-biblioentry'\n | 'doc-bibliography'\n | 'doc-biblioref'\n | 'doc-chapter'\n | 'doc-colophon'\n | 'doc-conclusion'\n | 'doc-cover'\n | 'doc-credit'\n | 'doc-credits'\n | 'doc-dedication'\n | 'doc-endnote'\n | 'doc-endnotes'\n | 'doc-epigraph'\n | 'doc-epilogue'\n | 'doc-errata'\n | 'doc-example'\n | 'doc-footnote'\n | 'doc-foreword'\n | 'doc-glossary'\n | 'doc-glossref'\n | 'doc-index'\n | 'doc-introduction'\n | 'doc-noteref'\n | 'doc-notice'\n | 'doc-pagebreak'\n | 'doc-pagelist'\n | 'doc-part'\n | 'doc-preface'\n | 'doc-prologue'\n | 'doc-pullquote'\n | 'doc-qna'\n | 'doc-subtitle'\n | 'doc-tip'\n | 'doc-toc';\n\n/**\n * This function will polyfill `ARIAMixin` properties for Firefox.\n *\n * @param ctor The `ReactiveElement` constructor to set up.\n */\nexport function polyfillARIAMixin(ctor: typeof ReactiveElement) {\n if (isServer || 'role' in Element.prototype) {\n return;\n }\n\n // Polyfill reflective aria properties for Firefox\n for (const ariaProperty of ARIA_PROPERTIES) {\n ctor.createProperty(ariaProperty, {\n attribute: ariaPropertyToAttribute(ariaProperty),\n reflect: true,\n });\n }\n\n ctor.createProperty('role', {reflect: true});\n}\n\n/**\n * Polyfills an element and its `ElementInternals` to support `ARIAMixin`\n * properties on internals. This is needed for Firefox.\n *\n * `polyfillARIAMixin()` must be called for the element class.\n *\n * @example\n * class XButton extends LitElement {\n * static {\n * polyfillARIAMixin(XButton);\n * }\n *\n * private internals =\n * polyfillElementInternalsAria(this, this.attachInternals());\n *\n * constructor() {\n * super();\n * this.internals.role = 'button';\n * }\n * }\n */\nexport function polyfillElementInternalsAria(\n host: ReactiveElement,\n internals: ElementInternals,\n) {\n if (checkIfElementInternalsSupportsAria(internals)) {\n return internals;\n }\n\n if (!('role' in host)) {\n throw new Error('Missing polyfillARIAMixin()');\n }\n\n let firstConnectedCallbacks: Array<{\n property: ARIAProperty | 'role';\n callback: () => void;\n }> = [];\n let hasBeenConnected = false;\n\n // Add support for Firefox, which has not yet implement ElementInternals aria\n for (const ariaProperty of ARIA_PROPERTIES) {\n let internalAriaValue: string | null = null;\n Object.defineProperty(internals, ariaProperty, {\n enumerable: true,\n configurable: true,\n get() {\n return internalAriaValue;\n },\n set(value: string | null) {\n const setValue = () => {\n internalAriaValue = value;\n if (!hasBeenConnected) {\n firstConnectedCallbacks.push({\n property: ariaProperty,\n callback: setValue,\n });\n return;\n }\n\n // Dynamic lookup rather than hardcoding all properties.\n // tslint:disable-next-line:no-dict-access-on-struct-type\n host[ariaProperty] = value;\n };\n\n setValue();\n },\n });\n }\n\n let internalRoleValue: string | null = null;\n Object.defineProperty(internals, 'role', {\n enumerable: true,\n configurable: true,\n get() {\n return internalRoleValue;\n },\n set(value: string | null) {\n const setRole = () => {\n internalRoleValue = value;\n\n if (!hasBeenConnected) {\n firstConnectedCallbacks.push({\n property: 'role',\n callback: setRole,\n });\n return;\n }\n\n if (value === null) {\n host.removeAttribute('role');\n } else {\n host.setAttribute('role', value);\n }\n };\n\n setRole();\n },\n });\n\n host.addController({\n hostConnected() {\n if (hasBeenConnected) {\n return;\n }\n\n hasBeenConnected = true;\n\n const propertiesSetByUser = new Set<ARIAProperty | 'role'>();\n\n // See which properties were set by the user on host before we apply\n // internals values as attributes to host. Needs to be done in another\n // for loop because the callbacks set these attributes on host.\n for (const {property} of firstConnectedCallbacks) {\n const wasSetByUser =\n host.getAttribute(ariaPropertyToAttribute(property)) !== null ||\n // Dynamic lookup rather than hardcoding all properties.\n // tslint:disable-next-line:no-dict-access-on-struct-type\n host[property] !== undefined;\n\n if (wasSetByUser) {\n propertiesSetByUser.add(property);\n }\n }\n\n for (const {property, callback} of firstConnectedCallbacks) {\n // If the user has set the attribute or property, do not override the\n // user's value\n if (propertiesSetByUser.has(property)) {\n continue;\n }\n\n callback();\n }\n\n // Remove strong callback references\n firstConnectedCallbacks = [];\n },\n });\n\n return internals;\n}\n\n// Separate function so that typescript doesn't complain about internals being\n// \"never\".\nfunction checkIfElementInternalsSupportsAria(internals: ElementInternals) {\n return 'role' in internals;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"aria.js","sourceRoot":"","sources":["aria.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAmB;IAC7C,YAAY;IACZ,kBAAkB;IAClB,UAAU;IACV,aAAa;IACb,cAAc;IACd,cAAc;IACd,aAAa;IACb,aAAa;IACb,cAAc;IACd,cAAc;IACd,cAAc;IACd,YAAY;IACZ,aAAa;IACb,kBAAkB;IAClB,WAAW;IACX,WAAW;IACX,UAAU;IACV,WAAW;IACX,eAAe;IACf,qBAAqB;IACrB,iBAAiB;IACjB,iBAAiB;IACjB,cAAc;IACd,aAAa;IACb,cAAc;IACd,cAAc;IACd,qBAAqB;IACrB,cAAc;IACd,cAAc;IACd,aAAa;IACb,cAAc;IACd,aAAa;IACb,UAAU;IACV,cAAc;IACd,cAAc;IACd,cAAc;IACd,eAAe;CAChB,CAAC;AAOF;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,eAAe,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;AAE5E;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,OAAO,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CACrC,QAAW;IAEX,OACE,QAAQ;SACL,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC;QACzB,kEAAkE;SACjE,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC;SACzB,WAAW,EACf,CAAC;AACJ,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2023 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Accessibility Object Model reflective aria property name types.\n */\nexport type ARIAProperty = Exclude<keyof ARIAMixin, 'role'>;\n\n/**\n * Accessibility Object Model reflective aria properties.\n */\nexport const ARIA_PROPERTIES: ARIAProperty[] = [\n 'ariaAtomic',\n 'ariaAutoComplete',\n 'ariaBusy',\n 'ariaChecked',\n 'ariaColCount',\n 'ariaColIndex',\n 'ariaColSpan',\n 'ariaCurrent',\n 'ariaDisabled',\n 'ariaExpanded',\n 'ariaHasPopup',\n 'ariaHidden',\n 'ariaInvalid',\n 'ariaKeyShortcuts',\n 'ariaLabel',\n 'ariaLevel',\n 'ariaLive',\n 'ariaModal',\n 'ariaMultiLine',\n 'ariaMultiSelectable',\n 'ariaOrientation',\n 'ariaPlaceholder',\n 'ariaPosInSet',\n 'ariaPressed',\n 'ariaReadOnly',\n 'ariaRequired',\n 'ariaRoleDescription',\n 'ariaRowCount',\n 'ariaRowIndex',\n 'ariaRowSpan',\n 'ariaSelected',\n 'ariaSetSize',\n 'ariaSort',\n 'ariaValueMax',\n 'ariaValueMin',\n 'ariaValueNow',\n 'ariaValueText',\n];\n\n/**\n * Accessibility Object Model aria attribute name types.\n */\nexport type ARIAAttribute = ARIAPropertyToAttribute<ARIAProperty>;\n\n/**\n * Accessibility Object Model aria attributes.\n */\nexport const ARIA_ATTRIBUTES = ARIA_PROPERTIES.map(ariaPropertyToAttribute);\n\n/**\n * Checks if an attribute is one of the AOM aria attributes.\n *\n * @example\n * isAriaAttribute('aria-label'); // true\n *\n * @param attribute The attribute to check.\n * @return True if the attribute is an aria attribute, or false if not.\n */\nexport function isAriaAttribute(attribute: string): attribute is ARIAAttribute {\n return attribute.startsWith('aria-');\n}\n\n/**\n * Converts an AOM aria property into its corresponding attribute.\n *\n * @example\n * ariaPropertyToAttribute('ariaLabel'); // 'aria-label'\n *\n * @param property The aria property.\n * @return The aria attribute.\n */\nexport function ariaPropertyToAttribute<K extends ARIAProperty | 'role'>(\n property: K,\n) {\n return (\n property\n .replace('aria', 'aria-')\n // IDREF attributes also include an \"Element\" or \"Elements\" suffix\n .replace(/Elements?/g, '')\n .toLowerCase() as ARIAPropertyToAttribute<K>\n );\n}\n\n// Converts an `ariaFoo` string type to an `aria-foo` string type.\ntype ARIAPropertyToAttribute<K extends string> =\n K extends `aria${infer Suffix}Element${infer OptS}`\n ? `aria-${Lowercase<Suffix>}`\n : K extends `aria${infer Suffix}`\n ? `aria-${Lowercase<Suffix>}`\n : K;\n\n/**\n * An extension of `ARIAMixin` that enforces strict value types for aria\n * properties.\n *\n * This is needed for correct typing in render functions with lit analyzer.\n *\n * @example\n * render() {\n * const {ariaLabel} = this as ARIAMixinStrict;\n * return html`\n * <button aria-label=${ariaLabel || nothing}>\n * <slot></slot>\n * </button>\n * `;\n * }\n */\nexport interface ARIAMixinStrict extends ARIAMixin {\n ariaAtomic: 'true' | 'false' | null;\n ariaAutoComplete: 'none' | 'inline' | 'list' | 'both' | null;\n ariaBusy: 'true' | 'false' | null;\n ariaChecked: 'true' | 'false' | null;\n ariaColCount: `${number}` | null;\n ariaColIndex: `${number}` | null;\n ariaColSpan: `${number}` | null;\n ariaCurrent:\n | 'page'\n | 'step'\n | 'location'\n | 'date'\n | 'time'\n | 'true'\n | 'false'\n | null;\n ariaDisabled: 'true' | 'false' | null;\n ariaExpanded: 'true' | 'false' | null;\n ariaHasPopup:\n | 'false'\n | 'true'\n | 'menu'\n | 'listbox'\n | 'tree'\n | 'grid'\n | 'dialog'\n | null;\n ariaHidden: 'true' | 'false' | null;\n ariaInvalid: 'true' | 'false' | null;\n ariaKeyShortcuts: string | null;\n ariaLabel: string | null;\n ariaLevel: `${number}` | null;\n ariaLive: 'assertive' | 'off' | 'polite' | null;\n ariaModal: 'true' | 'false' | null;\n ariaMultiLine: 'true' | 'false' | null;\n ariaMultiSelectable: 'true' | 'false' | null;\n ariaOrientation: 'horizontal' | 'vertical' | 'undefined' | null;\n ariaPlaceholder: string | null;\n ariaPosInSet: `${number}` | null;\n ariaPressed: 'true' | 'false' | null;\n ariaReadOnly: 'true' | 'false' | null;\n ariaRequired: 'true' | 'false' | null;\n ariaRoleDescription: string | null;\n ariaRowCount: `${number}` | null;\n ariaRowIndex: `${number}` | null;\n ariaRowSpan: `${number}` | null;\n ariaSelected: 'true' | 'false' | null;\n ariaSetSize: `${number}` | null;\n ariaSort: 'ascending' | 'descending' | 'none' | 'other' | null;\n ariaValueMax: `${number}` | null;\n ariaValueMin: `${number}` | null;\n ariaValueNow: `${number}` | null;\n ariaValueText: string | null;\n role: ARIARole | null;\n}\n\n/**\n * Valid values for `role`.\n */\nexport type ARIARole =\n | 'alert'\n | 'alertdialog'\n | 'button'\n | 'checkbox'\n | 'dialog'\n | 'gridcell'\n | 'link'\n | 'log'\n | 'marquee'\n | 'menuitem'\n | 'menuitemcheckbox'\n | 'menuitemradio'\n | 'option'\n | 'progressbar'\n | 'radio'\n | 'scrollbar'\n | 'searchbox'\n | 'slider'\n | 'spinbutton'\n | 'status'\n | 'switch'\n | 'tab'\n | 'tabpanel'\n | 'textbox'\n | 'timer'\n | 'tooltip'\n | 'treeitem'\n | 'combobox'\n | 'grid'\n | 'listbox'\n | 'menu'\n | 'menubar'\n | 'radiogroup'\n | 'tablist'\n | 'tree'\n | 'treegrid'\n | 'application'\n | 'article'\n | 'cell'\n | 'columnheader'\n | 'definition'\n | 'directory'\n | 'document'\n | 'feed'\n | 'figure'\n | 'group'\n | 'heading'\n | 'img'\n | 'list'\n | 'listitem'\n | 'math'\n | 'none'\n | 'note'\n | 'presentation'\n | 'region'\n | 'row'\n | 'rowgroup'\n | 'rowheader'\n | 'separator'\n | 'table'\n | 'term'\n | 'text'\n | 'toolbar'\n | 'banner'\n | 'complementary'\n | 'contentinfo'\n | 'form'\n | 'main'\n | 'navigation'\n | 'region'\n | 'search'\n | 'doc-abstract'\n | 'doc-acknowledgments'\n | 'doc-afterword'\n | 'doc-appendix'\n | 'doc-backlink'\n | 'doc-biblioentry'\n | 'doc-bibliography'\n | 'doc-biblioref'\n | 'doc-chapter'\n | 'doc-colophon'\n | 'doc-conclusion'\n | 'doc-cover'\n | 'doc-credit'\n | 'doc-credits'\n | 'doc-dedication'\n | 'doc-endnote'\n | 'doc-endnotes'\n | 'doc-epigraph'\n | 'doc-epilogue'\n | 'doc-errata'\n | 'doc-example'\n | 'doc-footnote'\n | 'doc-foreword'\n | 'doc-glossary'\n | 'doc-glossref'\n | 'doc-index'\n | 'doc-introduction'\n | 'doc-noteref'\n | 'doc-notice'\n | 'doc-pagebreak'\n | 'doc-pagelist'\n | 'doc-part'\n | 'doc-preface'\n | 'doc-prologue'\n | 'doc-pullquote'\n | 'doc-qna'\n | 'doc-subtitle'\n | 'doc-tip'\n | 'doc-toc';\n"]}
|
|
@@ -39,9 +39,9 @@ export function setupFormSubmitter(ctor) {
|
|
|
39
39
|
if (!form || type === 'button') {
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
|
-
// Wait a
|
|
42
|
+
// Wait a full task for event bubbling to complete.
|
|
43
43
|
await new Promise((resolve) => {
|
|
44
|
-
resolve
|
|
44
|
+
setTimeout(resolve);
|
|
45
45
|
});
|
|
46
46
|
if (event.defaultPrevented) {
|
|
47
47
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-submitter.js","sourceRoot":"","sources":["form-submitter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,QAAQ,EAAkB,MAAM,KAAK,CAAC;AAE9C,OAAO,EACL,SAAS,GAEV,MAAM,2CAA2C,CAAC;AAgDnD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAA8B;IAC/D,IAAI,QAAQ,EAAE;QACZ,OAAO;KACR;IAEA,IAA0C,CAAC,cAAc,CAAC,CAAC,QAAQ,EAAE,EAAE;QACtE,MAAM,SAAS,GAAG,QAAyB,CAAC;QAC5C,SAAS,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YAClD,MAAM,EAAC,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,gBAAgB,EAAC,GAAG,SAAS,CAAC;YACxD,MAAM,EAAC,IAAI,EAAC,GAAG,gBAAgB,CAAC;YAChC,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,QAAQ,EAAE;gBAC9B,OAAO;aACR;YAED,mDAAmD;YACnD,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;gBAClC,OAAO,
|
|
1
|
+
{"version":3,"file":"form-submitter.js","sourceRoot":"","sources":["form-submitter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,QAAQ,EAAkB,MAAM,KAAK,CAAC;AAE9C,OAAO,EACL,SAAS,GAEV,MAAM,2CAA2C,CAAC;AAgDnD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAA8B;IAC/D,IAAI,QAAQ,EAAE;QACZ,OAAO;KACR;IAEA,IAA0C,CAAC,cAAc,CAAC,CAAC,QAAQ,EAAE,EAAE;QACtE,MAAM,SAAS,GAAG,QAAyB,CAAC;QAC5C,SAAS,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YAClD,MAAM,EAAC,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,gBAAgB,EAAC,GAAG,SAAS,CAAC;YACxD,MAAM,EAAC,IAAI,EAAC,GAAG,gBAAgB,CAAC;YAChC,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,QAAQ,EAAE;gBAC9B,OAAO;aACR;YAED,mDAAmD;YACnD,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;gBAClC,UAAU,CAAC,OAAO,CAAC,CAAC;YACtB,CAAC,CAAC,CAAC;YAEH,IAAI,KAAK,CAAC,gBAAgB,EAAE;gBAC1B,OAAO;aACR;YAED,IAAI,IAAI,KAAK,OAAO,EAAE;gBACpB,IAAI,CAAC,KAAK,EAAE,CAAC;gBACb,OAAO;aACR;YAED,0EAA0E;YAC1E,wEAAwE;YACxE,eAAe;YACf,uDAAuD;YACvD,IAAI,CAAC,gBAAgB,CACnB,QAAQ,EACR,CAAC,WAAW,EAAE,EAAE;gBACd,MAAM,CAAC,cAAc,CAAC,WAAW,EAAE,WAAW,EAAE;oBAC9C,YAAY,EAAE,IAAI;oBAClB,UAAU,EAAE,IAAI;oBAChB,GAAG,EAAE,GAAG,EAAE,CAAC,SAAS;iBACrB,CAAC,CAAC;YACL,CAAC,EACD,EAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAC,CAC5B,CAAC;YAEF,gBAAgB,CAAC,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YAC/C,IAAI,CAAC,aAAa,EAAE,CAAC;QACvB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2023 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {isServer, ReactiveElement} from 'lit';\n\nimport {\n internals,\n WithElementInternals,\n} from '../../labs/behaviors/element-internals.js';\n\n/**\n * A string indicating the form submission behavior of the element.\n *\n * - submit: The element submits the form. This is the default value if the\n * attribute is not specified, or if it is dynamically changed to an empty or\n * invalid value.\n * - reset: The element resets the form.\n * - button: The element does nothing.\n */\nexport type FormSubmitterType = 'button' | 'submit' | 'reset';\n\n/**\n * An element that can submit or reset a `<form>`, similar to\n * `<button type=\"submit\">`.\n */\nexport interface FormSubmitter extends ReactiveElement, WithElementInternals {\n /**\n * A string indicating the form submission behavior of the element.\n *\n * - submit: The element submits the form. This is the default value if the\n * attribute is not specified, or if it is dynamically changed to an empty or\n * invalid value.\n * - reset: The element resets the form.\n * - button: The element does nothing.\n */\n type: FormSubmitterType;\n\n /**\n * The HTML name to use in form submission. When combined with a `value`, the\n * submitting button's name/value will be added to the form.\n *\n * Names must reflect to a `name` attribute for form integration.\n */\n name: string;\n\n /**\n * The value of the button. When combined with a `name`, the submitting\n * button's name/value will be added to the form.\n */\n value: string;\n}\n\ntype FormSubmitterConstructor =\n | (new () => FormSubmitter)\n | (abstract new () => FormSubmitter);\n\n/**\n * Sets up an element's constructor to enable form submission. The element\n * instance should be form associated and have a `type` property.\n *\n * A click listener is added to each element instance. If the click is not\n * default prevented, it will submit the element's form, if any.\n *\n * @example\n * ```ts\n * class MyElement extends mixinElementInternals(LitElement) {\n * static {\n * setupFormSubmitter(MyElement);\n * }\n *\n * static formAssociated = true;\n *\n * type: FormSubmitterType = 'submit';\n * }\n * ```\n *\n * @param ctor The form submitter element's constructor.\n */\nexport function setupFormSubmitter(ctor: FormSubmitterConstructor) {\n if (isServer) {\n return;\n }\n\n (ctor as unknown as typeof ReactiveElement).addInitializer((instance) => {\n const submitter = instance as FormSubmitter;\n submitter.addEventListener('click', async (event) => {\n const {type, [internals]: elementInternals} = submitter;\n const {form} = elementInternals;\n if (!form || type === 'button') {\n return;\n }\n\n // Wait a full task for event bubbling to complete.\n await new Promise<void>((resolve) => {\n setTimeout(resolve);\n });\n\n if (event.defaultPrevented) {\n return;\n }\n\n if (type === 'reset') {\n form.reset();\n return;\n }\n\n // form.requestSubmit(submitter) does not work with form associated custom\n // elements. This patches the dispatched submit event to add the correct\n // `submitter`.\n // See https://github.com/WICG/webcomponents/issues/814\n form.addEventListener(\n 'submit',\n (submitEvent) => {\n Object.defineProperty(submitEvent, 'submitter', {\n configurable: true,\n enumerable: true,\n get: () => submitter,\n });\n },\n {capture: true, once: true},\n );\n\n elementInternals.setFormValue(submitter.value);\n form.requestSubmit();\n });\n });\n}\n"]}
|
|
@@ -13,7 +13,7 @@ import { Validator } from './validators/validator.js';
|
|
|
13
13
|
*
|
|
14
14
|
* https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation
|
|
15
15
|
*/
|
|
16
|
-
export interface ConstraintValidation {
|
|
16
|
+
export interface ConstraintValidation extends FormAssociated {
|
|
17
17
|
/**
|
|
18
18
|
* Returns a ValidityState object that represents the validity states of the
|
|
19
19
|
* element.
|
|
@@ -99,7 +99,7 @@ export declare const createValidator: unique symbol;
|
|
|
99
99
|
*/
|
|
100
100
|
export declare const getValidityAnchor: unique symbol;
|
|
101
101
|
/**
|
|
102
|
-
*
|
|
102
|
+
* Mixes in constraint validation APIs for an element.
|
|
103
103
|
*
|
|
104
104
|
* See https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation
|
|
105
105
|
* for more details.
|
|
@@ -20,7 +20,7 @@ const privateValidator = Symbol('privateValidator');
|
|
|
20
20
|
const privateSyncValidity = Symbol('privateSyncValidity');
|
|
21
21
|
const privateCustomValidationMessage = Symbol('privateCustomValidationMessage');
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* Mixes in constraint validation APIs for an element.
|
|
24
24
|
*
|
|
25
25
|
* See https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation
|
|
26
26
|
* for more details.
|
|
@@ -90,6 +90,23 @@ export function mixinConstraintValidation(base) {
|
|
|
90
90
|
super.requestUpdate(name, oldValue, options);
|
|
91
91
|
this[privateSyncValidity]();
|
|
92
92
|
}
|
|
93
|
+
firstUpdated(changed) {
|
|
94
|
+
super.firstUpdated(changed);
|
|
95
|
+
// Sync the validity again when the element first renders, since the
|
|
96
|
+
// validity anchor is now available.
|
|
97
|
+
//
|
|
98
|
+
// Elements that `delegatesFocus: true` to an `<input>` will throw an
|
|
99
|
+
// error in Chrome and Safari when a form tries to submit or call
|
|
100
|
+
// `form.reportValidity()`:
|
|
101
|
+
// "An invalid form control with name='' is not focusable"
|
|
102
|
+
//
|
|
103
|
+
// The validity anchor MUST be provided in `internals.setValidity()` and
|
|
104
|
+
// MUST be the `<input>` element rendered.
|
|
105
|
+
//
|
|
106
|
+
// See https://lit.dev/playground/#gist=6c26e418e0010f7a5aac15005cde8bde
|
|
107
|
+
// for a reproduction.
|
|
108
|
+
this[privateSyncValidity]();
|
|
109
|
+
}
|
|
93
110
|
[(_a = privateCustomValidationMessage, privateSyncValidity)]() {
|
|
94
111
|
if (isServer) {
|
|
95
112
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constraint-validation.js","sourceRoot":"","sources":["constraint-validation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license\n * Copyright 2023 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {isServer, LitElement, PropertyDeclaration} from 'lit';\n\nimport {internals, WithElementInternals} from './element-internals.js';\nimport {FormAssociated} from './form-associated.js';\nimport {MixinBase, MixinReturn} from './mixin.js';\nimport {Validator} from './validators/validator.js';\n\n/**\n * A form associated element that provides constraint validation APIs.\n *\n * https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation\n */\nexport interface ConstraintValidation {\n /**\n * Returns a ValidityState object that represents the validity states of the\n * element.\n *\n * https://developer.mozilla.org/en-US/docs/Web/API/ValidityState\n */\n readonly validity: ValidityState;\n\n /**\n * Returns a validation error message or an empty string if the element is\n * valid.\n *\n * https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/validationMessage\n */\n readonly validationMessage: string;\n\n /**\n * Returns whether an element will successfully validate based on forms\n * validation rules and constraints.\n *\n * Disabled and readonly elements will not validate.\n *\n * https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/willValidate\n */\n readonly willValidate: boolean;\n\n /**\n * Checks the element's constraint validation and returns true if the element\n * is valid or false if not.\n *\n * If invalid, this method will dispatch an `invalid` event.\n *\n * https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/checkValidity\n *\n * @return true if the element is valid, or false if not.\n */\n checkValidity(): boolean;\n\n /**\n * Checks the element's constraint validation and returns true if the element\n * is valid or false if not.\n *\n * If invalid, this method will dispatch a cancelable `invalid` event. If not\n * canceled, a the current `validationMessage` will be reported to the user.\n *\n * https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/reportValidity\n *\n * @return true if the element is valid, or false if not.\n */\n reportValidity(): boolean;\n\n /**\n * Sets the element's constraint validation error message. When set to a\n * non-empty string, `validity.customError` will be true and\n * `validationMessage` will display the provided error.\n *\n * Use this method to customize error messages reported.\n *\n * https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setCustomValidity\n *\n * @param error The error message to display, or an empty string.\n */\n setCustomValidity(error: string): void;\n\n /**\n * Creates and returns a `Validator` that is used to compute and cache\n * validity for the element.\n *\n * A validator that caches validity is important since constraint validation\n * must be computed synchronously and frequently in response to constraint\n * validation property changes.\n */\n [createValidator](): Validator<unknown>;\n\n /**\n * Returns shadow DOM child that is used as the anchor for the platform\n * `reportValidity()` popup. This is often the root element or the inner\n * focus-delegated element.\n */\n [getValidityAnchor](): HTMLElement | null;\n}\n\n/**\n * A symbol property used to create a constraint validation `Validator`.\n * Required for all `mixinConstraintValidation()` elements.\n */\nexport const createValidator = Symbol('createValidator');\n\n/**\n * A symbol property used to return an anchor for constraint validation popups.\n * Required for all `mixinConstraintValidation()` elements.\n */\nexport const getValidityAnchor = Symbol('getValidityAnchor');\n\n// Private symbol members, used to avoid name clashing.\nconst privateValidator = Symbol('privateValidator');\nconst privateSyncValidity = Symbol('privateSyncValidity');\nconst privateCustomValidationMessage = Symbol('privateCustomValidationMessage');\n\n/**\n * Mixins in constraint validation APIs for an element.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation\n * for more details.\n *\n * Implementations must provide a validator to cache and compute its validity,\n * along with a shadow root element to anchor validation popups to.\n *\n * @example\n * ```ts\n * const baseClass = mixinConstraintValidation(\n * mixinFormAssociated(mixinElementInternals(LitElement))\n * );\n *\n * class MyCheckbox extends baseClass {\n * \\@property({type: Boolean}) checked = false;\n * \\@property({type: Boolean}) required = false;\n *\n * [createValidator]() {\n * return new CheckboxValidator(() => this);\n * }\n *\n * [getValidityAnchor]() {\n * return this.renderRoot.querySelector('.root');\n * }\n * }\n * ```\n *\n * @param base The class to mix functionality into.\n * @return The provided class with `ConstraintValidation` mixed in.\n */\nexport function mixinConstraintValidation<\n T extends MixinBase<LitElement & FormAssociated & WithElementInternals>,\n>(base: T): MixinReturn<T, ConstraintValidation> {\n abstract class ConstraintValidationElement\n extends base\n implements ConstraintValidation\n {\n get validity() {\n this[privateSyncValidity]();\n return this[internals].validity;\n }\n\n get validationMessage() {\n this[privateSyncValidity]();\n return this[internals].validationMessage;\n }\n\n get willValidate() {\n this[privateSyncValidity]();\n return this[internals].willValidate;\n }\n\n /**\n * A validator instance created from `[createValidator]()`.\n */\n [privateValidator]?: Validator<unknown>;\n\n /**\n * Needed for Safari, see https://bugs.webkit.org/show_bug.cgi?id=261432\n * Replace with this[internals].validity.customError when resolved.\n */\n [privateCustomValidationMessage] = '';\n\n checkValidity() {\n this[privateSyncValidity]();\n return this[internals].checkValidity();\n }\n\n reportValidity() {\n this[privateSyncValidity]();\n return this[internals].reportValidity();\n }\n\n setCustomValidity(error: string) {\n this[privateCustomValidationMessage] = error;\n this[privateSyncValidity]();\n }\n\n override requestUpdate(\n name?: PropertyKey,\n oldValue?: unknown,\n options?: PropertyDeclaration,\n ) {\n super.requestUpdate(name, oldValue, options);\n this[privateSyncValidity]();\n }\n\n [privateSyncValidity]() {\n if (isServer) {\n return;\n }\n\n if (!this[privateValidator]) {\n this[privateValidator] = this[createValidator]();\n }\n\n const {validity, validationMessage: nonCustomValidationMessage} =\n this[privateValidator].getValidity();\n\n const customError = !!this[privateCustomValidationMessage];\n const validationMessage =\n this[privateCustomValidationMessage] || nonCustomValidationMessage;\n\n this[internals].setValidity(\n {...validity, customError},\n validationMessage,\n this[getValidityAnchor]() ?? undefined,\n );\n }\n\n [createValidator](): Validator<unknown> {\n throw new Error('Implement [createValidator]');\n }\n\n [getValidityAnchor](): HTMLElement | null {\n throw new Error('Implement [getValidityAnchor]');\n }\n }\n\n return ConstraintValidationElement;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"constraint-validation.js","sourceRoot":"","sources":["constraint-validation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license\n * Copyright 2023 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {isServer, LitElement, PropertyDeclaration, PropertyValues} from 'lit';\n\nimport {internals, WithElementInternals} from './element-internals.js';\nimport {FormAssociated} from './form-associated.js';\nimport {MixinBase, MixinReturn} from './mixin.js';\nimport {Validator} from './validators/validator.js';\n\n/**\n * A form associated element that provides constraint validation APIs.\n *\n * https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation\n */\nexport interface ConstraintValidation extends FormAssociated {\n /**\n * Returns a ValidityState object that represents the validity states of the\n * element.\n *\n * https://developer.mozilla.org/en-US/docs/Web/API/ValidityState\n */\n readonly validity: ValidityState;\n\n /**\n * Returns a validation error message or an empty string if the element is\n * valid.\n *\n * https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/validationMessage\n */\n readonly validationMessage: string;\n\n /**\n * Returns whether an element will successfully validate based on forms\n * validation rules and constraints.\n *\n * Disabled and readonly elements will not validate.\n *\n * https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/willValidate\n */\n readonly willValidate: boolean;\n\n /**\n * Checks the element's constraint validation and returns true if the element\n * is valid or false if not.\n *\n * If invalid, this method will dispatch an `invalid` event.\n *\n * https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/checkValidity\n *\n * @return true if the element is valid, or false if not.\n */\n checkValidity(): boolean;\n\n /**\n * Checks the element's constraint validation and returns true if the element\n * is valid or false if not.\n *\n * If invalid, this method will dispatch a cancelable `invalid` event. If not\n * canceled, a the current `validationMessage` will be reported to the user.\n *\n * https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/reportValidity\n *\n * @return true if the element is valid, or false if not.\n */\n reportValidity(): boolean;\n\n /**\n * Sets the element's constraint validation error message. When set to a\n * non-empty string, `validity.customError` will be true and\n * `validationMessage` will display the provided error.\n *\n * Use this method to customize error messages reported.\n *\n * https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setCustomValidity\n *\n * @param error The error message to display, or an empty string.\n */\n setCustomValidity(error: string): void;\n\n /**\n * Creates and returns a `Validator` that is used to compute and cache\n * validity for the element.\n *\n * A validator that caches validity is important since constraint validation\n * must be computed synchronously and frequently in response to constraint\n * validation property changes.\n */\n [createValidator](): Validator<unknown>;\n\n /**\n * Returns shadow DOM child that is used as the anchor for the platform\n * `reportValidity()` popup. This is often the root element or the inner\n * focus-delegated element.\n */\n [getValidityAnchor](): HTMLElement | null;\n}\n\n/**\n * A symbol property used to create a constraint validation `Validator`.\n * Required for all `mixinConstraintValidation()` elements.\n */\nexport const createValidator = Symbol('createValidator');\n\n/**\n * A symbol property used to return an anchor for constraint validation popups.\n * Required for all `mixinConstraintValidation()` elements.\n */\nexport const getValidityAnchor = Symbol('getValidityAnchor');\n\n// Private symbol members, used to avoid name clashing.\nconst privateValidator = Symbol('privateValidator');\nconst privateSyncValidity = Symbol('privateSyncValidity');\nconst privateCustomValidationMessage = Symbol('privateCustomValidationMessage');\n\n/**\n * Mixes in constraint validation APIs for an element.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation\n * for more details.\n *\n * Implementations must provide a validator to cache and compute its validity,\n * along with a shadow root element to anchor validation popups to.\n *\n * @example\n * ```ts\n * const baseClass = mixinConstraintValidation(\n * mixinFormAssociated(mixinElementInternals(LitElement))\n * );\n *\n * class MyCheckbox extends baseClass {\n * \\@property({type: Boolean}) checked = false;\n * \\@property({type: Boolean}) required = false;\n *\n * [createValidator]() {\n * return new CheckboxValidator(() => this);\n * }\n *\n * [getValidityAnchor]() {\n * return this.renderRoot.querySelector('.root');\n * }\n * }\n * ```\n *\n * @param base The class to mix functionality into.\n * @return The provided class with `ConstraintValidation` mixed in.\n */\nexport function mixinConstraintValidation<\n T extends MixinBase<LitElement & FormAssociated & WithElementInternals>,\n>(base: T): MixinReturn<T, ConstraintValidation> {\n abstract class ConstraintValidationElement\n extends base\n implements ConstraintValidation\n {\n get validity() {\n this[privateSyncValidity]();\n return this[internals].validity;\n }\n\n get validationMessage() {\n this[privateSyncValidity]();\n return this[internals].validationMessage;\n }\n\n get willValidate() {\n this[privateSyncValidity]();\n return this[internals].willValidate;\n }\n\n /**\n * A validator instance created from `[createValidator]()`.\n */\n [privateValidator]?: Validator<unknown>;\n\n /**\n * Needed for Safari, see https://bugs.webkit.org/show_bug.cgi?id=261432\n * Replace with this[internals].validity.customError when resolved.\n */\n [privateCustomValidationMessage] = '';\n\n checkValidity() {\n this[privateSyncValidity]();\n return this[internals].checkValidity();\n }\n\n reportValidity() {\n this[privateSyncValidity]();\n return this[internals].reportValidity();\n }\n\n setCustomValidity(error: string) {\n this[privateCustomValidationMessage] = error;\n this[privateSyncValidity]();\n }\n\n override requestUpdate(\n name?: PropertyKey,\n oldValue?: unknown,\n options?: PropertyDeclaration,\n ) {\n super.requestUpdate(name, oldValue, options);\n this[privateSyncValidity]();\n }\n\n override firstUpdated(changed: PropertyValues) {\n super.firstUpdated(changed);\n // Sync the validity again when the element first renders, since the\n // validity anchor is now available.\n //\n // Elements that `delegatesFocus: true` to an `<input>` will throw an\n // error in Chrome and Safari when a form tries to submit or call\n // `form.reportValidity()`:\n // \"An invalid form control with name='' is not focusable\"\n //\n // The validity anchor MUST be provided in `internals.setValidity()` and\n // MUST be the `<input>` element rendered.\n //\n // See https://lit.dev/playground/#gist=6c26e418e0010f7a5aac15005cde8bde\n // for a reproduction.\n this[privateSyncValidity]();\n }\n\n [privateSyncValidity]() {\n if (isServer) {\n return;\n }\n\n if (!this[privateValidator]) {\n this[privateValidator] = this[createValidator]();\n }\n\n const {validity, validationMessage: nonCustomValidationMessage} =\n this[privateValidator].getValidity();\n\n const customError = !!this[privateCustomValidationMessage];\n const validationMessage =\n this[privateCustomValidationMessage] || nonCustomValidationMessage;\n\n this[internals].setValidity(\n {...validity, customError},\n validationMessage,\n this[getValidityAnchor]() ?? undefined,\n );\n }\n\n [createValidator](): Validator<unknown> {\n throw new Error('Implement [createValidator]');\n }\n\n [getValidityAnchor](): HTMLElement | null {\n throw new Error('Implement [getValidityAnchor]');\n }\n }\n\n return ConstraintValidationElement;\n}\n"]}
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Copyright 2023 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import { polyfillARIAMixin, polyfillElementInternalsAria, } from '../../internal/aria/aria.js';
|
|
7
6
|
/**
|
|
8
7
|
* A unique symbol used for protected access to an instance's
|
|
9
8
|
* `ElementInternals`.
|
|
@@ -37,14 +36,11 @@ export function mixinElementInternals(base) {
|
|
|
37
36
|
// construction in `ReactiveElement`, such as `requestUpdate()`.
|
|
38
37
|
if (!this[privateInternals]) {
|
|
39
38
|
// Cast needed for closure
|
|
40
|
-
this[privateInternals] =
|
|
39
|
+
this[privateInternals] = this.attachInternals();
|
|
41
40
|
}
|
|
42
41
|
return this[privateInternals];
|
|
43
42
|
}
|
|
44
43
|
}
|
|
45
|
-
(() => {
|
|
46
|
-
polyfillARIAMixin(WithElementInternalsElement);
|
|
47
|
-
})();
|
|
48
44
|
return WithElementInternalsElement;
|
|
49
45
|
}
|
|
50
46
|
//# sourceMappingURL=element-internals.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"element-internals.js","sourceRoot":"","sources":["element-internals.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"element-internals.js","sourceRoot":"","sources":["element-internals.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;AAgB7C,kBAAkB;AAClB,MAAM,gBAAgB,GAAG,MAAM,CAAC,kBAAkB,CAAC,CAAC;AAEpD;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAAO;IAEP,MAAe,2BACb,SAAQ,IAAI;QAGZ,IAAI,CAAC,SAAS,CAAC;YACb,yEAAyE;YACzE,gEAAgE;YAChE,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE;gBAC3B,0BAA0B;gBAC1B,IAAI,CAAC,gBAAgB,CAAC,GAAI,IAAoB,CAAC,eAAe,EAAE,CAAC;aAClE;YAED,OAAO,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAChC,CAAC;KAGF;IAED,OAAO,2BAA2B,CAAC;AACrC,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2023 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {LitElement} from 'lit';\n\nimport {MixinBase, MixinReturn} from './mixin.js';\n\n/**\n * A unique symbol used for protected access to an instance's\n * `ElementInternals`.\n *\n * @example\n * ```ts\n * class MyElement extends mixinElementInternals(LitElement) {\n * constructor() {\n * super();\n * this[internals].role = 'button';\n * }\n * }\n * ```\n */\nexport const internals = Symbol('internals');\n\n/**\n * An instance with an `internals` symbol property for the component's\n * `ElementInternals`.\n *\n * Use this when protected access is needed for an instance's `ElementInternals`\n * from other files. A unique symbol is used to access the internals.\n */\nexport interface WithElementInternals {\n /**\n * An instance's `ElementInternals`.\n */\n [internals]: ElementInternals;\n}\n\n// Private symbols\nconst privateInternals = Symbol('privateInternals');\n\n/**\n * Mixes in an attached `ElementInternals` instance.\n *\n * This mixin is only needed when other shared code needs access to a\n * component's `ElementInternals`, such as form-associated mixins.\n *\n * @param base The class to mix functionality into.\n * @return The provided class with `WithElementInternals` mixed in.\n */\nexport function mixinElementInternals<T extends MixinBase<LitElement>>(\n base: T,\n): MixinReturn<T, WithElementInternals> {\n abstract class WithElementInternalsElement\n extends base\n implements WithElementInternals\n {\n get [internals]() {\n // Create internals in getter so that it can be used in methods called on\n // construction in `ReactiveElement`, such as `requestUpdate()`.\n if (!this[privateInternals]) {\n // Cast needed for closure\n this[privateInternals] = (this as HTMLElement).attachInternals();\n }\n\n return this[privateInternals];\n }\n\n [privateInternals]?: ElementInternals;\n }\n\n return WithElementInternalsElement;\n}\n"]}
|
|
@@ -14,6 +14,7 @@ const privateIsFocusable = Symbol('privateIsFocusable');
|
|
|
14
14
|
const externalTabIndex = Symbol('externalTabIndex');
|
|
15
15
|
const isUpdatingTabIndex = Symbol('isUpdatingTabIndex');
|
|
16
16
|
const updateTabIndex = Symbol('updateTabIndex');
|
|
17
|
+
const hasConstructed = Symbol('hasConstructed');
|
|
17
18
|
/**
|
|
18
19
|
* Mixes in focusable functionality for a class.
|
|
19
20
|
*
|
|
@@ -31,7 +32,7 @@ const updateTabIndex = Symbol('updateTabIndex');
|
|
|
31
32
|
* @return The provided class with `Focusable` mixed in.
|
|
32
33
|
*/
|
|
33
34
|
export function mixinFocusable(base) {
|
|
34
|
-
var _a, _b, _c;
|
|
35
|
+
var _a, _b, _c, _d;
|
|
35
36
|
class FocusableElement extends base {
|
|
36
37
|
get [isFocusable]() {
|
|
37
38
|
return this[privateIsFocusable];
|
|
@@ -49,11 +50,21 @@ export function mixinFocusable(base) {
|
|
|
49
50
|
this[_a] = false;
|
|
50
51
|
this[_b] = null;
|
|
51
52
|
this[_c] = false;
|
|
53
|
+
this[_d] = false;
|
|
52
54
|
this[isFocusable] = true;
|
|
55
|
+
queueMicrotask(() => {
|
|
56
|
+
this[hasConstructed] = true;
|
|
57
|
+
this[updateTabIndex]();
|
|
58
|
+
});
|
|
53
59
|
}
|
|
54
60
|
attributeChangedCallback(name, old, value) {
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
if (name !== 'tabindex') {
|
|
62
|
+
super.attributeChangedCallback(name, old, value);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
this.requestUpdate('tabIndex', Number(old ?? -1));
|
|
66
|
+
if (this[isUpdatingTabIndex]) {
|
|
67
|
+
// Not an externally-initiated update.
|
|
57
68
|
return;
|
|
58
69
|
}
|
|
59
70
|
if (!this.hasAttribute('tabindex')) {
|
|
@@ -64,18 +75,20 @@ export function mixinFocusable(base) {
|
|
|
64
75
|
}
|
|
65
76
|
this[externalTabIndex] = this.tabIndex;
|
|
66
77
|
}
|
|
67
|
-
|
|
78
|
+
[(_a = privateIsFocusable, _b = externalTabIndex, _c = isUpdatingTabIndex, _d = hasConstructed, updateTabIndex)]() {
|
|
79
|
+
if (!this[hasConstructed]) {
|
|
80
|
+
// Custom elements may not hydrate attributes during construction.
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
68
83
|
const internalTabIndex = this[isFocusable] ? 0 : -1;
|
|
69
84
|
const computedTabIndex = this[externalTabIndex] ?? internalTabIndex;
|
|
70
85
|
this[isUpdatingTabIndex] = true;
|
|
71
86
|
this.tabIndex = computedTabIndex;
|
|
72
|
-
this.requestUpdate();
|
|
73
|
-
await this.updateComplete;
|
|
74
87
|
this[isUpdatingTabIndex] = false;
|
|
75
88
|
}
|
|
76
89
|
}
|
|
77
90
|
__decorate([
|
|
78
|
-
property({
|
|
91
|
+
property({ noAccessor: true })
|
|
79
92
|
], FocusableElement.prototype, "tabIndex", void 0);
|
|
80
93
|
return FocusableElement;
|
|
81
94
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"focusable.js","sourceRoot":"","sources":["focusable.ts"],"names":[],"mappings":"AAAA;;;;GAIG;;AAGH,OAAO,EAAC,QAAQ,EAAC,MAAM,mBAAmB,CAAC;AAe3C;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;AAEjD,MAAM,kBAAkB,GAAG,MAAM,CAAC,oBAAoB,CAAC,CAAC;AACxD,MAAM,gBAAgB,GAAG,MAAM,CAAC,kBAAkB,CAAC,CAAC;AACpD,MAAM,kBAAkB,GAAG,MAAM,CAAC,oBAAoB,CAAC,CAAC;AACxD,MAAM,cAAc,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAEhD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAO;;IAEP,MAAe,gBAAiB,SAAQ,IAAI;
|
|
1
|
+
{"version":3,"file":"focusable.js","sourceRoot":"","sources":["focusable.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license\n * Copyright 2023 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {LitElement} from 'lit';\nimport {property} from 'lit/decorators.js';\n\nimport {MixinBase, MixinReturn} from './mixin.js';\n\n/**\n * An element that can enable and disable `tabindex` focusability.\n */\nexport interface Focusable {\n /**\n * Whether or not the element can be focused. Defaults to true. Set to false\n * to disable focusing (unless a user has set a `tabindex`).\n */\n [isFocusable]: boolean;\n}\n\n/**\n * A property symbol that indicates whether or not a `Focusable` element can be\n * focused.\n */\nexport const isFocusable = Symbol('isFocusable');\n\nconst privateIsFocusable = Symbol('privateIsFocusable');\nconst externalTabIndex = Symbol('externalTabIndex');\nconst isUpdatingTabIndex = Symbol('isUpdatingTabIndex');\nconst updateTabIndex = Symbol('updateTabIndex');\nconst hasConstructed = Symbol('hasConstructed');\n\n/**\n * Mixes in focusable functionality for a class.\n *\n * Elements can enable and disable their focusability with the `isFocusable`\n * symbol property. Changing `tabIndex` will trigger a lit render, meaning\n * `this.tabIndex` can be used in template expressions.\n *\n * This mixin will preserve externally-set tabindices. If an element turns off\n * focusability, but a user sets `tabindex=\"0\"`, it will still be focusable.\n *\n * To remove user overrides and restore focusability control to the element,\n * remove the `tabindex` attribute.\n *\n * @param base The class to mix functionality into.\n * @return The provided class with `Focusable` mixed in.\n */\nexport function mixinFocusable<T extends MixinBase<LitElement>>(\n base: T,\n): MixinReturn<T, Focusable> {\n abstract class FocusableElement extends base implements Focusable {\n @property({noAccessor: true})\n declare tabIndex: number;\n\n get [isFocusable]() {\n return this[privateIsFocusable];\n }\n\n set [isFocusable](value: boolean) {\n if (this[isFocusable] === value) {\n return;\n }\n\n this[privateIsFocusable] = value;\n this[updateTabIndex]();\n }\n\n [privateIsFocusable] = false;\n [externalTabIndex]: number | null = null;\n [isUpdatingTabIndex] = false;\n [hasConstructed] = false;\n\n // tslint:disable-next-line:no-any\n constructor(...args: any[]) {\n super(...args);\n this[isFocusable] = true;\n queueMicrotask(() => {\n this[hasConstructed] = true;\n this[updateTabIndex]();\n });\n }\n\n override attributeChangedCallback(\n name: string,\n old: string | null,\n value: string | null,\n ) {\n if (name !== 'tabindex') {\n super.attributeChangedCallback(name, old, value);\n return;\n }\n\n this.requestUpdate('tabIndex', Number(old ?? -1));\n if (this[isUpdatingTabIndex]) {\n // Not an externally-initiated update.\n return;\n }\n\n if (!this.hasAttribute('tabindex')) {\n // User removed the attribute, can now use internal tabIndex\n this[externalTabIndex] = null;\n this[updateTabIndex]();\n return;\n }\n\n this[externalTabIndex] = this.tabIndex;\n }\n\n [updateTabIndex]() {\n if (!this[hasConstructed]) {\n // Custom elements may not hydrate attributes during construction.\n return;\n }\n\n const internalTabIndex = this[isFocusable] ? 0 : -1;\n const computedTabIndex = this[externalTabIndex] ?? internalTabIndex;\n\n this[isUpdatingTabIndex] = true;\n this.tabIndex = computedTabIndex;\n this[isUpdatingTabIndex] = false;\n }\n }\n\n return FocusableElement;\n}\n"]}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { LitElement } from 'lit';
|
|
7
|
+
import { ConstraintValidation } from './constraint-validation.js';
|
|
8
|
+
import { MixinBase, MixinReturn } from './mixin.js';
|
|
9
|
+
/**
|
|
10
|
+
* A constraint validation element that has a callback for when the element
|
|
11
|
+
* should report validity styles and error messages to the user.
|
|
12
|
+
*
|
|
13
|
+
* This is commonly used in text-field-like controls that display error styles
|
|
14
|
+
* and error messages.
|
|
15
|
+
*/
|
|
16
|
+
export interface OnReportValidity extends ConstraintValidation {
|
|
17
|
+
/**
|
|
18
|
+
* A callback that is invoked when validity should be reported. Components
|
|
19
|
+
* that can display their own error state can use this and update their
|
|
20
|
+
* styles.
|
|
21
|
+
*
|
|
22
|
+
* If an invalid event is provided, the element is invalid. If `null`, the
|
|
23
|
+
* element is valid.
|
|
24
|
+
*
|
|
25
|
+
* The invalid event's `preventDefault()` may be called to stop the platform
|
|
26
|
+
* popup from displaying.
|
|
27
|
+
*
|
|
28
|
+
* @param invalidEvent The `invalid` event dispatched when an element is
|
|
29
|
+
* invalid, or `null` if the element is valid.
|
|
30
|
+
*/
|
|
31
|
+
[onReportValidity](invalidEvent: Event | null): void;
|
|
32
|
+
formAssociatedCallback(form: HTMLFormElement | null): void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A symbol property used for a callback when validity has been reported.
|
|
36
|
+
*/
|
|
37
|
+
export declare const onReportValidity: unique symbol;
|
|
38
|
+
/**
|
|
39
|
+
* Mixes in a callback for constraint validation when validity should be
|
|
40
|
+
* styled and reported to the user.
|
|
41
|
+
*
|
|
42
|
+
* This is commonly used in text-field-like controls that display error styles
|
|
43
|
+
* and error messages.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const baseClass = mixinOnReportValidity(
|
|
48
|
+
* mixinConstraintValidation(
|
|
49
|
+
* mixinFormAssociated(mixinElementInternals(LitElement)),
|
|
50
|
+
* ),
|
|
51
|
+
* );
|
|
52
|
+
*
|
|
53
|
+
* class MyField extends baseClass {
|
|
54
|
+
* \@property({type: Boolean}) error = false;
|
|
55
|
+
* \@property() errorMessage = '';
|
|
56
|
+
*
|
|
57
|
+
* [onReportValidity](invalidEvent: Event | null) {
|
|
58
|
+
* this.error = !!invalidEvent;
|
|
59
|
+
* this.errorMessage = this.validationMessage;
|
|
60
|
+
*
|
|
61
|
+
* // Optionally prevent platform popup from displaying
|
|
62
|
+
* invalidEvent?.preventDefault();
|
|
63
|
+
* }
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* @param base The class to mix functionality into.
|
|
68
|
+
* @return The provided class with `OnReportValidity` mixed in.
|
|
69
|
+
*/
|
|
70
|
+
export declare function mixinOnReportValidity<T extends MixinBase<LitElement & ConstraintValidation>>(base: T): MixinReturn<T, OnReportValidity>;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { isServer } from 'lit';
|
|
7
|
+
/**
|
|
8
|
+
* A symbol property used for a callback when validity has been reported.
|
|
9
|
+
*/
|
|
10
|
+
export const onReportValidity = Symbol('onReportValidity');
|
|
11
|
+
// Private symbol members, used to avoid name clashing.
|
|
12
|
+
const privateCleanupFormListeners = Symbol('privateCleanupFormListeners');
|
|
13
|
+
const privateDoNotReportInvalid = Symbol('privateDoNotReportInvalid');
|
|
14
|
+
/**
|
|
15
|
+
* Mixes in a callback for constraint validation when validity should be
|
|
16
|
+
* styled and reported to the user.
|
|
17
|
+
*
|
|
18
|
+
* This is commonly used in text-field-like controls that display error styles
|
|
19
|
+
* and error messages.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const baseClass = mixinOnReportValidity(
|
|
24
|
+
* mixinConstraintValidation(
|
|
25
|
+
* mixinFormAssociated(mixinElementInternals(LitElement)),
|
|
26
|
+
* ),
|
|
27
|
+
* );
|
|
28
|
+
*
|
|
29
|
+
* class MyField extends baseClass {
|
|
30
|
+
* \@property({type: Boolean}) error = false;
|
|
31
|
+
* \@property() errorMessage = '';
|
|
32
|
+
*
|
|
33
|
+
* [onReportValidity](invalidEvent: Event | null) {
|
|
34
|
+
* this.error = !!invalidEvent;
|
|
35
|
+
* this.errorMessage = this.validationMessage;
|
|
36
|
+
*
|
|
37
|
+
* // Optionally prevent platform popup from displaying
|
|
38
|
+
* invalidEvent?.preventDefault();
|
|
39
|
+
* }
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @param base The class to mix functionality into.
|
|
44
|
+
* @return The provided class with `OnReportValidity` mixed in.
|
|
45
|
+
*/
|
|
46
|
+
export function mixinOnReportValidity(base) {
|
|
47
|
+
var _a, _b;
|
|
48
|
+
class OnReportValidityElement extends base {
|
|
49
|
+
// Mixins must have a constructor with `...args: any[]`
|
|
50
|
+
// tslint:disable-next-line:no-any
|
|
51
|
+
constructor(...args) {
|
|
52
|
+
super(...args);
|
|
53
|
+
/**
|
|
54
|
+
* Used to clean up event listeners when a new form is associated.
|
|
55
|
+
*/
|
|
56
|
+
this[_a] = new AbortController();
|
|
57
|
+
/**
|
|
58
|
+
* Used to determine if an invalid event should report validity. Invalid
|
|
59
|
+
* events from `checkValidity()` do not trigger reporting.
|
|
60
|
+
*/
|
|
61
|
+
this[_b] = false;
|
|
62
|
+
if (isServer) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
this.addEventListener('invalid', (invalidEvent) => {
|
|
66
|
+
// Listen for invalid events dispatched by a `<form>` when it tries to
|
|
67
|
+
// submit and the element is invalid. We ignore events dispatched when
|
|
68
|
+
// calling `checkValidity()` as well as untrusted events, since the
|
|
69
|
+
// `reportValidity()` and `<form>`-dispatched events are always
|
|
70
|
+
// trusted.
|
|
71
|
+
if (this[privateDoNotReportInvalid] || !invalidEvent.isTrusted) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this.addEventListener('invalid', () => {
|
|
75
|
+
// A normal bubbling phase event listener. By adding it here, we
|
|
76
|
+
// ensure it's the last event listener that is called during the
|
|
77
|
+
// bubbling phase.
|
|
78
|
+
if (!invalidEvent.defaultPrevented) {
|
|
79
|
+
this[onReportValidity](invalidEvent);
|
|
80
|
+
}
|
|
81
|
+
}, { once: true });
|
|
82
|
+
}, {
|
|
83
|
+
// Listen during the capture phase, which will happen before the
|
|
84
|
+
// bubbling phase. That way, we can add a final event listener that
|
|
85
|
+
// will run after other event listeners, and we can check if it was
|
|
86
|
+
// default prevented. This works because invalid does not bubble.
|
|
87
|
+
capture: true,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
checkValidity() {
|
|
91
|
+
this[privateDoNotReportInvalid] = true;
|
|
92
|
+
const valid = super.checkValidity();
|
|
93
|
+
this[privateDoNotReportInvalid] = false;
|
|
94
|
+
return valid;
|
|
95
|
+
}
|
|
96
|
+
reportValidity() {
|
|
97
|
+
const valid = super.reportValidity();
|
|
98
|
+
// Constructor's invalid listener will handle reporting invalid events.
|
|
99
|
+
if (valid) {
|
|
100
|
+
this[onReportValidity](null);
|
|
101
|
+
}
|
|
102
|
+
return valid;
|
|
103
|
+
}
|
|
104
|
+
[(_a = privateCleanupFormListeners, _b = privateDoNotReportInvalid, onReportValidity)](invalidEvent) {
|
|
105
|
+
throw new Error('Implement [onReportValidity]');
|
|
106
|
+
}
|
|
107
|
+
formAssociatedCallback(form) {
|
|
108
|
+
// can't use super.formAssociatedCallback?.() due to closure
|
|
109
|
+
if (super.formAssociatedCallback) {
|
|
110
|
+
super.formAssociatedCallback(form);
|
|
111
|
+
}
|
|
112
|
+
// Clean up previous submit listener
|
|
113
|
+
this[privateCleanupFormListeners].abort();
|
|
114
|
+
if (!form) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
this[privateCleanupFormListeners] = new AbortController();
|
|
118
|
+
// If the element's form submits, then all controls are valid. This lets
|
|
119
|
+
// the element remove its error styles that may have been set when
|
|
120
|
+
// `reportValidity()` was called.
|
|
121
|
+
form.addEventListener('submit', () => {
|
|
122
|
+
this[onReportValidity](null);
|
|
123
|
+
}, {
|
|
124
|
+
signal: this[privateCleanupFormListeners].signal,
|
|
125
|
+
});
|
|
126
|
+
// Inject a callback when `form.reportValidity()` is called and the form
|
|
127
|
+
// is valid. There isn't an event that is dispatched to alert us (unlike
|
|
128
|
+
// the 'invalid' event), and we need to remove error styles when
|
|
129
|
+
// `form.reportValidity()` is called and returns true.
|
|
130
|
+
let reportedInvalidEventFromForm = false;
|
|
131
|
+
let formReportValidityCleanup = new AbortController();
|
|
132
|
+
injectFormReportValidityHooks({
|
|
133
|
+
form,
|
|
134
|
+
cleanup: this[privateCleanupFormListeners].signal,
|
|
135
|
+
beforeReportValidity: () => {
|
|
136
|
+
reportedInvalidEventFromForm = false;
|
|
137
|
+
this.addEventListener('invalid', (invalidEvent) => {
|
|
138
|
+
reportedInvalidEventFromForm = true;
|
|
139
|
+
if (!invalidEvent.defaultPrevented) {
|
|
140
|
+
this[onReportValidity](invalidEvent);
|
|
141
|
+
}
|
|
142
|
+
}, { signal: formReportValidityCleanup.signal });
|
|
143
|
+
},
|
|
144
|
+
afterReportValidity: () => {
|
|
145
|
+
formReportValidityCleanup.abort();
|
|
146
|
+
formReportValidityCleanup = new AbortController();
|
|
147
|
+
if (reportedInvalidEventFromForm) {
|
|
148
|
+
reportedInvalidEventFromForm = false;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
// Report successful form validation if an invalid event wasn't
|
|
152
|
+
// fired.
|
|
153
|
+
this[onReportValidity](null);
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return OnReportValidityElement;
|
|
159
|
+
}
|
|
160
|
+
const FORM_REPORT_VALIDITY_HOOKS = new WeakMap();
|
|
161
|
+
function injectFormReportValidityHooks({ form, beforeReportValidity, afterReportValidity, cleanup, }) {
|
|
162
|
+
if (!FORM_REPORT_VALIDITY_HOOKS.has(form)) {
|
|
163
|
+
// Patch form.reportValidity() to add an event target that can be used to
|
|
164
|
+
// react when the method is called.
|
|
165
|
+
// We should only patch this method once, since multiple controls and other
|
|
166
|
+
// forces may want to patch this method. We cannot reliably clean it up by
|
|
167
|
+
// resetting the method to "superReportValidity", which may be a patched
|
|
168
|
+
// function.
|
|
169
|
+
// Instead, we never clean up the patch but add and clean up event listener
|
|
170
|
+
// hooks once it's patched.
|
|
171
|
+
const hooks = new EventTarget();
|
|
172
|
+
const superReportValidity = form.reportValidity;
|
|
173
|
+
form.reportValidity = function () {
|
|
174
|
+
hooks.dispatchEvent(new Event('before'));
|
|
175
|
+
const valid = superReportValidity.call(this);
|
|
176
|
+
hooks.dispatchEvent(new Event('after'));
|
|
177
|
+
return valid;
|
|
178
|
+
};
|
|
179
|
+
FORM_REPORT_VALIDITY_HOOKS.set(form, hooks);
|
|
180
|
+
}
|
|
181
|
+
const hooks = FORM_REPORT_VALIDITY_HOOKS.get(form);
|
|
182
|
+
hooks.addEventListener('before', beforeReportValidity, { signal: cleanup });
|
|
183
|
+
hooks.addEventListener('after', afterReportValidity, { signal: cleanup });
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=on-report-validity.js.map
|