@nordhealth/components 4.26.0 → 4.26.1
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/custom-elements.json +10383 -10374
- package/lib/Button.js +1 -1
- package/lib/Button.js.map +1 -1
- package/lib/Navigation.js +1 -1
- package/lib/Navigation.js.map +1 -1
- package/lib/bundle.js +2 -2
- package/lib/bundle.js.map +1 -1
- package/lib/src/button/Button.d.ts +6 -0
- package/lib/src/layout/Layout.d.ts +1 -1
- package/package.json +2 -2
package/lib/Button.js
CHANGED
|
@@ -9,5 +9,5 @@ import{_ as t}from"./tslib.es6-CmLYFWVC.js";import{css as o,html as n,nothing as
|
|
|
9
9
|
--n-button-box-shadow,
|
|
10
10
|
0 0 0 1px var(--n-color-surface),
|
|
11
11
|
0 0 0 3px var(--n-color-status-danger)
|
|
12
|
-
)}:host([disabled]){--_n-button-border-color:var(--n-button-border-color, var(--_n-button-background-color));--_n-button-background-color:var(--n-button-background-color, var(--n-color-border));--_n-button-box-shadow:var(--n-button-box-shadow, none);--_n-button-color:var(--n-button-color, var(--n-color-text-weak));--_n-button-opacity:0.5;pointer-events:none}:host([disabled]) .n-button::after{display:none}.n-button-spinner{position:absolute;transform:translateX(-50%) translateY(-50%);inset-block-start:50%;inset-inline-start:50%}:host([loading]:not([href])){pointer-events:none}:host([loading]:not([href])) .n-content,:host([loading]:not([href])) ::slotted([slot=end]),:host([loading]:not([href])) ::slotted([slot=start]){opacity:0}:host([size='s']){--_n-button-gap:var(--n-button-gap, var(--n-space-xs));--_n-button-padding-inline:var(--n-button-padding-inline, calc(var(--n-space-s) + 1px));--_n-button-font-size:var(--n-button-font-size, var(--n-font-size-s));--_n-button-min-block-size:var(--n-button-min-block-size, calc(var(--n-space-l) + var(--n-space-xs)));--_n-button-padding-block:calc(var(--n-space-xs) - 1px);--_n-button-icon-size:var(--n-size-icon-xs)}:host([size='l']){--_n-button-border-radius:var(--n-button-border-radius, var(--n-border-radius));--_n-button-padding-inline:var(--n-button-padding-inline, calc(var(--n-space-l) / 1.3));--_n-button-font-size:var(--n-button-font-size, var(--n-font-size-l));--_n-button-font-weight:var(--n-button-font-weight, var(--n-font-weight-active));--_n-button-min-block-size:var(--n-button-min-block-size, calc(var(--n-space-xxl) - var(--n-space-l)));--_n-button-icon-size:var(--n-size-icon-m)}:host([square]){--_n-button-inline-size:var(--_n-button-min-block-size);--_n-button-padding-block:0;--_n-button-padding-inline:0}:host([square]) .n-content{display:flex;justify-content:center;align-items:center}::slotted(*){color:inherit;pointer-events:none}::slotted(svg){color:var(--n-color-icon)}::slotted(button[slot=proxy]){display:none}:host(:not([variant=primary],[variant=danger])) ::slotted(nord-icon){color:var(--n-color-icon)}:host(:not([variant=primary],[variant=danger])) .n-toggle-icon{color:var(--_n-button-toggle-icon-color)}:host(:not([square])) slot:not([name])::slotted(nord-icon){transform:translateY(1px)}::slotted(nord-icon:not([size])),nord-icon{--_n-icon-size:var(--_n-button-icon-size)}`;let f=class extends(h(v(e))){constructor(){super(...arguments),this.defaultSlot=new c(this),this.buttonRef=s(),this.events=new b(this),this.lightDom=new d(this,{render:()=>this.renderLightDom()}),this.variant="default",this.type="submit",this.size="m",this.download=!1,this.target="_self",this.expand=!1,this.square=!1,this.loading=!1,this.hideDropdownIcon=!1,this.handleOuterClick=t=>{t.composedPath().some((t=>t===this.focusableRef.value||t===this.buttonRef.value))||t.stopPropagation()}}connectedCallback(){super.connectedCallback(),this.events.listen(this,"click",this.handleOuterClick,!0)}render(){var t;const o="toggle"===(null===(t=this.assignedSlot)||void 0===t?void 0:t.name),e=this.defaultSlot.assigned.some((t=>"nord-icon"===t.localName)),a=o&&!this.hideDropdownIcon&&!e&&!this.href,i=n`<slot name="start"></slot><div class="n-content"><slot></slot></div><nord-spinner class="n-button-spinner" color="currentColor" ?hidden="${!this.loading||Boolean(this.href)}"></nord-spinner><slot name="end">${a?n`<nord-icon name="interface-dropdown-small" class="n-toggle-icon"></nord-icon>`:r}</slot>`;return this.href?this.renderLink(i):this.renderButton(i)}renderLink(t){return n`<a ${l(this.focusableRef)} class="n-button" target="${this.target}" ?download="${this.download}" href="${u(this.disabled,r,this.href)}" tabindex="${u(this.disabled,"-1")}" aria-disabled="${u(this.disabled,"true")}" role="${u(this.disabled,"link")}">${t}</a>`}renderButton(t){return n`<slot name="proxy" @slotchange="${this.handleProxyChange}"></slot><button ${l(this.focusableRef)} class="n-button" ?disabled="${this.disabled}" name="${u(this.name)}" value="${u(this.value)}" @click="${this.handleClick}" aria-disabled="${u(this.loading,"true")}" aria-expanded="${u(this.accessibleExpanded)}" aria-haspopup="${u(this.accessibleHasPopup)}">${t}</button>`}renderLightDom(){return this.href||!this.form?r:n`<button ${l(this.buttonRef)} slot="proxy" name="${u(this.name)}" value="${u(this.value)}" ?disabled="${this.disabled}" form="${u(this._formId)}" type="${this.type}"></button>`}handleClick(t){this.buttonRef.value&&(t.stopPropagation(),this.buttonRef.value.click())}handleProxyChange(t){const o=t.target,n=this.buttonRef.value;n&&n.assignedSlot!==o&&this.appendChild(n)}};f.styles=[p,g],t([a({reflect:!0})],f.prototype,"variant",void 0),t([a({reflect:!0})],f.prototype,"type",void 0),t([a({reflect:!0})],f.prototype,"size",void 0),t([a({attribute:"aria-expanded"})],f.prototype,"accessibleExpanded",void 0),t([a({attribute:"aria-haspopup"})],f.prototype,"accessibleHasPopup",void 0),t([a({reflect:!0})],f.prototype,"href",void 0),t([a({reflect:!0,type:Boolean})],f.prototype,"download",void 0),t([a()],f.prototype,"target",void 0),t([a({reflect:!0,type:Boolean})],f.prototype,"expand",void 0),t([a({reflect:!0,type:Boolean})],f.prototype,"square",void 0),t([a({reflect:!0,type:Boolean})],f.prototype,"loading",void 0),t([a({reflect:!0,type:Boolean,attribute:"hide-dropdown-icon"})],f.prototype,"hideDropdownIcon",void 0),f=t([i("nord-button")],f);var _=f;export{_ as default};
|
|
12
|
+
)}:host([disabled]){--_n-button-border-color:var(--n-button-border-color, var(--_n-button-background-color));--_n-button-background-color:var(--n-button-background-color, var(--n-color-border));--_n-button-box-shadow:var(--n-button-box-shadow, none);--_n-button-color:var(--n-button-color, var(--n-color-text-weak));--_n-button-opacity:0.5;pointer-events:none}:host([disabled]) .n-button::after{display:none}.n-button-spinner{position:absolute;transform:translateX(-50%) translateY(-50%);inset-block-start:50%;inset-inline-start:50%}:host([loading]:not([href])){pointer-events:none}:host([loading]:not([href])) .n-content,:host([loading]:not([href])) ::slotted([slot=end]),:host([loading]:not([href])) ::slotted([slot=start]){opacity:0}:host([size='s']){--_n-button-gap:var(--n-button-gap, var(--n-space-xs));--_n-button-padding-inline:var(--n-button-padding-inline, calc(var(--n-space-s) + 1px));--_n-button-font-size:var(--n-button-font-size, var(--n-font-size-s));--_n-button-min-block-size:var(--n-button-min-block-size, calc(var(--n-space-l) + var(--n-space-xs)));--_n-button-padding-block:calc(var(--n-space-xs) - 1px);--_n-button-icon-size:var(--n-size-icon-xs)}:host([size='l']){--_n-button-border-radius:var(--n-button-border-radius, var(--n-border-radius));--_n-button-padding-inline:var(--n-button-padding-inline, calc(var(--n-space-l) / 1.3));--_n-button-font-size:var(--n-button-font-size, var(--n-font-size-l));--_n-button-font-weight:var(--n-button-font-weight, var(--n-font-weight-active));--_n-button-min-block-size:var(--n-button-min-block-size, calc(var(--n-space-xxl) - var(--n-space-l)));--_n-button-icon-size:var(--n-size-icon-m)}:host([square]){--_n-button-inline-size:var(--_n-button-min-block-size);--_n-button-padding-block:0;--_n-button-padding-inline:0}:host([square]) .n-content{display:flex;justify-content:center;align-items:center}::slotted(*){color:inherit;pointer-events:none}::slotted(svg){color:var(--n-color-icon)}::slotted(button[slot=proxy]){display:none}:host(:not([variant=primary],[variant=danger])) ::slotted(nord-icon){color:var(--n-color-icon)}:host(:not([variant=primary],[variant=danger])) .n-toggle-icon{color:var(--_n-button-toggle-icon-color)}:host(:not([square])) slot:not([name])::slotted(nord-icon){transform:translateY(1px)}::slotted(nord-icon:not([size])),nord-icon{--_n-icon-size:var(--_n-button-icon-size)}`;let f=class extends(h(v(e))){constructor(){super(...arguments),this.defaultSlot=new c(this),this.buttonRef=s(),this.events=new b(this),this.lightDom=new d(this,{render:()=>this.renderLightDom()}),this.variant="default",this.type="submit",this.size="m",this.download=!1,this.target="_self",this.expand=!1,this.square=!1,this.loading=!1,this.hideDropdownIcon=!1,this.handleOuterClick=t=>{t.composedPath().some((t=>t===this.focusableRef.value||t===this.buttonRef.value))||t.stopPropagation()}}connectedCallback(){super.connectedCallback(),this.events.listen(this,"click",this.handleOuterClick,!0)}render(){var t;const o="toggle"===(null===(t=this.assignedSlot)||void 0===t?void 0:t.name),e=this.defaultSlot.assigned.some((t=>"nord-icon"===t.localName)),a=o&&!this.hideDropdownIcon&&!e&&!this.href,i=n`<slot name="start"></slot><div class="n-content"><slot></slot></div><nord-spinner class="n-button-spinner" color="currentColor" ?hidden="${!this.loading||Boolean(this.href)}"></nord-spinner><slot name="end">${a?n`<nord-icon name="interface-dropdown-small" class="n-toggle-icon"></nord-icon>`:r}</slot>`;return this.href?this.renderLink(i):this.renderButton(i)}renderLink(t){return n`<a ${l(this.focusableRef)} class="n-button" target="${this.target}" ?download="${this.download}" href="${u(this.disabled,r,this.href)}" tabindex="${u(this.disabled,"-1")}" aria-disabled="${u(this.disabled,"true")}" role="${u(this.disabled,"link")}">${t}</a>`}renderButton(t){return n`<slot name="proxy" @slotchange="${this.handleProxyChange}"></slot><button ${l(this.focusableRef)} class="n-button" ?disabled="${this.disabled}" name="${u(this.name)}" value="${u(this.value)}" @click="${this.handleClick}" aria-disabled="${u(this.loading,"true")}" aria-expanded="${u(this.accessibleExpanded)}" aria-haspopup="${u(this.accessibleHasPopup)}">${t}</button>`}renderLightDom(){return this.href||!this.form?r:n`<button ${l(this.buttonRef)} slot="proxy" name="${u(this.name)}" value="${u(this.value)}" ?disabled="${this.disabled}" form="${u(this._formId)}" type="${this.type}"></button>`}handleClick(t){this.buttonRef.value&&(t.stopPropagation(),this.buttonRef.value.click())}handleProxyChange(t){const o=t.target,n=this.buttonRef.value;n&&n.assignedSlot!==o&&this.appendChild(n)}};f.shadowRootOptions={...e.shadowRootOptions,delegatesFocus:!0},f.styles=[p,g],t([a({reflect:!0})],f.prototype,"variant",void 0),t([a({reflect:!0})],f.prototype,"type",void 0),t([a({reflect:!0})],f.prototype,"size",void 0),t([a({attribute:"aria-expanded"})],f.prototype,"accessibleExpanded",void 0),t([a({attribute:"aria-haspopup"})],f.prototype,"accessibleHasPopup",void 0),t([a({reflect:!0})],f.prototype,"href",void 0),t([a({reflect:!0,type:Boolean})],f.prototype,"download",void 0),t([a()],f.prototype,"target",void 0),t([a({reflect:!0,type:Boolean})],f.prototype,"expand",void 0),t([a({reflect:!0,type:Boolean})],f.prototype,"square",void 0),t([a({reflect:!0,type:Boolean})],f.prototype,"loading",void 0),t([a({reflect:!0,type:Boolean,attribute:"hide-dropdown-icon"})],f.prototype,"hideDropdownIcon",void 0),f=t([i("nord-button")],f);var _=f;export{_ as default};
|
|
13
13
|
//# sourceMappingURL=Button.js.map
|
package/lib/Button.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.js","sources":["../src/button/Button.ts"],"sourcesContent":["/* eslint-disable lit-a11y/accessible-name */\nimport type { TemplateResult } from 'lit'\nimport { html, LitElement, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { createRef, ref } from 'lit/directives/ref.js'\nimport { EventController } from '../common/controllers/EventController.js'\nimport { LightDomController } from '../common/controllers/LightDomController.js'\nimport { SlotController } from '../common/controllers/SlotController.js'\n\nimport { cond } from '../common/directives/cond.js'\n\nimport { FocusableMixin } from '../common/mixins/FocusableMixin.js'\nimport { InputMixin } from '../common/mixins/InputMixin.js'\nimport componentStyle from '../common/styles/Component.css'\nimport style from './Button.css'\nimport '../spinner/Spinner.js'\n\n/**\n * Buttons are used for interface actions. Primary style should be\n * used only once per section for main call-to-action, while other\n * styles can appear more frequently.\n *\n * @status ready\n * @category action\n * @slot - The button content\n * @slot start - Used to place content at the start of button text. Typically used for icons.\n * @slot end - Used to place content at the end of button text. Typically used for icons.\n *\n * @cssprop [--n-button-border-radius=var(--n-border-radius-s)] - Controls the rounded corners of the button, using [border radius tokens](/tokens/#border-radius).\n * @cssprop [--n-button-gap=var(--n-space-s)] - Controls the spacing between items within the button, using our [spacing tokens](/tokens/#space).\n * @cssprop [--n-button-gradient=linear-gradient(to bottom, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0.013) 100%))] - Controls the overlayed gradient background on the button.\n * @cssprop [--n-button-background-color=var(--n-color-button)] - Controls the background color of the button, using our [color tokens](/tokens/#color).\n * @cssprop [--n-button-border-color=var(--n-color-border-strong)] - Controls the border color of the button, using our [color tokens](/tokens/#color).\n * @cssprop [--n-button-text-align=center] - Controls the text alignment for the text in the button.\n * @cssprop [--n-button-box-shadow=var(--n-box-shadow)] - Controls the surrounding shadow, using our [box shadow tokens](/tokens/#box-shadow).\n * @cssprop [--n-button-color=var(--n-color-text)] - Controls the color of the text within the button, using our [color tokens](/tokens/#color).\n * @cssprop [--n-button-padding-inline=calc(var(--n-space-m) / 1.2)] - Controls the inline, or left and right, padding of the button.\n * @cssprop [--n-button-font-size=var(--n-font-size-m)] - Controls the size of the text within the button, using our [font tokens](/tokens/#font).\n * @cssprop [--n-button-font-weight=var(--n-font-weight)] - Controls the weight of the text within the button, using our [font tokens](/tokens/#font).\n * @cssprop [--n-button-min-block-size=var(--n-space-xl)] - Controls the minimum block size, or height, of the button using our [spacing tokens](/tokens/#space).\n * @cssprop [--n-button-toggle-icon-color=var(--n-color-icon)] - Controls the color of toggle icon that gets rendered when the button is used as a [Dropdown](/components/dropdown/) toggle.\n * @cssprop [--n-button-user-select=none] - Controls the text selection behavior of the button text.\n */\n@customElement('nord-button')\nexport default class Button extends InputMixin(FocusableMixin(LitElement)) {\n static styles = [componentStyle, style]\n\n private defaultSlot = new SlotController(this)\n private buttonRef = createRef<HTMLButtonElement>()\n private events = new EventController(this)\n private lightDom = new LightDomController(this, {\n render: () => this.renderLightDom(),\n })\n\n /**\n * The style variant of the button.\n */\n @property({ reflect: true }) variant: 'default' | 'primary' | 'dashed' | 'plain' | 'danger' = 'default'\n\n /**\n * The type of the button.\n */\n @property({ reflect: true }) type: 'button' | 'submit' | 'reset' = 'submit'\n\n /**\n * The size of the button.\n * This affects font-size and padding.\n */\n @property({ reflect: true }) size: 's' | 'm' | 'l' = 'm'\n\n /**\n * This does not need to be documented,\n * since it is only for forwarding the aria-expanded attribute\n * to the internal button element.\n * @private\n */\n @property({ attribute: 'aria-expanded' }) accessibleExpanded?: 'true' | 'false'\n\n /**\n * This does not need to be documented,\n * since it is only for forwarding the aria-haspopup attribute\n * to the internal button element.\n * @private\n */\n @property({ attribute: 'aria-haspopup' }) accessibleHasPopup?:\n | 'false'\n | 'true'\n | 'menu'\n | 'listbox'\n | 'tree'\n | 'grid'\n | 'dialog'\n\n /**\n * When provided, renders the button as a link,\n * with its href attribute set to the given value.\n */\n @property({ reflect: true }) href?: string\n\n /**\n * When provided together with a href property, the button will\n * trigger a file download instead of a page visit.\n */\n @property({ reflect: true, type: Boolean }) download = false\n\n /**\n * When provided together with a href property, determines where\n * to open the linked URL. The keywords have special meanings for\n * where to load the URL: “_self” means the current browsing context,\n * “_blank” usually a new tab but users can configure browsers this to\n * open a new window instead, “_parent” means the parent browsing\n * context of the current one, but if no parent exists, behaves as\n * _self, and finally “top” means the topmost browsing context.\n */\n @property() target: '_self' | '_blank' | '_parent' | '_top' = '_self'\n\n /**\n * Controls whether the button expands to fill the width of its container.\n */\n @property({ reflect: true, type: Boolean }) expand = false\n\n /**\n * When provided, the button is rendered as a square button. Use this for icon only buttons.\n */\n @property({ reflect: true, type: Boolean }) square = false\n\n /**\n * Controls whether the button is in loading state. Please note that the spinner\n * is hidden from assistive technologies, so you need to make sure to announce\n * the loading state to e.g. screen reader users. We also recommend disabling\n * all user interactions on the button itself while in loading state.\n */\n @property({ reflect: true, type: Boolean }) loading = false\n\n /**\n * Controls whether the `interface-dropdown-small` icon from Nordicons is hidden\n * in the `end` slot when used as a dropdown toggle.\n */\n @property({ reflect: true, type: Boolean, attribute: 'hide-dropdown-icon' }) hideDropdownIcon = false\n\n connectedCallback() {\n super.connectedCallback()\n this.events.listen(this, 'click', this.handleOuterClick, true)\n }\n\n render() {\n const isDropdownToggle = this.assignedSlot?.name === 'toggle'\n const isIconButton = this.defaultSlot.assigned.some(node => node.localName === 'nord-icon')\n const shouldShowDropdownIcon = isDropdownToggle && !this.hideDropdownIcon && !isIconButton && !this.href\n\n const innards = html`\n <slot name=\"start\"></slot>\n <div class=\"n-content\">\n <slot></slot>\n </div>\n <nord-spinner\n class=\"n-button-spinner\"\n color=\"currentColor\"\n ?hidden=${!this.loading || Boolean(this.href)}\n ></nord-spinner>\n <slot name=\"end\">\n ${shouldShowDropdownIcon\n ? html`<nord-icon name=\"interface-dropdown-small\" class=\"n-toggle-icon\"></nord-icon>`\n : nothing}\n </slot>\n `\n\n return this.href ? this.renderLink(innards) : this.renderButton(innards)\n }\n\n /**\n * We jump through some hoops here to ensure the link is treated correctly when \"disabled\".\n * Links cannot be disabled natively, so we need to rely on some aria magic to get the correct semantics.\n * Along with the advice in the article below, we also set tabindex to \"-1\", so it is taken out of tab order.\n *\n * @see https://www.scottohara.me/blog/2021/05/28/disabled-links.html\n */\n private renderLink(innards: TemplateResult) {\n return html`\n <a\n ${ref(this.focusableRef)}\n class=\"n-button\"\n target=${this.target}\n ?download=${this.download}\n href=${cond(this.disabled, nothing, this.href)}\n tabindex=${cond(this.disabled, '-1')}\n aria-disabled=${cond(this.disabled, 'true')}\n role=${cond(this.disabled, 'link')}\n >${innards}\n </a>\n `\n }\n\n private renderButton(innards: TemplateResult) {\n return html`\n <slot name=\"proxy\" @slotchange=${this.handleProxyChange}></slot>\n <button\n ${ref(this.focusableRef)}\n class=\"n-button\"\n ?disabled=${this.disabled}\n name=${cond(this.name)}\n value=${cond(this.value)}\n @click=${this.handleClick}\n aria-disabled=${cond(this.loading, 'true')}\n aria-expanded=${cond(this.accessibleExpanded)}\n aria-haspopup=${cond(this.accessibleHasPopup)}\n >\n ${innards}\n </button>\n `\n }\n\n private renderLightDom() {\n if (this.href || !this.form) {\n return nothing\n }\n\n return html`\n <button\n ${ref(this.buttonRef)}\n slot=\"proxy\"\n name=${cond(this.name)}\n value=${cond(this.value)}\n ?disabled=${this.disabled}\n form=${cond(this._formId)}\n type=${this.type}\n ></button>\n `\n }\n\n private handleOuterClick = (e: MouseEvent) => {\n // we want to avoid emitting click events when a click\n // happens in blank space in the host, but not on the button\n // so we stop propagation of any events if click didn't happen on the internal or proxy button\n const isInternalButton = e\n .composedPath()\n .some(node => node === this.focusableRef.value || node === this.buttonRef.value)\n\n if (!isInternalButton) {\n e.stopPropagation()\n }\n }\n\n private handleClick(e: Event) {\n if (this.buttonRef.value) {\n // prevents two events: one from internal button, one from proxy button\n e.stopPropagation()\n this.buttonRef.value.click()\n }\n }\n\n /**\n * React/Vue etc may remove our proxy button when updating button text, since they are unaware of its existence.\n * So we listen for a slotchange event, and if the element is no longer connected to the DOM we add it back in.\n */\n private handleProxyChange(e: Event) {\n const slot = e.target as HTMLSlotElement\n const proxyButton = this.buttonRef.value\n\n if (proxyButton && proxyButton.assignedSlot !== slot) {\n this.appendChild(proxyButton)\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'nord-button': Button\n }\n}\n"],"names":["Button","InputMixin","FocusableMixin","LitElement","constructor","this","defaultSlot","SlotController","buttonRef","createRef","events","EventController","lightDom","LightDomController","render","renderLightDom","variant","type","size","download","target","expand","square","loading","hideDropdownIcon","handleOuterClick","e","composedPath","some","node","focusableRef","value","stopPropagation","connectedCallback","super","listen","isDropdownToggle","_a","assignedSlot","name","isIconButton","assigned","localName","shouldShowDropdownIcon","href","innards","html","Boolean","nothing","renderLink","renderButton","ref","cond","disabled","handleProxyChange","handleClick","accessibleExpanded","accessibleHasPopup","form","_formId","click","slot","proxyButton","appendChild","styles","componentStyle","style","__decorate","property","reflect","prototype","attribute","customElement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Button.js","sources":["../src/button/Button.ts"],"sourcesContent":["/* eslint-disable lit-a11y/accessible-name */\nimport type { TemplateResult } from 'lit'\nimport { html, LitElement, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { createRef, ref } from 'lit/directives/ref.js'\nimport { EventController } from '../common/controllers/EventController.js'\nimport { LightDomController } from '../common/controllers/LightDomController.js'\nimport { SlotController } from '../common/controllers/SlotController.js'\n\nimport { cond } from '../common/directives/cond.js'\n\nimport { FocusableMixin } from '../common/mixins/FocusableMixin.js'\nimport { InputMixin } from '../common/mixins/InputMixin.js'\nimport componentStyle from '../common/styles/Component.css'\nimport style from './Button.css'\nimport '../spinner/Spinner.js'\n\n/**\n * Buttons are used for interface actions. Primary style should be\n * used only once per section for main call-to-action, while other\n * styles can appear more frequently.\n *\n * @status ready\n * @category action\n * @slot - The button content\n * @slot start - Used to place content at the start of button text. Typically used for icons.\n * @slot end - Used to place content at the end of button text. Typically used for icons.\n *\n * @cssprop [--n-button-border-radius=var(--n-border-radius-s)] - Controls the rounded corners of the button, using [border radius tokens](/tokens/#border-radius).\n * @cssprop [--n-button-gap=var(--n-space-s)] - Controls the spacing between items within the button, using our [spacing tokens](/tokens/#space).\n * @cssprop [--n-button-gradient=linear-gradient(to bottom, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0.013) 100%))] - Controls the overlayed gradient background on the button.\n * @cssprop [--n-button-background-color=var(--n-color-button)] - Controls the background color of the button, using our [color tokens](/tokens/#color).\n * @cssprop [--n-button-border-color=var(--n-color-border-strong)] - Controls the border color of the button, using our [color tokens](/tokens/#color).\n * @cssprop [--n-button-text-align=center] - Controls the text alignment for the text in the button.\n * @cssprop [--n-button-box-shadow=var(--n-box-shadow)] - Controls the surrounding shadow, using our [box shadow tokens](/tokens/#box-shadow).\n * @cssprop [--n-button-color=var(--n-color-text)] - Controls the color of the text within the button, using our [color tokens](/tokens/#color).\n * @cssprop [--n-button-padding-inline=calc(var(--n-space-m) / 1.2)] - Controls the inline, or left and right, padding of the button.\n * @cssprop [--n-button-font-size=var(--n-font-size-m)] - Controls the size of the text within the button, using our [font tokens](/tokens/#font).\n * @cssprop [--n-button-font-weight=var(--n-font-weight)] - Controls the weight of the text within the button, using our [font tokens](/tokens/#font).\n * @cssprop [--n-button-min-block-size=var(--n-space-xl)] - Controls the minimum block size, or height, of the button using our [spacing tokens](/tokens/#space).\n * @cssprop [--n-button-toggle-icon-color=var(--n-color-icon)] - Controls the color of toggle icon that gets rendered when the button is used as a [Dropdown](/components/dropdown/) toggle.\n * @cssprop [--n-button-user-select=none] - Controls the text selection behavior of the button text.\n */\n@customElement('nord-button')\nexport default class Button extends InputMixin(FocusableMixin(LitElement)) {\n static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }\n\n static styles = [componentStyle, style]\n\n private defaultSlot = new SlotController(this)\n private buttonRef = createRef<HTMLButtonElement>()\n private events = new EventController(this)\n private lightDom = new LightDomController(this, {\n render: () => this.renderLightDom(),\n })\n\n /**\n * The style variant of the button.\n */\n @property({ reflect: true }) variant: 'default' | 'primary' | 'dashed' | 'plain' | 'danger' = 'default'\n\n /**\n * The type of the button.\n */\n @property({ reflect: true }) type: 'button' | 'submit' | 'reset' = 'submit'\n\n /**\n * The size of the button.\n * This affects font-size and padding.\n */\n @property({ reflect: true }) size: 's' | 'm' | 'l' = 'm'\n\n /**\n * This does not need to be documented,\n * since it is only for forwarding the aria-expanded attribute\n * to the internal button element.\n * @private\n */\n @property({ attribute: 'aria-expanded' }) accessibleExpanded?: 'true' | 'false'\n\n /**\n * This does not need to be documented,\n * since it is only for forwarding the aria-haspopup attribute\n * to the internal button element.\n * @private\n */\n @property({ attribute: 'aria-haspopup' }) accessibleHasPopup?:\n | 'false'\n | 'true'\n | 'menu'\n | 'listbox'\n | 'tree'\n | 'grid'\n | 'dialog'\n\n /**\n * When provided, renders the button as a link,\n * with its href attribute set to the given value.\n */\n @property({ reflect: true }) href?: string\n\n /**\n * When provided together with a href property, the button will\n * trigger a file download instead of a page visit.\n */\n @property({ reflect: true, type: Boolean }) download = false\n\n /**\n * When provided together with a href property, determines where\n * to open the linked URL. The keywords have special meanings for\n * where to load the URL: “_self” means the current browsing context,\n * “_blank” usually a new tab but users can configure browsers this to\n * open a new window instead, “_parent” means the parent browsing\n * context of the current one, but if no parent exists, behaves as\n * _self, and finally “top” means the topmost browsing context.\n */\n @property() target: '_self' | '_blank' | '_parent' | '_top' = '_self'\n\n /**\n * Controls whether the button expands to fill the width of its container.\n */\n @property({ reflect: true, type: Boolean }) expand = false\n\n /**\n * When provided, the button is rendered as a square button. Use this for icon only buttons.\n */\n @property({ reflect: true, type: Boolean }) square = false\n\n /**\n * Controls whether the button is in loading state. Please note that the spinner\n * is hidden from assistive technologies, so you need to make sure to announce\n * the loading state to e.g. screen reader users. We also recommend disabling\n * all user interactions on the button itself while in loading state.\n */\n @property({ reflect: true, type: Boolean }) loading = false\n\n /**\n * Controls whether the `interface-dropdown-small` icon from Nordicons is hidden\n * in the `end` slot when used as a dropdown toggle.\n */\n @property({ reflect: true, type: Boolean, attribute: 'hide-dropdown-icon' }) hideDropdownIcon = false\n\n connectedCallback() {\n super.connectedCallback()\n this.events.listen(this, 'click', this.handleOuterClick, true)\n }\n\n render() {\n const isDropdownToggle = this.assignedSlot?.name === 'toggle'\n const isIconButton = this.defaultSlot.assigned.some(node => node.localName === 'nord-icon')\n const shouldShowDropdownIcon = isDropdownToggle && !this.hideDropdownIcon && !isIconButton && !this.href\n\n const innards = html`\n <slot name=\"start\"></slot>\n <div class=\"n-content\">\n <slot></slot>\n </div>\n <nord-spinner\n class=\"n-button-spinner\"\n color=\"currentColor\"\n ?hidden=${!this.loading || Boolean(this.href)}\n ></nord-spinner>\n <slot name=\"end\">\n ${shouldShowDropdownIcon\n ? html`<nord-icon name=\"interface-dropdown-small\" class=\"n-toggle-icon\"></nord-icon>`\n : nothing}\n </slot>\n `\n\n return this.href ? this.renderLink(innards) : this.renderButton(innards)\n }\n\n /**\n * We jump through some hoops here to ensure the link is treated correctly when \"disabled\".\n * Links cannot be disabled natively, so we need to rely on some aria magic to get the correct semantics.\n * Along with the advice in the article below, we also set tabindex to \"-1\", so it is taken out of tab order.\n *\n * @see https://www.scottohara.me/blog/2021/05/28/disabled-links.html\n */\n private renderLink(innards: TemplateResult) {\n return html`\n <a\n ${ref(this.focusableRef)}\n class=\"n-button\"\n target=${this.target}\n ?download=${this.download}\n href=${cond(this.disabled, nothing, this.href)}\n tabindex=${cond(this.disabled, '-1')}\n aria-disabled=${cond(this.disabled, 'true')}\n role=${cond(this.disabled, 'link')}\n >${innards}\n </a>\n `\n }\n\n private renderButton(innards: TemplateResult) {\n return html`\n <slot name=\"proxy\" @slotchange=${this.handleProxyChange}></slot>\n <button\n ${ref(this.focusableRef)}\n class=\"n-button\"\n ?disabled=${this.disabled}\n name=${cond(this.name)}\n value=${cond(this.value)}\n @click=${this.handleClick}\n aria-disabled=${cond(this.loading, 'true')}\n aria-expanded=${cond(this.accessibleExpanded)}\n aria-haspopup=${cond(this.accessibleHasPopup)}\n >\n ${innards}\n </button>\n `\n }\n\n private renderLightDom() {\n if (this.href || !this.form) {\n return nothing\n }\n\n return html`\n <button\n ${ref(this.buttonRef)}\n slot=\"proxy\"\n name=${cond(this.name)}\n value=${cond(this.value)}\n ?disabled=${this.disabled}\n form=${cond(this._formId)}\n type=${this.type}\n ></button>\n `\n }\n\n private handleOuterClick = (e: MouseEvent) => {\n // we want to avoid emitting click events when a click\n // happens in blank space in the host, but not on the button\n // so we stop propagation of any events if click didn't happen on the internal or proxy button\n const isInternalButton = e\n .composedPath()\n .some(node => node === this.focusableRef.value || node === this.buttonRef.value)\n\n if (!isInternalButton) {\n e.stopPropagation()\n }\n }\n\n private handleClick(e: Event) {\n if (this.buttonRef.value) {\n // prevents two events: one from internal button, one from proxy button\n e.stopPropagation()\n this.buttonRef.value.click()\n }\n }\n\n /**\n * React/Vue etc may remove our proxy button when updating button text, since they are unaware of its existence.\n * So we listen for a slotchange event, and if the element is no longer connected to the DOM we add it back in.\n */\n private handleProxyChange(e: Event) {\n const slot = e.target as HTMLSlotElement\n const proxyButton = this.buttonRef.value\n\n if (proxyButton && proxyButton.assignedSlot !== slot) {\n this.appendChild(proxyButton)\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'nord-button': Button\n }\n}\n"],"names":["Button","InputMixin","FocusableMixin","LitElement","constructor","this","defaultSlot","SlotController","buttonRef","createRef","events","EventController","lightDom","LightDomController","render","renderLightDom","variant","type","size","download","target","expand","square","loading","hideDropdownIcon","handleOuterClick","e","composedPath","some","node","focusableRef","value","stopPropagation","connectedCallback","super","listen","isDropdownToggle","_a","assignedSlot","name","isIconButton","assigned","localName","shouldShowDropdownIcon","href","innards","html","Boolean","nothing","renderLink","renderButton","ref","cond","disabled","handleProxyChange","handleClick","accessibleExpanded","accessibleHasPopup","form","_formId","click","slot","proxyButton","appendChild","shadowRootOptions","delegatesFocus","styles","componentStyle","style","__decorate","property","reflect","prototype","attribute","customElement"],"mappings":"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"}
|
package/lib/Navigation.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{_ as n}from"./tslib.es6-CmLYFWVC.js";import{css as o,LitElement as t,html as a,nothing as e}from"lit";import{property as i,customElement as r}from"lit/decorators.js";import{E as s}from"./EventController-BBOmvfLa.js";import{S as l}from"./SlotController-Z6eG7LSZ.js";import{s as d}from"./Component-DSU3Qp0O.js";const c=o`:host{--_n-navigation-background-color:var(--n-navigation-background-color, var(--n-color-nav-surface));--_n-navigation-scroll-shadow-color-start:var(--n-navigation-scroll-shadow-color-start, rgb(0, 0, 0, 0.1));--_n-navigation-scroll-shadow-color-end:var(--n-navigation-scroll-shadow-color-end, rgb(0, 0, 0, 0.1));--_n-navigation-scroll-mask:var(--n-navigation-scroll-mask, none);--_n-navigation-padding-block:var(--n-navigation-padding-block, var(--n-space-m));--_n-navigation-padding-inline:var(--n-navigation-padding-inline, var(--n-space-m));--_n-navigation-footer-padding-block:var(--n-navigation-footer-padding-block, var(--n-space-s));--_n-navigation-footer-padding-inline:var(--n-navigation-footer-padding-inline, var(--_n-navigation-padding-inline));--_n-navigation-header-block-size:var(--n-navigation-header-block-size, var(--n-space-xxl));--_n-navigation-header-padding-inline:var(--n-navigation-header-padding-inline);--_n-navigation-header-background:var(--n-navigation-header-background, var(--_n-navigation-background-color));--_n-navigation-header-divider:var(--n-navigation-header-divider, 1px solid var(--n-color-border));--_n-navigation-header-color:var(--n-navigation-header-color);--_n-navigation-header-toggle-icon-color:var(--n-navigation-header-toggle-icon-color);--_n-navigation-header-focus-style:var(--n-navigation-header-focus-style, var(--_n-navigation-dropdown-box-shadow));--_n-navigation-dropdown-box-shadow:0 0 0 1px var(--n-color-accent) inset;display:flex;flex-direction:column;block-size:100%;background:var(--_n-navigation-background-color)}.n-navigation-main{display:flex;flex-direction:column;flex:1;overflow-y:auto;overscroll-behavior-y:none;overflow-x:hidden}@-moz-document url-prefix(){.n-navigation-main{scrollbar-width:thin}}.n-navigation-main::-webkit-scrollbar{inline-size:6px;block-size:6px}.n-navigation-main::-webkit-scrollbar-thumb{background-color:var(--n-color-border-strong);border-radius:var(--n-border-radius-pill)}.n-navigation-main::-webkit-scrollbar-thumb:hover{background-color:var(--n-color-border-hover)}.n-navigation-main::-webkit-scrollbar-corner,.n-navigation-main::-webkit-scrollbar-track{background:0 0}nav{flex:1;padding-block-start:var(--_n-navigation-padding-block);padding-block-end:var(--_n-navigation-padding-block);padding-inline-start:var(--_n-navigation-padding-inline);padding-inline-end:var(--_n-navigation-padding-inline)}::slotted(nord-nav-group[heading]:first-of-type){margin-block-start:calc(var(--n-space-s) * -1)}slot[name=footer],slot[name=header]{display:flex;flex-direction:column;justify-content:center;--n-button-text-align:start;--n-button-background-color:transparent;--n-button-border-color:transparent;--n-button-box-shadow:none;--n-button-gradient:none}slot[name=header]{--n-button-border-radius:0;--n-button-color:var(--_n-navigation-header-color);--n-button-min-block-size:var(--_n-navigation-header-block-size);--n-select-block-size:var(--n-button-min-block-size);--n-button-font-weight:var(--n-font-weight-active);--n-button-toggle-icon-color:var(--_n-navigation-header-toggle-icon-color);--n-button-padding-inline:var(--_n-navigation-header-padding-inline);block-size:var(--_n-navigation-header-block-size);background-color:var(--_n-navigation-header-background);border-block-end:var(--_n-navigation-header-divider)}slot[name=footer]{--n-button-color:var(--n-color-text-weak);--n-button-padding-inline:var(--n-space-xs);margin:var(--n-space-m)}:host([data-rail-collapsed]) slot[name=header]{--n-button-padding-inline:0;--n-button-text-align:center;--n-button-content-display:none;overflow:hidden}:host([data-rail-collapsed]) slot[name=footer]{--n-button-padding-inline:0;--n-button-content-display:none;overflow:hidden}:is([name=header],[name=footer])::slotted(:is(nord-button,nord-dropdown):not([open]):focus-
|
|
1
|
+
import{_ as n}from"./tslib.es6-CmLYFWVC.js";import{css as o,LitElement as t,html as a,nothing as e}from"lit";import{property as i,customElement as r}from"lit/decorators.js";import{E as s}from"./EventController-BBOmvfLa.js";import{S as l}from"./SlotController-Z6eG7LSZ.js";import{s as d}from"./Component-DSU3Qp0O.js";const c=o`:host{--_n-navigation-background-color:var(--n-navigation-background-color, var(--n-color-nav-surface));--_n-navigation-scroll-shadow-color-start:var(--n-navigation-scroll-shadow-color-start, rgb(0, 0, 0, 0.1));--_n-navigation-scroll-shadow-color-end:var(--n-navigation-scroll-shadow-color-end, rgb(0, 0, 0, 0.1));--_n-navigation-scroll-mask:var(--n-navigation-scroll-mask, none);--_n-navigation-padding-block:var(--n-navigation-padding-block, var(--n-space-m));--_n-navigation-padding-inline:var(--n-navigation-padding-inline, var(--n-space-m));--_n-navigation-footer-padding-block:var(--n-navigation-footer-padding-block, var(--n-space-s));--_n-navigation-footer-padding-inline:var(--n-navigation-footer-padding-inline, var(--_n-navigation-padding-inline));--_n-navigation-header-block-size:var(--n-navigation-header-block-size, var(--n-space-xxl));--_n-navigation-header-padding-inline:var(--n-navigation-header-padding-inline);--_n-navigation-header-background:var(--n-navigation-header-background, var(--_n-navigation-background-color));--_n-navigation-header-divider:var(--n-navigation-header-divider, 1px solid var(--n-color-border));--_n-navigation-header-color:var(--n-navigation-header-color);--_n-navigation-header-toggle-icon-color:var(--n-navigation-header-toggle-icon-color);--_n-navigation-header-focus-style:var(--n-navigation-header-focus-style, var(--_n-navigation-dropdown-box-shadow));--_n-navigation-dropdown-box-shadow:0 0 0 1px var(--n-color-accent) inset;display:flex;flex-direction:column;block-size:100%;background:var(--_n-navigation-background-color)}.n-navigation-main{display:flex;flex-direction:column;flex:1;overflow-y:auto;overscroll-behavior-y:none;overflow-x:hidden}@-moz-document url-prefix(){.n-navigation-main{scrollbar-width:thin}}.n-navigation-main::-webkit-scrollbar{inline-size:6px;block-size:6px}.n-navigation-main::-webkit-scrollbar-thumb{background-color:var(--n-color-border-strong);border-radius:var(--n-border-radius-pill)}.n-navigation-main::-webkit-scrollbar-thumb:hover{background-color:var(--n-color-border-hover)}.n-navigation-main::-webkit-scrollbar-corner,.n-navigation-main::-webkit-scrollbar-track{background:0 0}nav{flex:1;padding-block-start:var(--_n-navigation-padding-block);padding-block-end:var(--_n-navigation-padding-block);padding-inline-start:var(--_n-navigation-padding-inline);padding-inline-end:var(--_n-navigation-padding-inline)}::slotted(nord-nav-group[heading]:first-of-type){margin-block-start:calc(var(--n-space-s) * -1)}slot[name=footer],slot[name=header]{display:flex;flex-direction:column;justify-content:center;--n-button-text-align:start;--n-button-background-color:transparent;--n-button-border-color:transparent;--n-button-box-shadow:none;--n-button-gradient:none}slot[name=header]{--n-button-border-radius:0;--n-button-color:var(--_n-navigation-header-color);--n-button-min-block-size:var(--_n-navigation-header-block-size);--n-select-block-size:var(--n-button-min-block-size);--n-button-font-weight:var(--n-font-weight-active);--n-button-toggle-icon-color:var(--_n-navigation-header-toggle-icon-color);--n-button-padding-inline:var(--_n-navigation-header-padding-inline);block-size:var(--_n-navigation-header-block-size);background-color:var(--_n-navigation-header-background);border-block-end:var(--_n-navigation-header-divider)}slot[name=footer]{--n-button-color:var(--n-color-text-weak);--n-button-padding-inline:var(--n-space-xs);margin:var(--n-space-m)}:host([data-rail-collapsed]) slot[name=header]{--n-button-padding-inline:0;--n-button-text-align:center;--n-button-content-display:none;overflow:hidden}:host([data-rail-collapsed]) slot[name=footer]{--n-button-padding-inline:0;--n-button-content-display:none;overflow:hidden}:is([name=header],[name=footer])::slotted(:is(nord-button,nord-dropdown):not([open]):focus-visible){--n-button-border-color:var(--n-color-accent);--n-button-box-shadow:var(--_n-navigation-dropdown-box-shadow)}slot[name=header]::slotted(:is(nord-button,nord-dropdown):not([open],:active):focus-visible){--n-button-box-shadow:var(--_n-navigation-header-focus-style)}[name=footer]::slotted(:is(nord-button,nord-dropdown):is(:hover,[open])){--n-button-border-color:var(--n-color-border-hover)}:host([sticky-footer]) slot[name=footer]{background:var(--_n-navigation-background-color);z-index:var(--n-index-sticky);padding-block-start:var(--_n-navigation-footer-padding-block);padding-block-end:var(--_n-navigation-footer-padding-block);padding-inline-start:var(--_n-navigation-footer-padding-inline);padding-inline-end:var(--_n-navigation-footer-padding-inline);margin:0;position:sticky;inset-block-end:0}:host([sticky-footer]) .n-navigation-main{background:linear-gradient(var(--_n-navigation-background-color) 30%,rgb(255,255,255,0)) center top,linear-gradient(rgb(255,255,255,0),var(--_n-navigation-background-color) 70%) center bottom,radial-gradient(farthest-side at 50% 0,var(--_n-navigation-scroll-shadow-color-start),rgb(0,0,0,0)) center top,radial-gradient(farthest-side at 50% 100%,var(--_n-navigation-scroll-shadow-color-end),rgb(0,0,0,0)) center bottom;background-repeat:no-repeat;background-size:100% 40px,100% 40px,100% 7px,100% 7px;background-attachment:local,local,scroll,scroll;-webkit-mask-image:var(--_n-navigation-scroll-mask);mask-image:var(--_n-navigation-scroll-mask)}`,v="undefined"!=typeof matchMedia?matchMedia("(min-width: 768px)"):{matches:!1,addEventListener(){},removeEventListener(){}};let g=class extends t{constructor(){super(...arguments),this.headerSlot=new l(this,"header"),this.events=new s(this),this.footerObserver=null,this.allowItemsToRemainOpen=!1,this.stickyFooter=!1,this.handleViewportChange=()=>this.updateRailState(),this.handleActivate=n=>{var o,t;const a=n.target,e=new Set;let i=null===(o=a.parentElement)||void 0===o?void 0:o.closest("nord-nav-item");for(;i;)e.add(i),i=null===(t=i.parentElement)||void 0===t?void 0:t.closest("nord-nav-item");for(const n of this.querySelectorAll("nord-nav-item"))if(n!==a){const o=e.has(n);n.open=o||this.allowItemsToRemainOpen&&n.open,n.active=o}this.allowItemsToRemainOpen=!1}}connectedCallback(){super.connectedCallback(),this.events.listen(this,"activate",this.handleActivate);const n=this.querySelector("nord-nav-item[active]");this.allowItemsToRemainOpen=null!=n,"undefined"!=typeof window&&this.observeFooter(),this.startObservingLayout(),v.addEventListener("change",this.handleViewportChange)}disconnectedCallback(){var n;super.disconnectedCallback(),null===(n=this.footerObserver)||void 0===n||n.disconnect(),this.footerObserver=null,this.stopObservingLayout(),v.removeEventListener("change",this.handleViewportChange)}startObservingLayout(){this.observedLayout=this.closest("nord-layout"),this.observedLayout&&(this.updateRailState(),this.layoutObserver=new MutationObserver((()=>this.updateRailState())),this.layoutObserver.observe(this.observedLayout,{attributes:!0,attributeFilter:["nav-open","collapse-mode"]}))}stopObservingLayout(){var n;null===(n=this.layoutObserver)||void 0===n||n.disconnect(),this.layoutObserver=void 0,this.observedLayout=void 0,this.toggleAttribute("data-rail-collapsed",!1)}updateRailState(){if(!this.observedLayout)return;const n=v.matches&&"rail"===this.observedLayout.getAttribute("collapse-mode")&&!this.observedLayout.hasAttribute("nav-open");this.toggleAttribute("data-rail-collapsed",n)}observeFooter(){this.footerObserver=new MutationObserver((()=>this.requestUpdate())),this.footerObserver.observe(this,{childList:!0});const n=this.querySelector(':scope > [slot="footer"]');n&&this.footerObserver.observe(n,{attributes:!0,attributeFilter:["style","class"]})}get isFooterVisible(){if("undefined"==typeof window)return!0;const n=this.querySelector(':scope > [slot="footer"]');if(!n)return!1;const o=window.getComputedStyle(n);return"none"!==o.display&&"hidden"!==o.visibility&&"collapse"!==o.visibility&&"0"!==o.opacity&&"hidden"!==o.contentVisibility}render(){return a`<slot name="header" ?hidden="${this.headerSlot.isEmpty}"></slot><div class="n-navigation-main"><nav><slot></slot></nav>${this.stickyFooter?e:a`<slot name="footer" ?hidden="${!this.isFooterVisible}"></slot>`}</div>${this.stickyFooter?a`<slot name="footer" ?hidden="${!this.isFooterVisible}"></slot>`:e}`}};g.styles=[d,c],n([i({type:Boolean,reflect:!0,attribute:"sticky-footer"})],g.prototype,"stickyFooter",void 0),g=n([r("nord-navigation")],g);var b=g;export{b as default};
|
|
2
2
|
//# sourceMappingURL=Navigation.js.map
|
package/lib/Navigation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Navigation.js","sources":["../src/navigation/Navigation.ts"],"sourcesContent":["import type NavItem from '../nav-item/NavItem.js'\nimport { html, LitElement, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { EventController } from '../common/controllers/EventController.js'\nimport { SlotController } from '../common/controllers/SlotController.js'\nimport componentStyle from '../common/styles/Component.css'\nimport style from './Navigation.css'\n\n// Mirrors the layout's wide-viewport breakpoint. Used to drive the\n// `data-rail-collapsed` attribute below — Safari doesn't support\n// `:host-context()`, so we propagate the layout's rail state via an\n// observed attribute on the host instead.\nconst railMediaQuery\n = typeof matchMedia !== 'undefined'\n ? matchMedia('(min-width: 768px)')\n : ({\n matches: false,\n addEventListener() {\n /* noop */\n },\n removeEventListener() {\n /* noop */\n },\n } as unknown as MediaQueryList)\n\n/**\n * Navigation is used to display the primary navigation in the sidebar\n * of an application. Navigation includes a list of links that users\n * use to move between sections of the application.\n *\n * @status ready\n * @category navigation\n * @slot - The main section of the sidebar, for holding nav components.\n * @slot header - The top section of the sidebar.\n * @slot footer - The bottom section of the sidebar.\n *\n * @fires {NordEvent} toggle - Dispatched whenever a nav item's state changes between open and closed.\n * @fires {NordEvent} activate - Dispatched whenever a nav item has been marked as active\n *\n * @cssprop [--n-navigation-background-color=var(--n-color-nav-surface)] - Controls the background color of the navigation element.\n * @cssprop [--n-navigation-scroll-shadow-color-start=rgb(0, 0, 0, 0.1)] - Color of the scroll-affordance shadow shown at the top of the scroll area (below the header) when `sticky-footer` is set. Set to `transparent` to disable.\n * @cssprop [--n-navigation-scroll-shadow-color-end=rgb(0, 0, 0, 0.1)] - Color of the scroll-affordance shadow shown at the bottom of the scroll area (above the footer) when `sticky-footer` is set. Set to `transparent` to disable.\n * @cssprop [--n-navigation-scroll-mask=none] - `mask-image` applied to the scrollable area when `sticky-footer` is set. Use with the shadow colors set to `transparent` to replace the dark scroll-shadow with a soft content fade at the edges. Example: `linear-gradient(to bottom, transparent 0, black 24px, black calc(100% - 24px), transparent 100%)`.\n * @cssprop [--n-navigation-padding-block=var(--n-space-m)] - Block-axis (top/bottom) padding of the `<nav>` element that wraps the slotted nav groups.\n * @cssprop [--n-navigation-padding-inline=var(--n-space-m)] - Inline-axis (start/end) padding of the `<nav>` element that wraps the slotted nav groups.\n * @cssprop [--n-navigation-footer-padding-block=var(--n-space-s)] - Block-axis (top/bottom) padding inside the footer slot when `sticky-footer` is set. Has no visual effect when the footer slot is empty (slot is hidden in that case).\n * @cssprop [--n-navigation-footer-padding-inline=var(--n-navigation-padding-inline)] - Inline-axis (start/end) padding inside the footer slot when `sticky-footer` is set. Defaults to `--n-navigation-padding-inline` so the footer aligns with the nav content unless overridden. Has no visual effect when the footer slot is empty.\n */\n@customElement('nord-navigation')\nexport default class Navigation extends LitElement {\n static styles = [componentStyle, style]\n\n private headerSlot = new SlotController(this, 'header')\n private events = new EventController(this)\n private footerObserver: MutationObserver | null = null\n private layoutObserver?: MutationObserver\n private observedLayout?: HTMLElement | null\n private allowItemsToRemainOpen = false\n\n /**\n * Controls whether the navigations's footer has sticky positioning.\n */\n @property({ type: Boolean, reflect: true, attribute: 'sticky-footer' }) stickyFooter: boolean = false\n\n connectedCallback() {\n super.connectedCallback()\n // if we add this event listener during render\n // we seem to miss the first activate event.\n // so let's add it as early as possible.\n this.events.listen(this, 'activate', this.handleActivate)\n\n // in cases where there is an initially active nav-item,\n // we should allow any nav-items with the `open` attribute to remain open.\n const activeItem = this.querySelector('nord-nav-item[active]')\n this.allowItemsToRemainOpen = activeItem != null\n\n if (typeof window !== 'undefined') {\n this.observeFooter()\n }\n this.startObservingLayout()\n railMediaQuery.addEventListener('change', this.handleViewportChange)\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.footerObserver?.disconnect()\n this.footerObserver = null\n this.stopObservingLayout()\n railMediaQuery.removeEventListener('change', this.handleViewportChange)\n }\n\n private handleViewportChange = () => this.updateRailState()\n\n private startObservingLayout() {\n this.observedLayout = this.closest<HTMLElement>('nord-layout')\n if (!this.observedLayout) {\n return\n }\n this.updateRailState()\n this.layoutObserver = new MutationObserver(() => this.updateRailState())\n this.layoutObserver.observe(this.observedLayout, {\n attributes: true,\n attributeFilter: ['nav-open', 'collapse-mode'],\n })\n }\n\n private stopObservingLayout() {\n this.layoutObserver?.disconnect()\n this.layoutObserver = undefined\n this.observedLayout = undefined\n this.toggleAttribute('data-rail-collapsed', false)\n }\n\n private updateRailState() {\n if (!this.observedLayout) {\n return\n }\n const next = railMediaQuery.matches\n && this.observedLayout.getAttribute('collapse-mode') === 'rail'\n && !this.observedLayout.hasAttribute('nav-open')\n this.toggleAttribute('data-rail-collapsed', next)\n }\n\n /**\n * Observe the footer slot element for visibility changes.\n * Watches style and class attributes on the footer element itself, and\n * childList on the host to detect footer being added/removed (e.g. v-if).\n */\n private observeFooter() {\n this.footerObserver = new MutationObserver(() => this.requestUpdate())\n\n // Watch for footer element being added/removed from the host\n this.footerObserver.observe(this, { childList: true })\n\n // Watch hidden attribute on the footer element directly\n const footerEl = this.querySelector(':scope > [slot=\"footer\"]')\n if (footerEl) {\n this.footerObserver.observe(footerEl, {\n attributes: true,\n attributeFilter: ['style', 'class'],\n })\n }\n }\n\n private get isFooterVisible() {\n // In SSR, the element is backed by a minimal DOM shim that does not implement\n // querySelector or window.getComputedStyle. Return true so the footer slot\n // renders as visible during server-side rendering.\n if (typeof window === 'undefined') {\n return true\n }\n\n const footerEl = this.querySelector<HTMLElement>(':scope > [slot=\"footer\"]')\n if (!footerEl) {\n return false\n }\n\n const style = window.getComputedStyle(footerEl)\n return (\n style.display !== 'none'\n && style.visibility !== 'hidden'\n && style.visibility !== 'collapse'\n && style.opacity !== '0'\n && style.contentVisibility !== 'hidden'\n )\n }\n\n render() {\n return html`\n <slot name=\"header\" ?hidden=${this.headerSlot.isEmpty}></slot>\n <div class=\"n-navigation-main\">\n <nav>\n <slot></slot>\n </nav>\n ${!this.stickyFooter ? html`<slot name=\"footer\" ?hidden=${!this.isFooterVisible}></slot>` : nothing}\n </div>\n ${this.stickyFooter ? html`<slot name=\"footer\" ?hidden=${!this.isFooterVisible}></slot>` : nothing}\n `\n }\n\n private handleActivate = (e: Event) => {\n const target = e.target as NavItem\n const ancestors = new Set<NavItem>()\n\n // walk up the DOM, gathering all ancestors from the target to its top-most nav-item.\n // it's more efficient to first gather ancestors here,\n // than to repeatedly call item.contains(target) in the for-loop below.\n let parent = target.parentElement?.closest('nord-nav-item')\n while (parent) {\n ancestors.add(parent)\n parent = parent.parentElement?.closest('nord-nav-item')\n }\n\n // open and activate any items on the path, otherwise close and deactivate.\n // however, if the item is set to open initially, we should not close it.\n for (const item of this.querySelectorAll('nord-nav-item')) {\n if (item !== target) {\n const isAncestor = ancestors.has(item)\n item.open = isAncestor || (this.allowItemsToRemainOpen && item.open)\n item.active = isAncestor\n }\n }\n\n // this is a one-time thing, purely for initial open state\n this.allowItemsToRemainOpen = false\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'nord-navigation': Navigation\n }\n}\n"],"names":["railMediaQuery","matchMedia","matches","addEventListener","removeEventListener","Navigation","LitElement","constructor","this","headerSlot","SlotController","events","EventController","footerObserver","allowItemsToRemainOpen","stickyFooter","handleViewportChange","updateRailState","handleActivate","e","target","ancestors","Set","parent","_a","parentElement","closest","add","_b","item","querySelectorAll","isAncestor","has","open","active","connectedCallback","super","listen","activeItem","querySelector","window","observeFooter","startObservingLayout","disconnectedCallback","disconnect","stopObservingLayout","observedLayout","layoutObserver","MutationObserver","observe","attributes","attributeFilter","undefined","toggleAttribute","next","getAttribute","hasAttribute","requestUpdate","childList","footerEl","isFooterVisible","style","getComputedStyle","display","visibility","opacity","contentVisibility","render","html","isEmpty","nothing","styles","componentStyle","__decorate","property","type","Boolean","reflect","attribute","prototype","customElement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Navigation.js","sources":["../src/navigation/Navigation.ts"],"sourcesContent":["import type NavItem from '../nav-item/NavItem.js'\nimport { html, LitElement, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { EventController } from '../common/controllers/EventController.js'\nimport { SlotController } from '../common/controllers/SlotController.js'\nimport componentStyle from '../common/styles/Component.css'\nimport style from './Navigation.css'\n\n// Mirrors the layout's wide-viewport breakpoint. Used to drive the\n// `data-rail-collapsed` attribute below — Safari doesn't support\n// `:host-context()`, so we propagate the layout's rail state via an\n// observed attribute on the host instead.\nconst railMediaQuery\n = typeof matchMedia !== 'undefined'\n ? matchMedia('(min-width: 768px)')\n : ({\n matches: false,\n addEventListener() {\n /* noop */\n },\n removeEventListener() {\n /* noop */\n },\n } as unknown as MediaQueryList)\n\n/**\n * Navigation is used to display the primary navigation in the sidebar\n * of an application. Navigation includes a list of links that users\n * use to move between sections of the application.\n *\n * @status ready\n * @category navigation\n * @slot - The main section of the sidebar, for holding nav components.\n * @slot header - The top section of the sidebar.\n * @slot footer - The bottom section of the sidebar.\n *\n * @fires {NordEvent} toggle - Dispatched whenever a nav item's state changes between open and closed.\n * @fires {NordEvent} activate - Dispatched whenever a nav item has been marked as active\n *\n * @cssprop [--n-navigation-background-color=var(--n-color-nav-surface)] - Controls the background color of the navigation element.\n * @cssprop [--n-navigation-scroll-shadow-color-start=rgb(0, 0, 0, 0.1)] - Color of the scroll-affordance shadow shown at the top of the scroll area (below the header) when `sticky-footer` is set. Set to `transparent` to disable.\n * @cssprop [--n-navigation-scroll-shadow-color-end=rgb(0, 0, 0, 0.1)] - Color of the scroll-affordance shadow shown at the bottom of the scroll area (above the footer) when `sticky-footer` is set. Set to `transparent` to disable.\n * @cssprop [--n-navigation-scroll-mask=none] - `mask-image` applied to the scrollable area when `sticky-footer` is set. Use with the shadow colors set to `transparent` to replace the dark scroll-shadow with a soft content fade at the edges. Example: `linear-gradient(to bottom, transparent 0, black 24px, black calc(100% - 24px), transparent 100%)`.\n * @cssprop [--n-navigation-padding-block=var(--n-space-m)] - Block-axis (top/bottom) padding of the `<nav>` element that wraps the slotted nav groups.\n * @cssprop [--n-navigation-padding-inline=var(--n-space-m)] - Inline-axis (start/end) padding of the `<nav>` element that wraps the slotted nav groups.\n * @cssprop [--n-navigation-footer-padding-block=var(--n-space-s)] - Block-axis (top/bottom) padding inside the footer slot when `sticky-footer` is set. Has no visual effect when the footer slot is empty (slot is hidden in that case).\n * @cssprop [--n-navigation-footer-padding-inline=var(--n-navigation-padding-inline)] - Inline-axis (start/end) padding inside the footer slot when `sticky-footer` is set. Defaults to `--n-navigation-padding-inline` so the footer aligns with the nav content unless overridden. Has no visual effect when the footer slot is empty.\n */\n@customElement('nord-navigation')\nexport default class Navigation extends LitElement {\n static styles = [componentStyle, style]\n\n private headerSlot = new SlotController(this, 'header')\n private events = new EventController(this)\n private footerObserver: MutationObserver | null = null\n private layoutObserver?: MutationObserver\n private observedLayout?: HTMLElement | null\n private allowItemsToRemainOpen = false\n\n /**\n * Controls whether the navigations's footer has sticky positioning.\n */\n @property({ type: Boolean, reflect: true, attribute: 'sticky-footer' }) stickyFooter: boolean = false\n\n connectedCallback() {\n super.connectedCallback()\n // if we add this event listener during render\n // we seem to miss the first activate event.\n // so let's add it as early as possible.\n this.events.listen(this, 'activate', this.handleActivate)\n\n // in cases where there is an initially active nav-item,\n // we should allow any nav-items with the `open` attribute to remain open.\n const activeItem = this.querySelector('nord-nav-item[active]')\n this.allowItemsToRemainOpen = activeItem != null\n\n if (typeof window !== 'undefined') {\n this.observeFooter()\n }\n this.startObservingLayout()\n railMediaQuery.addEventListener('change', this.handleViewportChange)\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.footerObserver?.disconnect()\n this.footerObserver = null\n this.stopObservingLayout()\n railMediaQuery.removeEventListener('change', this.handleViewportChange)\n }\n\n private handleViewportChange = () => this.updateRailState()\n\n private startObservingLayout() {\n this.observedLayout = this.closest<HTMLElement>('nord-layout')\n if (!this.observedLayout) {\n return\n }\n this.updateRailState()\n this.layoutObserver = new MutationObserver(() => this.updateRailState())\n this.layoutObserver.observe(this.observedLayout, {\n attributes: true,\n attributeFilter: ['nav-open', 'collapse-mode'],\n })\n }\n\n private stopObservingLayout() {\n this.layoutObserver?.disconnect()\n this.layoutObserver = undefined\n this.observedLayout = undefined\n this.toggleAttribute('data-rail-collapsed', false)\n }\n\n private updateRailState() {\n if (!this.observedLayout) {\n return\n }\n const next = railMediaQuery.matches\n && this.observedLayout.getAttribute('collapse-mode') === 'rail'\n && !this.observedLayout.hasAttribute('nav-open')\n this.toggleAttribute('data-rail-collapsed', next)\n }\n\n /**\n * Observe the footer slot element for visibility changes.\n * Watches style and class attributes on the footer element itself, and\n * childList on the host to detect footer being added/removed (e.g. v-if).\n */\n private observeFooter() {\n this.footerObserver = new MutationObserver(() => this.requestUpdate())\n\n // Watch for footer element being added/removed from the host\n this.footerObserver.observe(this, { childList: true })\n\n // Watch hidden attribute on the footer element directly\n const footerEl = this.querySelector(':scope > [slot=\"footer\"]')\n if (footerEl) {\n this.footerObserver.observe(footerEl, {\n attributes: true,\n attributeFilter: ['style', 'class'],\n })\n }\n }\n\n private get isFooterVisible() {\n // In SSR, the element is backed by a minimal DOM shim that does not implement\n // querySelector or window.getComputedStyle. Return true so the footer slot\n // renders as visible during server-side rendering.\n if (typeof window === 'undefined') {\n return true\n }\n\n const footerEl = this.querySelector<HTMLElement>(':scope > [slot=\"footer\"]')\n if (!footerEl) {\n return false\n }\n\n const style = window.getComputedStyle(footerEl)\n return (\n style.display !== 'none'\n && style.visibility !== 'hidden'\n && style.visibility !== 'collapse'\n && style.opacity !== '0'\n && style.contentVisibility !== 'hidden'\n )\n }\n\n render() {\n return html`\n <slot name=\"header\" ?hidden=${this.headerSlot.isEmpty}></slot>\n <div class=\"n-navigation-main\">\n <nav>\n <slot></slot>\n </nav>\n ${!this.stickyFooter ? html`<slot name=\"footer\" ?hidden=${!this.isFooterVisible}></slot>` : nothing}\n </div>\n ${this.stickyFooter ? html`<slot name=\"footer\" ?hidden=${!this.isFooterVisible}></slot>` : nothing}\n `\n }\n\n private handleActivate = (e: Event) => {\n const target = e.target as NavItem\n const ancestors = new Set<NavItem>()\n\n // walk up the DOM, gathering all ancestors from the target to its top-most nav-item.\n // it's more efficient to first gather ancestors here,\n // than to repeatedly call item.contains(target) in the for-loop below.\n let parent = target.parentElement?.closest('nord-nav-item')\n while (parent) {\n ancestors.add(parent)\n parent = parent.parentElement?.closest('nord-nav-item')\n }\n\n // open and activate any items on the path, otherwise close and deactivate.\n // however, if the item is set to open initially, we should not close it.\n for (const item of this.querySelectorAll('nord-nav-item')) {\n if (item !== target) {\n const isAncestor = ancestors.has(item)\n item.open = isAncestor || (this.allowItemsToRemainOpen && item.open)\n item.active = isAncestor\n }\n }\n\n // this is a one-time thing, purely for initial open state\n this.allowItemsToRemainOpen = false\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'nord-navigation': Navigation\n }\n}\n"],"names":["railMediaQuery","matchMedia","matches","addEventListener","removeEventListener","Navigation","LitElement","constructor","this","headerSlot","SlotController","events","EventController","footerObserver","allowItemsToRemainOpen","stickyFooter","handleViewportChange","updateRailState","handleActivate","e","target","ancestors","Set","parent","_a","parentElement","closest","add","_b","item","querySelectorAll","isAncestor","has","open","active","connectedCallback","super","listen","activeItem","querySelector","window","observeFooter","startObservingLayout","disconnectedCallback","disconnect","stopObservingLayout","observedLayout","layoutObserver","MutationObserver","observe","attributes","attributeFilter","undefined","toggleAttribute","next","getAttribute","hasAttribute","requestUpdate","childList","footerEl","isFooterVisible","style","getComputedStyle","display","visibility","opacity","contentVisibility","render","html","isEmpty","nothing","styles","componentStyle","__decorate","property","type","Boolean","reflect","attribute","prototype","customElement"],"mappings":"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"}
|