@vielzeug/refine 26.10.10 → 26.10.11
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/README.md +2 -0
- package/THIRD_PARTY_NOTICES +116 -0
- package/dist/content/icon/icon.d.ts +8 -9
- package/dist/content/icon/icon.d.ts.map +1 -1
- package/dist/content/index.d.ts +6 -1
- package/dist/content/index.d.ts.map +1 -1
- package/dist/custom-elements.json +4481 -4035
- package/dist/icon.cjs +4 -4
- package/dist/icon.cjs.map +1 -1
- package/dist/icon.js +4 -4
- package/dist/icon.js.map +1 -1
- package/dist/icons/_iconify-map.cjs +2 -0
- package/dist/icons/_iconify-map.cjs.map +1 -0
- package/dist/icons/_iconify-map.d.ts +5 -0
- package/dist/icons/_iconify-map.d.ts.map +1 -0
- package/dist/icons/_iconify-map.js +2 -0
- package/dist/icons/_iconify-map.js.map +1 -0
- package/dist/icons/defaults.cjs +2 -0
- package/dist/icons/defaults.cjs.map +1 -0
- package/dist/icons/defaults.d.ts +2 -0
- package/dist/icons/defaults.d.ts.map +1 -0
- package/dist/icons/defaults.js +2 -0
- package/dist/icons/defaults.js.map +1 -0
- package/dist/icons/definition.d.ts +13 -0
- package/dist/icons/definition.d.ts.map +1 -0
- package/dist/icons/heroicons.cjs +2 -0
- package/dist/icons/heroicons.cjs.map +1 -0
- package/dist/icons/heroicons.d.ts +9 -0
- package/dist/icons/heroicons.d.ts.map +1 -0
- package/dist/icons/heroicons.js +2 -0
- package/dist/icons/heroicons.js.map +1 -0
- package/dist/icons/iconify.cjs +2 -0
- package/dist/icons/iconify.cjs.map +1 -0
- package/dist/icons/iconify.d.ts +34 -0
- package/dist/icons/iconify.d.ts.map +1 -0
- package/dist/icons/iconify.js +2 -0
- package/dist/icons/iconify.js.map +1 -0
- package/dist/icons/index.cjs +1 -0
- package/dist/icons/index.d.ts +3 -0
- package/dist/icons/index.d.ts.map +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/icons/lucide/all.cjs +2 -0
- package/dist/icons/lucide/all.cjs.map +1 -0
- package/dist/icons/lucide/all.d.ts +2 -0
- package/dist/icons/lucide/all.d.ts.map +1 -0
- package/dist/icons/lucide/all.js +2 -0
- package/dist/icons/lucide/all.js.map +1 -0
- package/dist/icons/lucide.cjs +2 -0
- package/dist/icons/lucide.cjs.map +1 -0
- package/dist/icons/lucide.d.ts +5 -0
- package/dist/icons/lucide.d.ts.map +1 -0
- package/dist/icons/lucide.js +2 -0
- package/dist/icons/lucide.js.map +1 -0
- package/dist/icons/mdi.cjs +2 -0
- package/dist/icons/mdi.cjs.map +1 -0
- package/dist/icons/mdi.d.ts +9 -0
- package/dist/icons/mdi.d.ts.map +1 -0
- package/dist/icons/mdi.js +2 -0
- package/dist/icons/mdi.js.map +1 -0
- package/dist/icons/registry.cjs +2 -0
- package/dist/icons/registry.cjs.map +1 -0
- package/dist/icons/registry.d.ts +7 -0
- package/dist/icons/registry.d.ts.map +1 -0
- package/dist/icons/registry.js +2 -0
- package/dist/icons/registry.js.map +1 -0
- package/dist/icons/svg.cjs +2 -0
- package/dist/icons/svg.cjs.map +1 -0
- package/dist/icons/svg.d.ts +8 -0
- package/dist/icons/svg.d.ts.map +1 -0
- package/dist/icons/svg.js +2 -0
- package/dist/icons/svg.js.map +1 -0
- package/dist/icons/tabler.cjs +2 -0
- package/dist/icons/tabler.cjs.map +1 -0
- package/dist/icons/tabler.d.ts +9 -0
- package/dist/icons/tabler.d.ts.map +1 -0
- package/dist/icons/tabler.js +2 -0
- package/dist/icons/tabler.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/inputs/message-composer/message-composer.d.ts +2 -2
- package/dist/inputs/message-composer/message-composer.d.ts.map +1 -1
- package/dist/inputs/select/select.d.ts +1 -1
- package/dist/inputs/select/select.d.ts.map +1 -1
- package/dist/layout/navbar/navbar.d.ts.map +1 -1
- package/dist/message-composer.cjs.map +1 -1
- package/dist/message-composer.js.map +1 -1
- package/dist/navbar.cjs +1 -1
- package/dist/navbar.cjs.map +1 -1
- package/dist/navbar.js +1 -1
- package/dist/navbar.js.map +1 -1
- package/dist/overlay/command-palette/command-palette.types.d.ts +1 -1
- package/dist/overlay/command-palette/command-palette.types.d.ts.map +1 -1
- package/dist/refine.cjs +153 -153
- package/dist/refine.cjs.map +1 -1
- package/dist/refine.iife.js +153 -153
- package/dist/refine.iife.js.map +1 -1
- package/dist/refine.js +129 -129
- package/dist/refine.js.map +1 -1
- package/dist/select.cjs.map +1 -1
- package/dist/select.js.map +1 -1
- package/dist/shared/prop-bundles.d.ts +3 -3
- package/package.json +81 -22
- package/dist/_virtual/_rolldown/runtime.cjs +0 -1
- package/dist/core/icon-registry.cjs +0 -2
- package/dist/core/icon-registry.cjs.map +0 -1
- package/dist/core/icon-registry.d.ts +0 -15
- package/dist/core/icon-registry.d.ts.map +0 -1
- package/dist/core/icon-registry.js +0 -2
- package/dist/core/icon-registry.js.map +0 -1
- package/dist/core/icons.cjs +0 -2
- package/dist/core/icons.cjs.map +0 -1
- package/dist/core/icons.d.ts +0 -2
- package/dist/core/icons.d.ts.map +0 -1
- package/dist/core/icons.js +0 -2
- package/dist/core/icons.js.map +0 -1
- package/dist/icon-lucide.cjs +0 -2
- package/dist/icon-lucide.cjs.map +0 -1
- package/dist/icon-lucide.d.ts +0 -2
- package/dist/icon-lucide.d.ts.map +0 -1
- package/dist/icon-lucide.js +0 -2
- package/dist/icon-lucide.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-composer.js","names":[],"sources":["../src/inputs/message-composer/message-composer.ts"],"sourcesContent":["import {\n bind,\n define,\n getHost,\n html,\n model,\n onCleanup,\n onElement,\n prop,\n ref,\n useEmit,\n useField,\n useSlots,\n} from '@vielzeug/ore';\nimport { computed } from '@vielzeug/ripple';\nimport {\n bindRefCallback,\n createAutoResize,\n createComposerControl,\n createTextField,\n lifecycleSignal,\n type SendShortcut,\n} from '../../core';\nimport {\n disablableBundle,\n loadableBundle,\n MESSAGE_COMPOSER_SIZE_PRESET,\n sizableBundle,\n themableBundle,\n} from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledLoadingMixin,\n fieldVariantMixin,\n forcedColorsFocusMixin,\n forcedColorsMixin,\n reducedMotionMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport { FIELD_VARIANTS, type FieldVariant } from '../../types';\nimport { errorAttr } from '../shared/field-binding';\nimport { defineFieldValue, dispatchNativeFieldEvent, setFieldValue } from '../shared/native-field-event';\nimport { renderFieldStatusRegion, renderStatusIcon } from '../shared/templates';\n\nimport componentStyles from './message-composer.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\nexport type { SendShortcut } from '../../core';\nexport { BUTTON_TAG } from '../button/button';\n\nconst DEFAULT_SEND_LABEL = 'Send message';\nconst SEND_ICON = 'arrow-up';\n\n/** Events emitted by the message composer */\nexport type OreMessageComposerEvents = {\n input: InputEvent;\n /**\n * Fired for a send attempt (the resolved `send-shortcut` or the send button) while the\n * composer isn't blank/disabled/loading. Cancelable: call `preventDefault()` to keep the\n * current text in the field instead of the default clear + refocus.\n */\n send: { originalEvent: KeyboardEvent | MouseEvent; value: string };\n};\n\n/** Message composer properties */\nexport type OreMessageComposerProps = {\n /** Clear the value and refocus after a non-cancelled `send` (default `true`) */\n 'clear-on-send'?: boolean;\n /** Theme color (card focus ring + default send button color) */\n color?: ThemeColor;\n /** Disable the whole composer (field, slots, and send action) */\n disabled?: boolean;\n /** Error message shown below the field */\n error?: string;\n /** Stretch the composer to the full width of its container */\n fullwidth?: boolean;\n /** Helper text shown below the field */\n helper?: string;\n /** Accessible name for the field: never rendered visually, only as `aria-label` */\n label?: string;\n /** Blocks further sends (e.g. a send is already in flight) without disabling editing */\n loading?: boolean;\n /** Maximum character count; shows a counter */\n maxlength?: number;\n /** Form field name */\n name?: string;\n /** Placeholder text */\n placeholder?: string;\n /** Make the field read-only */\n readonly?: boolean;\n /**\n * JS-only callback fired with the inner `<textarea>` element when it mounts, and with\n * `null` when it unmounts. Set as a JS property: `composer.ref = (el) => { ... }`.\n */\n ref?: ((el: HTMLTextAreaElement | null) => void) | null;\n /** Require a non-blank value for native form validation */\n required?: boolean;\n /** Number of visible rows before auto-resize grows the field */\n rows?: number;\n /** Lucide icon name for the default send button (default `'arrow-up'`) */\n 'send-icon'?: string;\n /** Accessible label for the send button (default `'Send message'`) */\n 'send-label'?: string;\n /** Keyboard shortcut that sends: 'enter' (Shift+Enter for a newline) or 'mod+enter' (Enter always inserts a newline; Ctrl/Cmd+Enter sends) */\n 'send-shortcut'?: SendShortcut;\n /** Component size */\n size?: ComponentSize;\n /**\n * Shows an inline green check icon inside the field to confirm the value has\n * passed validation. Ignored while `error` is set: an error always wins.\n */\n success?: boolean;\n /** Current text value */\n value?: string;\n /** Visual variant of the card: same variant set as `ore-textarea`, applied to the card. */\n variant?: FieldVariant;\n};\n\n/**\n * A message/comment composer: a card with an auto-resizing field on top and a toolbar row\n * below it, built directly on the same core primitives as `ore-textarea` (`createTextField`,\n * `createAutoResize`) rather than composing `ore-textarea` itself. Nesting a fully-styled sibling\n * component and suppressing most of its chrome (as an earlier version of this component did) is\n * a leaky composition: this owns its single `<textarea>` outright, so there's exactly one\n * implementation of the field's appearance to reason about, not two fighting each other.\n *\n * Handles the send gesture (Enter to send, Shift+Enter for a newline, or a Ctrl/Cmd+Enter\n * alternative), IME-safe composition, and clear-and-refocus after sending.\n *\n * @element ore-message-composer\n *\n * @attr {string} value - Current text value\n * @attr {string} placeholder - Placeholder text\n * @attr {string} label - Accessible name for the field (not rendered visually)\n * @attr {string} name - Form field name\n * @attr {string} helper - Helper text shown below the field\n * @attr {string} error - Error message shown below the field\n * @attr {boolean} success - Show an inline success check icon (suppressed while `error` is set)\n * @attr {boolean} disabled - Disable the whole composer\n * @attr {boolean} readonly - Read-only mode\n * @attr {boolean} required - Require a non-blank value for native form validation\n * @attr {boolean} loading - Blocks further sends without disabling editing\n * @attr {number} maxlength - Max character count; shows a counter\n * @attr {number} rows - Visible rows before auto-resize grows the field (default 1)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant of the card: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} size - 'sm' | 'md' | 'lg'\n * @attr {boolean} fullwidth - Stretch to the full width of the container\n * @attr {string} send-icon - Lucide icon name for the default send button (default 'arrow-up')\n * @attr {string} send-label - Accessible label for the send button (default 'Send message')\n * @attr {string} send-shortcut - 'enter' (default) | 'mod+enter'\n * @attr {boolean} clear-on-send - Clear the value and refocus after a non-cancelled send (default true)\n *\n * @fires input - Fired on every keystroke. detail: { value, originalEvent }\n * @fires send - Fired on a send attempt. Cancelable. detail: { value, originalEvent }\n *\n * @slot prefix - Content at the start of the toolbar row (e.g. an attach menu)\n * @slot suffix - Content at the end of the toolbar row, before the send button (e.g. a model picker)\n * @slot send - Replaces the default send button entirely: the only supported way to customize it\n *\n * @cssprop --message-composer-bg - Card background color\n * @cssprop --message-composer-border-color - Card border color\n * @cssprop --message-composer-radius - Card border radius\n * @cssprop --message-composer-padding - Card inner padding\n * @cssprop --message-composer-gap - Gap between the field/toolbar rows and between toolbar items\n * @cssprop --message-composer-placeholder-color - Field placeholder text color\n * @cssprop --message-composer-min-height - Minimum field height (default one line)\n * @cssprop --message-composer-hover-bg - Card background on hover (flat/ghost variants)\n * @cssprop --message-composer-hover-border-color - Card border on hover (flat/bordered variants)\n * @cssprop --message-composer-focus-bg - Card background when focused (flat variant)\n * @cssprop --message-composer-focus-border-color - Card border when focused (flat variant)\n *\n * @part composer - Root card container\n * @part field - The native `<textarea>` element\n * @part status-icon - The inline error/success icon shown inside the field\n * @part helper - Helper text element\n * @part error - Error text element (`role=\"alert\"`)\n * @part toolbar - Toolbar row below the field\n * @part toolbar-start - Toolbar group holding the `prefix` slot\n * @part toolbar-end - Toolbar group holding the `suffix` slot and the send button\n * @part send-button - The default send button (absent when the `send` slot is used)\n *\n * @example\n * ```html\n * <ore-message-composer placeholder=\"Message…\" send-shortcut=\"mod+enter\"></ore-message-composer>\n * ```\n */\nexport const MESSAGE_COMPOSER_TAG = 'ore-message-composer' as const;\ndefine<OreMessageComposerProps>(MESSAGE_COMPOSER_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...loadableBundle,\n 'clear-on-send': prop.bool(true),\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n maxlength: prop.json(undefined as number | undefined),\n name: prop.string(),\n placeholder: prop.string(),\n readonly: prop.bool(false),\n ref: prop.data<((el: HTMLTextAreaElement | null) => void) | null>(),\n required: prop.bool(false),\n rows: prop.json(undefined as number | undefined),\n 'send-icon': prop.string(),\n 'send-label': prop.string(),\n 'send-shortcut': prop.oneOf(['enter', 'mod+enter'] as const, 'enter'),\n success: prop.bool(false),\n value: prop.string(),\n variant: prop.oneOf(FIELD_VARIANTS),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreMessageComposerEvents>();\n const slots = useSlots<'prefix' | 'send' | 'suffix'>();\n\n const isDisabled = props.disabled;\n\n const abortSignal = lifecycleSignal(onCleanup);\n const textareaRef = ref<HTMLTextAreaElement>();\n const autoResize = createAutoResize();\n\n const tf = createTextField({\n disabled: isDisabled,\n error: props.error,\n helper: props.helper,\n maxLength: props.maxlength,\n onInput: (_event, value) => {\n setFieldValue(el, value);\n dispatchNativeFieldEvent(el, 'input');\n },\n prefix: 'composer',\n readonly: props.readonly,\n required: props.required,\n signal: abortSignal,\n value: props.value,\n });\n\n defineFieldValue(\n el,\n () => tf.value.value,\n (value) => {\n tf.value.value = value;\n },\n );\n\n // Directly form-associated now that the field is owned here rather than borrowed from a\n // nested `ore-textarea` (which previously registered with the ancestor `<form>` on this\n // component's behalf).\n tf.attachFormField(\n useField<string>({\n disabled: tf.disabled,\n onReset: () => {\n tf.reset();\n autoResize.recompute();\n },\n toFormValue: (v) => v,\n validity: tf.validity,\n value: tf.value,\n }),\n );\n\n function attemptSend(event: KeyboardEvent | MouseEvent): void {\n const notPrevented = emit('send', { originalEvent: event, value: tf.value.value.trim() });\n\n // `preventDefault()` on `send` means \"keep the current text: skip the default clear\n // + refocus\" (see the event's own doc comment): both halves of that default are gated on\n // the same condition, not just the clear.\n if (notPrevented && props['clear-on-send'].value !== false) {\n tf.clear();\n autoResize.recompute();\n textareaRef.value?.focus();\n }\n }\n\n const composer = createComposerControl({\n disabled: isDisabled,\n loading: computed(() => Boolean(props.loading.value)),\n onSend: attemptSend,\n sendShortcut: props['send-shortcut'],\n value: tf.value,\n });\n\n onElement(textareaRef, (textareaEl) => {\n const unwireField = tf.wire(textareaEl);\n const unwireAutoResize = autoResize.wire(textareaEl);\n const unwireRef = bindRefCallback(props.ref, textareaEl);\n const handleKeydown = (e: KeyboardEvent) => composer.handleKeydown(e);\n\n textareaEl.addEventListener('keydown', handleKeydown);\n\n return () => {\n textareaEl.removeEventListener('keydown', handleKeydown);\n unwireField();\n unwireAutoResize();\n unwireRef();\n };\n });\n\n bind({\n attr: {\n error: errorAttr(tf.errorText),\n size: props.size,\n // Reflects `success` only once `error` is confirmed empty: keeps the two host\n // attributes mutually exclusive even if a consumer sets both props at once.\n success: () => (props.success.value && !tf.errorText.value ? true : undefined),\n variant: props.variant,\n },\n });\n\n return html`\n <div class=\"composer\" part=\"composer\">\n <textarea\n class=\"field\"\n part=\"field\"\n ref=\"${textareaRef}\"\n rows=\"${() => props.rows.value ?? 1}\"\n name=\"${() => props.name.value ?? ''}\"\n placeholder=\"${() => props.placeholder.value ?? 'Message…'}\"\n maxlength=\"${() => props.maxlength.value}\"\n ?disabled=\"${isDisabled}\"\n ?readonly=\"${() => Boolean(props.readonly.value)}\"\n ?required=\"${() => Boolean(props.required.value)}\"\n value=\"${model(tf.value)}\"\n aria-label=\"${() => props.label.value || 'Message'}\"\n aria-keyshortcuts=\"${composer.keyShortcutsHint}\"\n aria-describedby=\"${tf.ariaDescribedBy}\"\n aria-errormessage=\"${tf.ariaErrorMessage}\"\n aria-invalid=\"${tf.ariaInvalid}\"></textarea>\n ${renderStatusIcon(tf.errorText)} ${renderFieldStatusRegion(tf)}\n <div class=\"toolbar\" part=\"toolbar\">\n <div class=\"toolbar-start\" part=\"toolbar-start\">\n <slot name=\"prefix\"></slot>\n </div>\n <div class=\"toolbar-end\" part=\"toolbar-end\">\n <slot name=\"suffix\"></slot>\n <slot name=\"send\"></slot>\n ${() =>\n slots.has('send').value\n ? ''\n : html`\n <ore-button\n class=\"send-btn\"\n part=\"send-button\"\n type=\"button\"\n icon-only\n variant=\"solid\"\n color=\"${() => props.color.value || 'primary'}\"\n size=\"${props.size}\"\n label=\"${() => props['send-label'].value || DEFAULT_SEND_LABEL}\"\n ?loading=\"${() => Boolean(props.loading.value)}\"\n ?disabled=\"${() => !composer.canSend.value}\"\n @click=\"${(e: MouseEvent) => composer.send(e)}\">\n <ore-icon name=\"${() => props['send-icon'].value || SEND_ICON}\" size=\"16\"></ore-icon>\n </ore-button>\n `}\n </div>\n </div>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [\n colorThemeMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n disabledLoadingMixin,\n forcedColorsMixin,\n forcedColorsFocusMixin('.field'),\n sizeVariantMixin(MESSAGE_COMPOSER_SIZE_PRESET),\n componentStyles,\n // Must come after `componentStyles`: see `ore-input`'s identical ordering note for why\n // (`@layer` precedence is fixed by which layer name is *first* referenced across this whole\n // array; `componentStyles` establishes `refine.base`, which this mixin's `refine.variants`\n // rules need to win over).\n fieldVariantMixin({ container: '.composer', text: '.field', tokenPrefix: 'message-composer' }),\n ],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"message-composer.js","names":[],"sources":["../src/inputs/message-composer/message-composer.ts"],"sourcesContent":["import {\n bind,\n define,\n getHost,\n html,\n model,\n onCleanup,\n onElement,\n prop,\n ref,\n useEmit,\n useField,\n useSlots,\n} from '@vielzeug/ore';\nimport { computed } from '@vielzeug/ripple';\nimport {\n bindRefCallback,\n createAutoResize,\n createComposerControl,\n createTextField,\n lifecycleSignal,\n type SendShortcut,\n} from '../../core';\nimport {\n disablableBundle,\n loadableBundle,\n MESSAGE_COMPOSER_SIZE_PRESET,\n sizableBundle,\n themableBundle,\n} from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledLoadingMixin,\n fieldVariantMixin,\n forcedColorsFocusMixin,\n forcedColorsMixin,\n reducedMotionMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport { FIELD_VARIANTS, type FieldVariant } from '../../types';\nimport { errorAttr } from '../shared/field-binding';\nimport { defineFieldValue, dispatchNativeFieldEvent, setFieldValue } from '../shared/native-field-event';\nimport { renderFieldStatusRegion, renderStatusIcon } from '../shared/templates';\n\nimport componentStyles from './message-composer.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\nexport type { SendShortcut } from '../../core';\nexport { BUTTON_TAG } from '../button/button';\n\nconst DEFAULT_SEND_LABEL = 'Send message';\nconst SEND_ICON = 'arrow-up';\n\n/** Events emitted by the message composer */\nexport type OreMessageComposerEvents = {\n input: InputEvent;\n /**\n * Fired for a send attempt (the resolved `send-shortcut` or the send button) while the\n * composer isn't blank/disabled/loading. Cancelable: call `preventDefault()` to keep the\n * current text in the field instead of the default clear + refocus.\n */\n send: { originalEvent: KeyboardEvent | MouseEvent; value: string };\n};\n\n/** Message composer properties */\nexport type OreMessageComposerProps = {\n /** Clear the value and refocus after a non-cancelled `send` (default `true`) */\n 'clear-on-send'?: boolean;\n /** Theme color (card focus ring + default send button color) */\n color?: ThemeColor;\n /** Disable the whole composer (field, slots, and send action) */\n disabled?: boolean;\n /** Error message shown below the field */\n error?: string;\n /** Stretch the composer to the full width of its container */\n fullwidth?: boolean;\n /** Helper text shown below the field */\n helper?: string;\n /** Accessible name for the field: never rendered visually, only as `aria-label` */\n label?: string;\n /** Blocks further sends (e.g. a send is already in flight) without disabling editing */\n loading?: boolean;\n /** Maximum character count; shows a counter */\n maxlength?: number;\n /** Form field name */\n name?: string;\n /** Placeholder text */\n placeholder?: string;\n /** Make the field read-only */\n readonly?: boolean;\n /**\n * JS-only callback fired with the inner `<textarea>` element when it mounts, and with\n * `null` when it unmounts. Set as a JS property: `composer.ref = (el) => { ... }`.\n */\n ref?: ((el: HTMLTextAreaElement | null) => void) | null;\n /** Require a non-blank value for native form validation */\n required?: boolean;\n /** Number of visible rows before auto-resize grows the field */\n rows?: number;\n /** Registered icon name for the default send button (default `'arrow-up'`) */\n 'send-icon'?: string;\n /** Accessible label for the send button (default `'Send message'`) */\n 'send-label'?: string;\n /** Keyboard shortcut that sends: 'enter' (Shift+Enter for a newline) or 'mod+enter' (Enter always inserts a newline; Ctrl/Cmd+Enter sends) */\n 'send-shortcut'?: SendShortcut;\n /** Component size */\n size?: ComponentSize;\n /**\n * Shows an inline green check icon inside the field to confirm the value has\n * passed validation. Ignored while `error` is set: an error always wins.\n */\n success?: boolean;\n /** Current text value */\n value?: string;\n /** Visual variant of the card: same variant set as `ore-textarea`, applied to the card. */\n variant?: FieldVariant;\n};\n\n/**\n * A message/comment composer: a card with an auto-resizing field on top and a toolbar row\n * below it, built directly on the same core primitives as `ore-textarea` (`createTextField`,\n * `createAutoResize`) rather than composing `ore-textarea` itself. Nesting a fully-styled sibling\n * component and suppressing most of its chrome (as an earlier version of this component did) is\n * a leaky composition: this owns its single `<textarea>` outright, so there's exactly one\n * implementation of the field's appearance to reason about, not two fighting each other.\n *\n * Handles the send gesture (Enter to send, Shift+Enter for a newline, or a Ctrl/Cmd+Enter\n * alternative), IME-safe composition, and clear-and-refocus after sending.\n *\n * @element ore-message-composer\n *\n * @attr {string} value - Current text value\n * @attr {string} placeholder - Placeholder text\n * @attr {string} label - Accessible name for the field (not rendered visually)\n * @attr {string} name - Form field name\n * @attr {string} helper - Helper text shown below the field\n * @attr {string} error - Error message shown below the field\n * @attr {boolean} success - Show an inline success check icon (suppressed while `error` is set)\n * @attr {boolean} disabled - Disable the whole composer\n * @attr {boolean} readonly - Read-only mode\n * @attr {boolean} required - Require a non-blank value for native form validation\n * @attr {boolean} loading - Blocks further sends without disabling editing\n * @attr {number} maxlength - Max character count; shows a counter\n * @attr {number} rows - Visible rows before auto-resize grows the field (default 1)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant of the card: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} size - 'sm' | 'md' | 'lg'\n * @attr {boolean} fullwidth - Stretch to the full width of the container\n * @attr {string} send-icon - Registered icon name for the default send button (default 'arrow-up')\n * @attr {string} send-label - Accessible label for the send button (default 'Send message')\n * @attr {string} send-shortcut - 'enter' (default) | 'mod+enter'\n * @attr {boolean} clear-on-send - Clear the value and refocus after a non-cancelled send (default true)\n *\n * @fires input - Fired on every keystroke. detail: { value, originalEvent }\n * @fires send - Fired on a send attempt. Cancelable. detail: { value, originalEvent }\n *\n * @slot prefix - Content at the start of the toolbar row (e.g. an attach menu)\n * @slot suffix - Content at the end of the toolbar row, before the send button (e.g. a model picker)\n * @slot send - Replaces the default send button entirely: the only supported way to customize it\n *\n * @cssprop --message-composer-bg - Card background color\n * @cssprop --message-composer-border-color - Card border color\n * @cssprop --message-composer-radius - Card border radius\n * @cssprop --message-composer-padding - Card inner padding\n * @cssprop --message-composer-gap - Gap between the field/toolbar rows and between toolbar items\n * @cssprop --message-composer-placeholder-color - Field placeholder text color\n * @cssprop --message-composer-min-height - Minimum field height (default one line)\n * @cssprop --message-composer-hover-bg - Card background on hover (flat/ghost variants)\n * @cssprop --message-composer-hover-border-color - Card border on hover (flat/bordered variants)\n * @cssprop --message-composer-focus-bg - Card background when focused (flat variant)\n * @cssprop --message-composer-focus-border-color - Card border when focused (flat variant)\n *\n * @part composer - Root card container\n * @part field - The native `<textarea>` element\n * @part status-icon - The inline error/success icon shown inside the field\n * @part helper - Helper text element\n * @part error - Error text element (`role=\"alert\"`)\n * @part toolbar - Toolbar row below the field\n * @part toolbar-start - Toolbar group holding the `prefix` slot\n * @part toolbar-end - Toolbar group holding the `suffix` slot and the send button\n * @part send-button - The default send button (absent when the `send` slot is used)\n *\n * @example\n * ```html\n * <ore-message-composer placeholder=\"Message…\" send-shortcut=\"mod+enter\"></ore-message-composer>\n * ```\n */\nexport const MESSAGE_COMPOSER_TAG = 'ore-message-composer' as const;\ndefine<OreMessageComposerProps>(MESSAGE_COMPOSER_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...loadableBundle,\n 'clear-on-send': prop.bool(true),\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n maxlength: prop.json(undefined as number | undefined),\n name: prop.string(),\n placeholder: prop.string(),\n readonly: prop.bool(false),\n ref: prop.data<((el: HTMLTextAreaElement | null) => void) | null>(),\n required: prop.bool(false),\n rows: prop.json(undefined as number | undefined),\n 'send-icon': prop.string(),\n 'send-label': prop.string(),\n 'send-shortcut': prop.oneOf(['enter', 'mod+enter'] as const, 'enter'),\n success: prop.bool(false),\n value: prop.string(),\n variant: prop.oneOf(FIELD_VARIANTS),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreMessageComposerEvents>();\n const slots = useSlots<'prefix' | 'send' | 'suffix'>();\n\n const isDisabled = props.disabled;\n\n const abortSignal = lifecycleSignal(onCleanup);\n const textareaRef = ref<HTMLTextAreaElement>();\n const autoResize = createAutoResize();\n\n const tf = createTextField({\n disabled: isDisabled,\n error: props.error,\n helper: props.helper,\n maxLength: props.maxlength,\n onInput: (_event, value) => {\n setFieldValue(el, value);\n dispatchNativeFieldEvent(el, 'input');\n },\n prefix: 'composer',\n readonly: props.readonly,\n required: props.required,\n signal: abortSignal,\n value: props.value,\n });\n\n defineFieldValue(\n el,\n () => tf.value.value,\n (value) => {\n tf.value.value = value;\n },\n );\n\n // Directly form-associated now that the field is owned here rather than borrowed from a\n // nested `ore-textarea` (which previously registered with the ancestor `<form>` on this\n // component's behalf).\n tf.attachFormField(\n useField<string>({\n disabled: tf.disabled,\n onReset: () => {\n tf.reset();\n autoResize.recompute();\n },\n toFormValue: (v) => v,\n validity: tf.validity,\n value: tf.value,\n }),\n );\n\n function attemptSend(event: KeyboardEvent | MouseEvent): void {\n const notPrevented = emit('send', { originalEvent: event, value: tf.value.value.trim() });\n\n // `preventDefault()` on `send` means \"keep the current text: skip the default clear\n // + refocus\" (see the event's own doc comment): both halves of that default are gated on\n // the same condition, not just the clear.\n if (notPrevented && props['clear-on-send'].value !== false) {\n tf.clear();\n autoResize.recompute();\n textareaRef.value?.focus();\n }\n }\n\n const composer = createComposerControl({\n disabled: isDisabled,\n loading: computed(() => Boolean(props.loading.value)),\n onSend: attemptSend,\n sendShortcut: props['send-shortcut'],\n value: tf.value,\n });\n\n onElement(textareaRef, (textareaEl) => {\n const unwireField = tf.wire(textareaEl);\n const unwireAutoResize = autoResize.wire(textareaEl);\n const unwireRef = bindRefCallback(props.ref, textareaEl);\n const handleKeydown = (e: KeyboardEvent) => composer.handleKeydown(e);\n\n textareaEl.addEventListener('keydown', handleKeydown);\n\n return () => {\n textareaEl.removeEventListener('keydown', handleKeydown);\n unwireField();\n unwireAutoResize();\n unwireRef();\n };\n });\n\n bind({\n attr: {\n error: errorAttr(tf.errorText),\n size: props.size,\n // Reflects `success` only once `error` is confirmed empty: keeps the two host\n // attributes mutually exclusive even if a consumer sets both props at once.\n success: () => (props.success.value && !tf.errorText.value ? true : undefined),\n variant: props.variant,\n },\n });\n\n return html`\n <div class=\"composer\" part=\"composer\">\n <textarea\n class=\"field\"\n part=\"field\"\n ref=\"${textareaRef}\"\n rows=\"${() => props.rows.value ?? 1}\"\n name=\"${() => props.name.value ?? ''}\"\n placeholder=\"${() => props.placeholder.value ?? 'Message…'}\"\n maxlength=\"${() => props.maxlength.value}\"\n ?disabled=\"${isDisabled}\"\n ?readonly=\"${() => Boolean(props.readonly.value)}\"\n ?required=\"${() => Boolean(props.required.value)}\"\n value=\"${model(tf.value)}\"\n aria-label=\"${() => props.label.value || 'Message'}\"\n aria-keyshortcuts=\"${composer.keyShortcutsHint}\"\n aria-describedby=\"${tf.ariaDescribedBy}\"\n aria-errormessage=\"${tf.ariaErrorMessage}\"\n aria-invalid=\"${tf.ariaInvalid}\"></textarea>\n ${renderStatusIcon(tf.errorText)} ${renderFieldStatusRegion(tf)}\n <div class=\"toolbar\" part=\"toolbar\">\n <div class=\"toolbar-start\" part=\"toolbar-start\">\n <slot name=\"prefix\"></slot>\n </div>\n <div class=\"toolbar-end\" part=\"toolbar-end\">\n <slot name=\"suffix\"></slot>\n <slot name=\"send\"></slot>\n ${() =>\n slots.has('send').value\n ? ''\n : html`\n <ore-button\n class=\"send-btn\"\n part=\"send-button\"\n type=\"button\"\n icon-only\n variant=\"solid\"\n color=\"${() => props.color.value || 'primary'}\"\n size=\"${props.size}\"\n label=\"${() => props['send-label'].value || DEFAULT_SEND_LABEL}\"\n ?loading=\"${() => Boolean(props.loading.value)}\"\n ?disabled=\"${() => !composer.canSend.value}\"\n @click=\"${(e: MouseEvent) => composer.send(e)}\">\n <ore-icon name=\"${() => props['send-icon'].value || SEND_ICON}\" size=\"16\"></ore-icon>\n </ore-button>\n `}\n </div>\n </div>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [\n colorThemeMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n disabledLoadingMixin,\n forcedColorsMixin,\n forcedColorsFocusMixin('.field'),\n sizeVariantMixin(MESSAGE_COMPOSER_SIZE_PRESET),\n componentStyles,\n // Must come after `componentStyles`: see `ore-input`'s identical ordering note for why\n // (`@layer` precedence is fixed by which layer name is *first* referenced across this whole\n // array; `componentStyles` establishes `refine.base`, which this mixin's `refine.variants`\n // rules need to win over).\n fieldVariantMixin({ container: '.composer', text: '.field', tokenPrefix: 'message-composer' }),\n ],\n});\n"],"mappings":"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"}
|
package/dist/navbar.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./styles/mixins/shape.css.cjs"),n=require("./styles/mixins/theme.css.cjs"),r=require("./styles/mixins/variants.css.cjs"),i=require("./shared/types.cjs"),a=require("./icon.cjs"),o=require("./utils/link.cjs"),s=require("./layout/shared.cjs"),c=require("./layout/navbar/navbar.css.cjs");let l=require("@vielzeug/ripple"),u=require("@vielzeug/sentinel"),d=require("@vielzeug/ore");var f=[`flat`,`solid`,`bordered`,`outline`,`frost`],listen=(e,t,n,r)=>e?(e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)):()=>{},hasElementContent=e=>!(e instanceof HTMLElement)||e.tagName.includes(`-`)||e.childElementCount>0?!0:(e.textContent??``).trim().length>0,findScrollContainer=e=>{let t=e.parentElement;for(;t;){let e=window.getComputedStyle(t).overflowY;if(e===`auto`||e===`scroll`||e===`overlay`)return t;t=t.parentElement}return null},p=(0,d.createContext)(`NavbarContext`),m=`ore-navbar`;(0,d.define)(m,{props:{breakpoint:d.prop.string(`(max-width: 768px)`),color:d.prop.oneOf(i.THEME_COLORS),"container-breakpoints":d.prop.bool(!1),elevation:d.prop.oneOf(i.ELEVATION_LEVELS),floating:d.prop.bool(!1),label:d.prop.string(`Main navigation`),"menu-close-icon":d.prop.string(`x`),"menu-close-label":d.prop.string(`Close navigation menu`),"menu-icon":d.prop.string(`menu`),"menu-open-label":d.prop.string(`Open navigation menu`),"mobile-sidebar":d.prop.string(),rounded:d.prop.oneOf(i.ROUNDED_SIZES),"scroll-threshold":d.prop.number(80),sticky:d.prop.bool(!1),variant:d.prop.oneOf(f)},setup(e){let t=(0,d.getHost)(),n=(0,d.useEmit)(),r=(0,d.useSlots)(),i=r.has(`logo`),a=r.has(`start`),o=r.has(),c=r.has(`end`),f=r.has(`persistent`),m=r.elements(`mobile-menu`),h=(0,l.signal)(!1),g=(0,l.signal)(null),_=(0,l.signal)(!1),v=(0,l.signal)(!1),y=(0,l.signal)(!1),b=(0,l.signal)(!1),x=(0,l.signal)(!1),S=(0,l.signal)(!1),C=(0,l.signal)(0),w=(0,l.signal)(!1),T=(0,l.signal)(!1),E=(0,l.signal)(s.parseMaxWidthPx(e.breakpoint.value)),D=(0,l.signal)(!1),getExternalSidebar=()=>{let n=String(e[`mobile-sidebar`].value??``).trim();if(!n)return null;let r=t.getRootNode();return(r instanceof ShadowRoot||r instanceof Document?r.querySelector(n):null)??document.querySelector(n)};(0,d.provide)(p,{closeMobileMenu:()=>closeMobileMenu(),isMobile:(0,l.computed)(()=>y.value),mobileMenuOpen:(0,l.computed)(()=>b.value)});let computeMode=()=>{if(e.floating.value&&e.sticky.value)return S.value?`sticky`:`floating`;if(e.floating.value)return`floating`;if(e.sticky.value)return`sticky`},setMobileMenu=e=>{let t=!!e;if(!h.value){let e=g.value??getExternalSidebar();if(e){t?e.openDrawer?.():e.closeDrawer?.();return}}(!t||h.value)&&b.value!==t&&(b.value=t,n(`mobile-menu-change`,{open:t}))},closeMobileMenu=()=>setMobileMenu(!1),openMobileMenu=()=>setMobileMenu(!0),toggleMobileMenu=()=>{if(!h.value){let e=g.value??getExternalSidebar();if(e){typeof e.toggleDrawer==`function`?e.toggleDrawer():typeof e.openDrawer==`function`&&typeof e.closeDrawer==`function`&&(e.hasAttribute(`data-drawer-open`)?e.closeDrawer():e.openDrawer());return}}setMobileMenu(!b.value)},syncMobileMode=()=>{let t=(g.value&&!h.value?_.value:!1)||w.value||!!e[`container-breakpoints`].value&&T.value;y.value=t,t||closeMobileMenu()},readScrollOffset=e=>e?e.scrollTop||0:window.scrollY||document.documentElement.scrollTop||0,resetScrollBase=e=>{C.value=readScrollOffset(e)},updateScrolled=(t=null)=>{if(!(e.floating.value&&e.sticky.value)){S.value&&=!1;return}let n=Math.max(0,Number(e[`scroll-threshold`].value)||0),r=readScrollOffset(t)-C.value;S.value=r>n};return(0,d.bind)({attr:{"data-mobile":()=>y.value?!0:void 0,"data-mobile-open":()=>y.value&&b.value?!0:void 0,"data-mode":()=>computeMode()??null,"data-mode-transitioning":()=>x.value?!0:void 0,"data-preview-mode":()=>D.value?!0:void 0,"data-scrolled":()=>e.floating.value&&e.sticky.value&&S.value?!0:void 0}}),(0,d.onMounted)(()=>{let n=t,r=findScrollContainer(t);n.closeMobileMenu=closeMobileMenu,n.openMobileMenu=openMobileMenu,n.toggleMobileMenu=toggleMobileMenu;let i=listen(window,`scroll`,()=>{updateScrolled(r)},{passive:!0}),a=r?listen(r,`scroll`,()=>{updateScrolled(r)},{passive:!0}):void 0,o=listen(t,`keydown`,e=>{e.key===`Escape`&&b.value&&closeMobileMenu()}),mediaCleanup,mobileSidebarCleanup,c,f,resolveMobileSidebarTarget=()=>{if(mobileSidebarCleanup?.(),mobileSidebarCleanup=void 0,g.value=null,_.value=!1,v.value=!1,!String(e[`mobile-sidebar`].value??``).trim())return;let n=getExternalSidebar();if(!n){let e=t.getRootNode(),n=e instanceof Document||e instanceof ShadowRoot?e:document,r=new MutationObserver(()=>{getExternalSidebar()&&resolveMobileSidebarTarget()});r.observe(n,{childList:!0,subtree:!0}),mobileSidebarCleanup=()=>r.disconnect();return}let syncTargetState=e=>{let t=n.hasAttribute(`data-bottom-nav`),r=n.hasAttribute(`data-drawer-open`);_.value=t,v.value=e?.detail?e.detail.open:r,v.value&&!_.value&&(_.value=!0),syncMobileMode()},r=new MutationObserver(()=>{syncTargetState()});syncTargetState(),r.observe(n,{attributeFilter:[`data-bottom-nav`,`data-drawer-open`],attributes:!0}),n.addEventListener(`drawer-change`,syncTargetState),mobileSidebarCleanup=()=>{r.disconnect(),n.removeEventListener(`drawer-change`,syncTargetState)},g.value=n},triggerModeTransition=()=>{x.value=!0,c!=null&&clearTimeout(c),c=setTimeout(()=>{x.value=!1,c=void 0},220)},p=(0,l.watch)((0,l.computed)(()=>e.floating.value&&e.sticky.value),e=>{if(!e){S.value&&=!1,x.value&&=!1,f=void 0,c!=null&&(clearTimeout(c),c=void 0);return}resetScrollBase(r),updateScrolled(r),f=computeMode()},{immediate:!0}),y=(0,l.watch)(e[`mobile-sidebar`],()=>{resolveMobileSidebarTarget()},{immediate:!0}),C=(0,l.watch)((0,l.computed)(()=>computeMode()),t=>{if(!(e.floating.value&&e.sticky.value)){f=t??void 0;return}let n=t??void 0;f&&n&&f!==n&&triggerModeTransition(),f=n},{immediate:!0}),O=(0,l.watch)(h,e=>{!e&&b.value&&closeMobileMenu()},{immediate:!0}),syncMobileMenuContent=()=>{h.value=m.value.some(hasElementContent)},k=(0,l.watch)(m,syncMobileMenuContent,{immediate:!0}),A=new MutationObserver(()=>{syncMobileMenuContent()});A.observe(t,{childList:!0,subtree:!0}),(0,l.watch)(e.breakpoint,n=>{(0,l.untrack)(()=>{mediaCleanup?.(),mediaCleanup=void 0;let r=String(n??``).trim();E.value=s.parseMaxWidthPx(r),w.value=!1;let i=s.readContainerWidth(t);if(T.value=i>0&&E.value!=null&&i<=E.value,syncMobileMode(),!(e[`container-breakpoints`].value&&E.value!=null)&&r)try{let e=(0,u.createMediaQuery)(r),syncMedia=e=>{e&&(w.value=e.matches,syncMobileMode())};syncMedia(e.getSnapshot());let t=(0,l.watch)((0,l.fromSubscribable)(e,{signal:e.disposalSignal}),syncMedia);mediaCleanup=()=>{t.dispose(),e.dispose()}}catch(e){if(!(e instanceof u.SentinelUnavailableError))throw e}})},{immediate:!0});let j=typeof ResizeObserver==`function`?(()=>{let e=(0,u.createElementSize)(t);(0,d.onCleanup)(()=>e.dispose());let n=(0,l.fromSubscribable)(e,{signal:e.disposalSignal}),r=t.parentElement,i=s.resolveContainerElement(t),a=r?(0,u.createElementSize)(r):void 0,o=i&&i!==r?(0,u.createElementSize)(i):void 0;a&&(0,d.onCleanup)(()=>a.dispose()),o&&(0,d.onCleanup)(()=>o.dispose());let c=a?(0,l.fromSubscribable)(a,{signal:a.disposalSignal}):void 0,f=o?(0,l.fromSubscribable)(o,{signal:o.disposalSignal}):void 0;return(0,l.watch)((0,l.computed)(()=>[n.value?.width,n.value?.height,c?.value?.width,c?.value?.height,f?.value?.width,f?.value?.height]),()=>{let e=s.readContainerWidth(t),n=s.resolveContainerElement(t)?.clientWidth??0;T.value=e>0&&E.value!=null&&e<=E.value,D.value=n>0&&n<window.innerWidth,syncMobileMode()})})():void 0;return()=>{o?.(),i?.(),a?.(),p?.dispose(),C?.dispose(),O?.dispose(),k?.dispose(),y?.dispose(),A.disconnect(),mediaCleanup?.(),mobileSidebarCleanup?.(),j?.dispose(),c!=null&&(clearTimeout(c),c=void 0)}}),d.html`
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./styles/mixins/shape.css.cjs"),n=require("./styles/mixins/theme.css.cjs"),r=require("./styles/mixins/variants.css.cjs"),i=require("./shared/types.cjs"),a=require("./icon.cjs"),o=require("./utils/link.cjs"),s=require("./layout/shared.cjs"),c=require("./layout/navbar/navbar.css.cjs");let l=require("@vielzeug/ripple"),u=require("@vielzeug/sentinel"),d=require("@vielzeug/ore");var f=[`flat`,`solid`,`bordered`,`outline`,`frost`],listen=(e,t,n,r)=>e?(e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)):()=>{},hasElementContent=e=>!(e instanceof HTMLElement)||e.tagName.includes(`-`)||e.childElementCount>0?!0:(e.textContent??``).trim().length>0,findScrollContainer=e=>{let t=e.parentElement;for(;t;){let e=window.getComputedStyle(t).overflowY;if(e===`auto`||e===`scroll`||e===`overlay`)return t;t=t.parentElement}return null},p=(0,d.createContext)(`NavbarContext`),m=`ore-navbar`;(0,d.define)(m,{props:{breakpoint:d.prop.string(`(max-width: 768px)`),color:d.prop.oneOf(i.THEME_COLORS),"container-breakpoints":d.prop.bool(!1),elevation:d.prop.oneOf(i.ELEVATION_LEVELS),floating:d.prop.bool(!1),label:d.prop.string(`Main navigation`),"menu-close-icon":d.prop.string(`x`),"menu-close-label":d.prop.string(`Close navigation menu`),"menu-icon":d.prop.string(`menu`),"menu-open-label":d.prop.string(`Open navigation menu`),"mobile-sidebar":d.prop.string(),rounded:d.prop.oneOf(i.ROUNDED_SIZES),"scroll-threshold":d.prop.number(80),sticky:d.prop.bool(!1),variant:d.prop.oneOf(f)},setup(e){let t=(0,d.getHost)(),n=(0,d.useEmit)(),r=(0,d.useSlots)(),i=r.has(`logo`),a=r.has(`start`),o=r.has(),c=r.has(`end`),f=r.has(`persistent`),m=r.elements(`mobile-menu`),h=(0,l.signal)(!1),g=(0,l.signal)(null),_=(0,l.signal)(!1),v=(0,l.signal)(!1),y=(0,l.signal)(!1),b=(0,l.signal)(!1),x=(0,l.signal)(!1),S=(0,l.signal)(!1),C=(0,l.signal)(0),w=(0,l.signal)(!1),T=(0,l.signal)(!1),E=(0,l.signal)(s.parseMaxWidthPx(e.breakpoint.value)),D=(0,l.signal)(!1),getExternalSidebar=()=>{let n=String(e[`mobile-sidebar`].value??``).trim();if(!n)return null;let r=t.getRootNode();return(r instanceof ShadowRoot||r instanceof Document?r.querySelector(n):null)??document.querySelector(n)};(0,d.provide)(p,{closeMobileMenu:()=>closeMobileMenu(),isMobile:(0,l.computed)(()=>y.value),mobileMenuOpen:(0,l.computed)(()=>b.value)});let computeMode=()=>{if(e.floating.value&&e.sticky.value)return S.value?`sticky`:`floating`;if(e.floating.value)return`floating`;if(e.sticky.value)return`sticky`},setMobileMenu=e=>{let t=!!e;if(!h.value){let e=g.value??getExternalSidebar();if(e){t?e.openDrawer?.():e.closeDrawer?.();return}}(!t||h.value)&&b.value!==t&&(b.value=t,n(`mobile-menu-change`,{open:t}))},closeMobileMenu=()=>setMobileMenu(!1),openMobileMenu=()=>setMobileMenu(!0),toggleMobileMenu=()=>{if(!h.value){let e=g.value??getExternalSidebar();if(e){typeof e.toggleDrawer==`function`?e.toggleDrawer():typeof e.openDrawer==`function`&&typeof e.closeDrawer==`function`&&(e.hasAttribute(`data-drawer-open`)?e.closeDrawer():e.openDrawer());return}}setMobileMenu(!b.value)},syncMobileMode=()=>{let t=(g.value&&!h.value?_.value:!1)||w.value||!!e[`container-breakpoints`].value&&T.value;y.value=t,t||closeMobileMenu()},readScrollOffset=e=>e?e.scrollTop||0:window.scrollY||document.documentElement.scrollTop||0,resetScrollBase=e=>{C.value=readScrollOffset(e)},updateScrolled=(t=null)=>{if(!(e.floating.value&&e.sticky.value)){S.value&&=!1;return}let n=Math.max(0,Number(e[`scroll-threshold`].value)||0),r=readScrollOffset(t)-C.value;S.value=r>n};return(0,d.bind)({attr:{"data-mobile":()=>y.value?!0:void 0,"data-mobile-open":()=>y.value&&b.value?!0:void 0,"data-mode":()=>computeMode()??null,"data-mode-transitioning":()=>x.value?!0:void 0,"data-preview-mode":()=>D.value?!0:void 0,"data-scrolled":()=>e.floating.value&&e.sticky.value&&S.value?!0:void 0}}),(0,d.onMounted)(()=>{let n=t,r=findScrollContainer(t);n.closeMobileMenu=closeMobileMenu,n.openMobileMenu=openMobileMenu,n.toggleMobileMenu=toggleMobileMenu;let i=listen(window,`scroll`,()=>{updateScrolled(r)},{passive:!0}),a=r?listen(r,`scroll`,()=>{updateScrolled(r)},{passive:!0}):void 0,o=listen(t,`keydown`,e=>{e.key===`Escape`&&b.value&&(e.preventDefault(),e.stopPropagation(),closeMobileMenu(),t.shadowRoot?.querySelector(`[part="mobile-toggle"]`)?.focus())}),mediaCleanup,mobileSidebarCleanup,c,f,resolveMobileSidebarTarget=()=>{if(mobileSidebarCleanup?.(),mobileSidebarCleanup=void 0,g.value=null,_.value=!1,v.value=!1,!String(e[`mobile-sidebar`].value??``).trim())return;let n=getExternalSidebar();if(!n){let e=t.getRootNode(),n=e instanceof Document||e instanceof ShadowRoot?e:document,r=new MutationObserver(()=>{getExternalSidebar()&&resolveMobileSidebarTarget()});r.observe(n,{childList:!0,subtree:!0}),mobileSidebarCleanup=()=>r.disconnect();return}let syncTargetState=e=>{let t=n.hasAttribute(`data-bottom-nav`),r=n.hasAttribute(`data-drawer-open`);_.value=t,v.value=e?.detail?e.detail.open:r,v.value&&!_.value&&(_.value=!0),syncMobileMode()},r=new MutationObserver(()=>{syncTargetState()});syncTargetState(),r.observe(n,{attributeFilter:[`data-bottom-nav`,`data-drawer-open`],attributes:!0}),n.addEventListener(`drawer-change`,syncTargetState),mobileSidebarCleanup=()=>{r.disconnect(),n.removeEventListener(`drawer-change`,syncTargetState)},g.value=n},triggerModeTransition=()=>{x.value=!0,c!=null&&clearTimeout(c),c=setTimeout(()=>{x.value=!1,c=void 0},220)},p=(0,l.watch)((0,l.computed)(()=>e.floating.value&&e.sticky.value),e=>{if(!e){S.value&&=!1,x.value&&=!1,f=void 0,c!=null&&(clearTimeout(c),c=void 0);return}resetScrollBase(r),updateScrolled(r),f=computeMode()},{immediate:!0}),y=(0,l.watch)(e[`mobile-sidebar`],()=>{resolveMobileSidebarTarget()},{immediate:!0}),C=(0,l.watch)((0,l.computed)(()=>computeMode()),t=>{if(!(e.floating.value&&e.sticky.value)){f=t??void 0;return}let n=t??void 0;f&&n&&f!==n&&triggerModeTransition(),f=n},{immediate:!0}),O=(0,l.watch)(h,e=>{!e&&b.value&&closeMobileMenu()},{immediate:!0}),syncMobileMenuContent=()=>{h.value=m.value.some(hasElementContent)},k=(0,l.watch)(m,syncMobileMenuContent,{immediate:!0}),A=new MutationObserver(()=>{syncMobileMenuContent()});A.observe(t,{childList:!0,subtree:!0}),(0,l.watch)(e.breakpoint,n=>{(0,l.untrack)(()=>{mediaCleanup?.(),mediaCleanup=void 0;let r=String(n??``).trim();E.value=s.parseMaxWidthPx(r),w.value=!1;let i=s.readContainerWidth(t);if(T.value=i>0&&E.value!=null&&i<=E.value,syncMobileMode(),!(e[`container-breakpoints`].value&&E.value!=null)&&r)try{let e=(0,u.createMediaQuery)(r),syncMedia=e=>{e&&(w.value=e.matches,syncMobileMode())};syncMedia(e.getSnapshot());let t=(0,l.watch)((0,l.fromSubscribable)(e,{signal:e.disposalSignal}),syncMedia);mediaCleanup=()=>{t.dispose(),e.dispose()}}catch(e){if(!(e instanceof u.SentinelUnavailableError))throw e}})},{immediate:!0});let j=typeof ResizeObserver==`function`?(()=>{let e=(0,u.createElementSize)(t);(0,d.onCleanup)(()=>e.dispose());let n=(0,l.fromSubscribable)(e,{signal:e.disposalSignal}),r=t.parentElement,i=s.resolveContainerElement(t),a=r?(0,u.createElementSize)(r):void 0,o=i&&i!==r?(0,u.createElementSize)(i):void 0;a&&(0,d.onCleanup)(()=>a.dispose()),o&&(0,d.onCleanup)(()=>o.dispose());let c=a?(0,l.fromSubscribable)(a,{signal:a.disposalSignal}):void 0,f=o?(0,l.fromSubscribable)(o,{signal:o.disposalSignal}):void 0;return(0,l.watch)((0,l.computed)(()=>[n.value?.width,n.value?.height,c?.value?.width,c?.value?.height,f?.value?.width,f?.value?.height]),()=>{let e=s.readContainerWidth(t),n=s.resolveContainerElement(t)?.clientWidth??0;T.value=e>0&&E.value!=null&&e<=E.value,D.value=n>0&&n<window.innerWidth,syncMobileMode()})})():void 0;return()=>{o?.(),i?.(),a?.(),p?.dispose(),C?.dispose(),O?.dispose(),k?.dispose(),y?.dispose(),A.disconnect(),mediaCleanup?.(),mobileSidebarCleanup?.(),j?.dispose(),c!=null&&(clearTimeout(c),c=void 0)}}),d.html`
|
|
2
2
|
<nav part="nav" aria-label="${e.label}">
|
|
3
3
|
<div class="navbar" part="bar">
|
|
4
4
|
<div class="navbar-logo" part="logo" ?hidden=${()=>!i.value}>
|
package/dist/navbar.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navbar.cjs","names":[],"sources":["../src/layout/navbar/navbar.ts"],"sourcesContent":["import {\n bind,\n createContext,\n define,\n getHost,\n html,\n inject,\n onCleanup,\n onMounted,\n prop,\n provide,\n useEmit,\n useSlots,\n} from '@vielzeug/ore';\nimport { computed, fromSubscribable, type Readable, signal, untrack, watch } from '@vielzeug/ripple';\nimport { createElementSize, createMediaQuery, SentinelUnavailableError } from '@vielzeug/sentinel';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n elevationMixin,\n frostVariantMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n} from '../../styles';\nimport {\n ELEVATION_LEVELS,\n type ElevationLevel,\n ROUNDED_SIZES,\n type RoundedSize,\n THEME_COLORS,\n type ThemeColor,\n} from '../../types';\nimport { computeSafeRel } from '../../utils';\nimport { parseMaxWidthPx, readContainerWidth, resolveContainerElement } from '../shared';\nimport navbarStyles from './navbar.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\n\nconst NAVBAR_VARIANTS = ['flat', 'solid', 'bordered', 'outline', 'frost'] as const;\ntype NavbarVariant = (typeof NAVBAR_VARIANTS)[number];\n\nconst listen = <E extends Event = Event>(\n el: EventTarget | null | undefined,\n name: string,\n handler: (e: E) => void,\n options?: AddEventListenerOptions,\n): (() => void) => {\n if (!el) return () => {};\n\n el.addEventListener(name, handler as EventListener, options);\n\n return () => el.removeEventListener(name, handler as EventListener, options);\n};\n\ntype NavbarMode = 'floating' | 'sticky';\n\nconst hasElementContent = (el: Element): boolean => {\n if (!(el instanceof HTMLElement)) return true;\n\n if (el.tagName.includes('-')) return true;\n\n if (el.childElementCount > 0) return true;\n\n return (el.textContent ?? '').trim().length > 0;\n};\n\nconst findScrollContainer = (start: HTMLElement): HTMLElement | null => {\n let current: HTMLElement | null = start.parentElement;\n\n while (current) {\n const styles = window.getComputedStyle(current);\n const overflowY = styles.overflowY;\n\n if (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay') {\n return current;\n }\n\n current = current.parentElement;\n }\n\n return null;\n};\n\n/** Context provided by `ore-navbar` to `ore-navbar-item` children. */\nexport type NavbarContext = {\n closeMobileMenu: () => void;\n isMobile: Readable<boolean>;\n mobileMenuOpen: Readable<boolean>;\n};\n\n/** Injection key for navbar context. */\nexport const NAVBAR_CTX = createContext<NavbarContext>('NavbarContext');\n\n/** Navbar component events */\nexport type OreNavbarEvents = {\n 'mobile-menu-change': { open: boolean };\n};\n\n/** Navbar component element interface */\nexport type NavbarElement = HTMLElement &\n OreNavbarProps & {\n closeMobileMenu(): void;\n openMobileMenu(): void;\n toggleMobileMenu(): void;\n };\n\ntype MobileSidebarElement = HTMLElement & {\n closeDrawer?: () => void;\n openDrawer?: () => void;\n toggleDrawer?: () => void;\n};\n\n/** Navbar component properties */\nexport type OreNavbarProps = {\n /** CSS media query used for mobile layout switching */\n breakpoint?: string;\n /** Theme color */\n color?: ThemeColor;\n /** Evaluate mobile breakpoint against container width only when possible. */\n 'container-breakpoints'?: boolean;\n /** Shadow elevation level (0-5) */\n elevation?: ElevationLevel;\n /** Start as detached floating navbar */\n floating?: boolean;\n /** Accessible nav landmark label */\n label?: string;\n /** Icon shown while the mobile menu is open */\n 'menu-close-icon'?: string;\n 'menu-close-label'?: string;\n /** Icon shown while the mobile menu is closed */\n 'menu-icon'?: string;\n 'menu-open-label'?: string;\n /** CSS selector for an external sidebar toggled by the mobile button */\n 'mobile-sidebar'?: string;\n /** Border radius size */\n rounded?: RoundedSize;\n /** Scroll threshold in px used by floating+sticky transition */\n 'scroll-threshold'?: number;\n /** Stick to top of viewport */\n sticky?: boolean;\n /** Visual style variant */\n variant?: NavbarVariant;\n};\n\n/** Navbar item properties */\nexport type OreNavbarItemProps = {\n /** Accessible name for icon-only items, forwarded to the internal button or anchor */\n ariaLabel?: string;\n /** Whether this item represents the current page */\n active?: boolean;\n /** Whether this item is disabled */\n disabled?: boolean;\n /** Link target URL. Renders an anchor when provided. */\n href?: string;\n /**\n * Icon-only mode: hides the (empty) label region and switches to compact, symmetric\n * padding instead of the icon+label layout's wider horizontal padding. Set this whenever\n * the default slot is intentionally left empty (name conveyed via `aria-label` instead),\n * otherwise the empty label still reserves its inter-element gap and the item ends up\n * with lopsided padding around the icon.\n */\n 'icon-only'?: boolean;\n /** Relationship of linked URL (anchor-only) */\n rel?: string;\n /** Browsing context for link (anchor-only) */\n target?: string;\n};\n\n/**\n * `ore-navbar`: Responsive navigation with sticky/floating modes and a mobile overflow panel.\n *\n * @element ore-navbar\n * @element ore-navbar-item - Navigation link or button placed in the default or mobile-menu slot\n *\n * @attr {string} label - Accessible nav landmark label\n * @attr {string} menu-icon - Icon shown while the mobile menu is closed\n * @attr {string} menu-close-icon - Icon shown while the mobile menu is open\n * @attr {boolean} sticky - Makes navbar sticky at top\n * @attr {boolean} floating - Makes navbar detached and floating\n * @attr {number} scroll-threshold - Scroll threshold for floating+sticky transition\n * @attr {string} breakpoint - CSS media query used for mobile mode\n * @attr {boolean} container-breakpoints - Evaluates parseable max-width breakpoints against container width\n * @attr {string} variant - Visual variant: 'flat' | 'solid' | 'bordered' | 'outline' | 'frost'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} elevation - Shadow elevation: '0' | '1' | '2' | '3' | '4' | '5'\n *\n * @fires mobile-menu-change - Emitted when mobile menu open state changes. detail: { open: boolean }\n *\n * @slot logo - Brand/logo content rendered at the leading edge\n * @slot start - Content rendered in the left/start region\n * @slot - Center navigation content rendered between the start and end regions\n * @slot end - Content rendered in the right/end region\n * @slot persistent - Trailing content that stays in the bar through the mobile collapse (global actions and status icons)\n * @slot mobile-menu - Controls and links rendered inside the mobile overflow panel\n *\n * @cssprop --navbar-height - Navbar bar height\n * @cssprop --navbar-width - Max width of the navbar content\n * @cssprop --navbar-offset - Horizontal offset for floating/sticky positioning\n * @cssprop --navbar-bg - Navbar background color\n * @cssprop --navbar-color - Navbar text color\n * @cssprop --navbar-border-color - Navbar border color\n * @cssprop --navbar-shadow - Navbar box shadow\n * @cssprop --navbar-radius - Border radius (floating/sticky modes)\n * @cssprop --navbar-backdrop-filter - Backdrop filter for blur effect\n * @cssprop --navbar-item-color - Nav item text color\n * @cssprop --navbar-item-hover-color - Nav item hover text color\n * @cssprop --navbar-item-hover-bg - Nav item hover background\n * @cssprop --navbar-item-active-color - Nav item active/current text color\n * @cssprop --navbar-item-active-bg - Nav item active/current background\n * @part nav - Outer navigation landmark\n * @part bar - Main navbar row container\n * @part logo - Logo slot container\n * @part start - Leading action/content region\n * @part center - Center content region\n * @part end - Trailing action/content region\n * @part persistent - Trailing region that survives the mobile collapse\n * @part mobile-toggle - Mobile menu toggle button\n * @part mobile-menu - Mobile overflow panel\n * @part item-icon - Leading icon inside navbar items\n * @part item-label - Label text inside navbar items\n * @part item-end - Trailing content inside navbar items\n * @part item - Clickable navbar item root\n * @example\n * ```html\n * <ore-navbar sticky elevation=\"1\">\n * <a href=\"/\" slot=\"logo\"><strong>MyApp</strong></a>\n * <ore-navbar-item href=\"/dashboard\">Dashboard</ore-navbar-item>\n * <ore-navbar-item href=\"/docs\">Docs</ore-navbar-item>\n * <ore-button slot=\"end\" variant=\"solid\" color=\"primary\" size=\"sm\">Sign in</ore-button>\n * </ore-navbar>\n * ```\n */\nexport const NAVBAR_TAG = 'ore-navbar' as const;\ndefine<OreNavbarProps>(NAVBAR_TAG, {\n props: {\n breakpoint: prop.string('(max-width: 768px)'),\n color: prop.oneOf(THEME_COLORS),\n 'container-breakpoints': prop.bool(false),\n elevation: prop.oneOf(ELEVATION_LEVELS),\n floating: prop.bool(false),\n label: prop.string('Main navigation'),\n 'menu-close-icon': prop.string('x'),\n 'menu-close-label': prop.string('Close navigation menu'),\n 'menu-icon': prop.string('menu'),\n 'menu-open-label': prop.string('Open navigation menu'),\n 'mobile-sidebar': prop.string(),\n rounded: prop.oneOf(ROUNDED_SIZES),\n 'scroll-threshold': prop.number(80),\n sticky: prop.bool(false),\n variant: prop.oneOf(NAVBAR_VARIANTS),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreNavbarEvents>();\n const slots = useSlots();\n\n const hasLogo = slots.has('logo');\n const hasStart = slots.has('start');\n const hasCenter = slots.has();\n const hasEnd = slots.has('end');\n const hasPersistent = slots.has('persistent');\n const mobileMenuElements = slots.elements('mobile-menu');\n const hasMobileMenu = signal(false);\n const mobileSidebarTarget = signal<MobileSidebarElement | null>(null);\n const isExternalMobileMode = signal(false);\n const isExternalMobileOpen = signal(false);\n const isMobile = signal(false);\n const isMobileMenuOpen = signal(false);\n const isModeTransitioning = signal(false);\n const isScrolled = signal(false);\n const scrollBase = signal(0);\n const mediaMatches = signal(false);\n const sizeMatches = signal(false);\n const maxWidthPx = signal<number | undefined>(parseMaxWidthPx(props.breakpoint.value));\n const isPreviewMode = signal(false);\n\n const getExternalSidebar = () => {\n const selector = String(props['mobile-sidebar'].value ?? '').trim();\n\n if (!selector) return null;\n\n const root = el.getRootNode();\n const scopedTarget =\n root instanceof ShadowRoot || root instanceof Document\n ? (root.querySelector(selector) as MobileSidebarElement | null)\n : null;\n\n return scopedTarget ?? (document.querySelector(selector) as MobileSidebarElement | null);\n };\n\n provide(NAVBAR_CTX, {\n // Wrapped, not passed directly: `closeMobileMenu` (below) isn't defined yet at this point\n // in `setup()`; by the time `ore-navbar-item` actually calls this (a later click), it is.\n closeMobileMenu: () => closeMobileMenu(),\n isMobile: computed(() => isMobile.value) as Readable<boolean>,\n mobileMenuOpen: computed(() => isMobileMenuOpen.value) as Readable<boolean>,\n });\n\n const computeMode = (): NavbarMode | undefined => {\n if (props.floating.value && props.sticky.value) {\n return isScrolled.value ? 'sticky' : 'floating';\n }\n\n if (props.floating.value) return 'floating';\n\n if (props.sticky.value) return 'sticky';\n\n return undefined;\n };\n\n const setMobileMenu = (next: boolean) => {\n const open = Boolean(next);\n\n if (!hasMobileMenu.value) {\n const target = mobileSidebarTarget.value ?? getExternalSidebar();\n\n if (target) {\n if (open) {\n target.openDrawer?.();\n } else {\n target.closeDrawer?.();\n }\n\n return;\n }\n }\n\n if (open && !hasMobileMenu.value) return;\n\n if (isMobileMenuOpen.value === open) return;\n\n isMobileMenuOpen.value = open;\n emit('mobile-menu-change', { open });\n };\n\n const closeMobileMenu = () => setMobileMenu(false);\n const openMobileMenu = () => setMobileMenu(true);\n const toggleMobileMenu = () => {\n if (!hasMobileMenu.value) {\n const target = mobileSidebarTarget.value ?? getExternalSidebar();\n\n if (target) {\n if (typeof target.toggleDrawer === 'function') {\n target.toggleDrawer();\n } else if (typeof target.openDrawer === 'function' && typeof target.closeDrawer === 'function') {\n const isOpen = target.hasAttribute('data-drawer-open');\n\n if (isOpen) target.closeDrawer();\n else target.openDrawer();\n }\n\n return;\n }\n }\n\n setMobileMenu(!isMobileMenuOpen.value);\n };\n\n const syncMobileMode = () => {\n const next =\n (mobileSidebarTarget.value && !hasMobileMenu.value ? isExternalMobileMode.value : false) ||\n mediaMatches.value ||\n (Boolean(props['container-breakpoints'].value) && sizeMatches.value);\n\n isMobile.value = next;\n\n if (!next) closeMobileMenu();\n };\n\n const readScrollOffset = (scrollContainer: HTMLElement | null): number => {\n if (scrollContainer) {\n return scrollContainer.scrollTop || 0;\n }\n\n return window.scrollY || document.documentElement.scrollTop || 0;\n };\n\n const resetScrollBase = (scrollContainer: HTMLElement | null) => {\n scrollBase.value = readScrollOffset(scrollContainer);\n };\n\n const updateScrolled = (scrollContainer: HTMLElement | null = null) => {\n if (!(props.floating.value && props.sticky.value)) {\n if (isScrolled.value) isScrolled.value = false;\n\n return;\n }\n\n const threshold = Math.max(0, Number(props['scroll-threshold'].value) || 0);\n const currentOffset = readScrollOffset(scrollContainer);\n const delta = currentOffset - scrollBase.value;\n\n isScrolled.value = delta > threshold;\n };\n\n bind({\n attr: {\n 'data-mobile': () => (isMobile.value ? true : undefined),\n 'data-mobile-open': () => (isMobile.value && isMobileMenuOpen.value ? true : undefined),\n 'data-mode': () => computeMode() ?? null,\n 'data-mode-transitioning': () => (isModeTransitioning.value ? true : undefined),\n 'data-preview-mode': () => (isPreviewMode.value ? true : undefined),\n 'data-scrolled': () => (props.floating.value && props.sticky.value && isScrolled.value ? true : undefined),\n },\n });\n\n onMounted(() => {\n const navbarEl = el as NavbarElement;\n const scrollContainer = findScrollContainer(el);\n\n navbarEl.closeMobileMenu = closeMobileMenu;\n navbarEl.openMobileMenu = openMobileMenu;\n navbarEl.toggleMobileMenu = toggleMobileMenu;\n\n const stopScroll = listen(\n window,\n 'scroll',\n () => {\n updateScrolled(scrollContainer);\n },\n { passive: true },\n );\n const stopContainerScroll = scrollContainer\n ? listen(\n scrollContainer,\n 'scroll',\n () => {\n updateScrolled(scrollContainer);\n },\n { passive: true },\n )\n : undefined;\n const stopEscape = listen(el, 'keydown', (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || !isMobileMenuOpen.value) return;\n\n closeMobileMenu();\n });\n\n let mediaCleanup: (() => void) | undefined;\n let mobileSidebarCleanup: (() => void) | undefined;\n let modeTransitionTimeout: ReturnType<typeof setTimeout> | undefined;\n let previousMode: NavbarMode | undefined;\n\n const resolveMobileSidebarTarget = () => {\n mobileSidebarCleanup?.();\n mobileSidebarCleanup = undefined;\n mobileSidebarTarget.value = null;\n isExternalMobileMode.value = false;\n isExternalMobileOpen.value = false;\n\n if (!String(props['mobile-sidebar'].value ?? '').trim()) return;\n\n const target = getExternalSidebar();\n\n if (!target) {\n const root = el.getRootNode();\n const discoveryRoot = root instanceof Document || root instanceof ShadowRoot ? root : document;\n const discoveryObserver = new MutationObserver(() => {\n if (getExternalSidebar()) resolveMobileSidebarTarget();\n });\n\n discoveryObserver.observe(discoveryRoot, { childList: true, subtree: true });\n mobileSidebarCleanup = () => discoveryObserver.disconnect();\n\n return;\n }\n\n const syncTargetState = (event?: CustomEvent<{ open: boolean }>) => {\n const hasBottomNav = target.hasAttribute('data-bottom-nav');\n const hasDrawerOpen = target.hasAttribute('data-drawer-open');\n\n isExternalMobileMode.value = hasBottomNav;\n isExternalMobileOpen.value = event?.detail ? event.detail.open : hasDrawerOpen;\n\n // If the sidebar is reporting it's open, it must be in bottom-nav mode\n // (either natural or forced). We honor this to avoid race conditions\n // where attributes haven't reflected yet.\n if (isExternalMobileOpen.value && !isExternalMobileMode.value) {\n isExternalMobileMode.value = true;\n }\n\n syncMobileMode();\n };\n\n const targetObserver = new MutationObserver(() => {\n syncTargetState();\n });\n\n syncTargetState();\n targetObserver.observe(target, {\n attributeFilter: ['data-bottom-nav', 'data-drawer-open'],\n attributes: true,\n });\n target.addEventListener('drawer-change', syncTargetState as EventListener);\n mobileSidebarCleanup = () => {\n targetObserver.disconnect();\n target.removeEventListener('drawer-change', syncTargetState as EventListener);\n };\n mobileSidebarTarget.value = target;\n };\n\n const triggerModeTransition = () => {\n isModeTransitioning.value = true;\n\n if (modeTransitionTimeout != null) clearTimeout(modeTransitionTimeout);\n\n modeTransitionTimeout = setTimeout(() => {\n isModeTransitioning.value = false;\n modeTransitionTimeout = undefined;\n }, 220);\n };\n\n const stopModeWatch = watch(\n computed(() => props.floating.value && props.sticky.value),\n (enabled) => {\n if (!enabled) {\n if (isScrolled.value) isScrolled.value = false;\n\n if (isModeTransitioning.value) isModeTransitioning.value = false;\n\n previousMode = undefined;\n\n if (modeTransitionTimeout != null) {\n clearTimeout(modeTransitionTimeout);\n modeTransitionTimeout = undefined;\n }\n\n return;\n }\n\n resetScrollBase(scrollContainer);\n updateScrolled(scrollContainer);\n\n const currentMode = computeMode();\n\n previousMode = currentMode;\n },\n { immediate: true },\n );\n\n const stopMobileSidebarWatch = watch(\n props['mobile-sidebar'],\n () => {\n resolveMobileSidebarTarget();\n },\n { immediate: true },\n );\n\n const stopModeTransitionWatch = watch(\n computed(() => computeMode()),\n (nextMode) => {\n const comboEnabled = props.floating.value && props.sticky.value;\n\n if (!comboEnabled) {\n previousMode = nextMode ?? undefined;\n\n return;\n }\n\n const next = nextMode ?? undefined;\n\n if (previousMode && next && previousMode !== next) {\n triggerModeTransition();\n }\n\n previousMode = next;\n },\n { immediate: true },\n );\n\n const stopMobileMenuSlotWatch = watch(\n hasMobileMenu,\n (hasContent) => {\n if (!hasContent && isMobileMenuOpen.value) {\n closeMobileMenu();\n }\n },\n { immediate: true },\n );\n const syncMobileMenuContent = () => {\n hasMobileMenu.value = mobileMenuElements.value.some(hasElementContent);\n };\n const stopMobileMenuElementsWatch = watch(mobileMenuElements, syncMobileMenuContent, { immediate: true });\n const mobileMenuContentObserver = new MutationObserver(() => {\n syncMobileMenuContent();\n });\n\n // Parser-created demo content can populate an already-assigned slot wrapper after this\n // custom element connects; `slotchange` alone does not observe that nested content.\n mobileMenuContentObserver.observe(el, { childList: true, subtree: true });\n\n watch(\n props.breakpoint,\n (query) => {\n // Untrack the callback body: the watch already tracks `props.breakpoint` via\n // its internal `read()`. Without untrack, signals read inside (e.g.\n // `mediaMatches` via `syncMobileMode()`) become dependencies of this watch\n // effect. When the sentinel updates `mediaMatches` on a viewport change, the\n // effect re-runs: `EffectNode.run()` disposes the previous scope (killing the\n // mediaHandle and its MediaQueryList listener) but the callback is NOT invoked\n // (source unchanged), so no new handle is created. The navbar is then deaf to\n // further MQ changes and stays stuck in mobile mode after resizing back to\n // desktop.\n untrack(() => {\n mediaCleanup?.();\n mediaCleanup = undefined;\n\n const mediaQuery = String(query ?? '').trim();\n\n maxWidthPx.value = parseMaxWidthPx(mediaQuery);\n mediaMatches.value = false;\n\n const width = readContainerWidth(el);\n\n sizeMatches.value = width > 0 && maxWidthPx.value != null ? width <= maxWidthPx.value : false;\n syncMobileMode();\n\n if (props['container-breakpoints'].value && maxWidthPx.value != null) {\n return;\n }\n\n if (!mediaQuery) {\n return;\n }\n\n try {\n const mediaHandle = createMediaQuery(mediaQuery);\n const syncMedia = (state: ReturnType<typeof mediaHandle.getSnapshot>) => {\n if (state) {\n mediaMatches.value = state.matches;\n syncMobileMode();\n }\n };\n\n syncMedia(mediaHandle.getSnapshot());\n\n const mediaCleanupFn = watch(\n fromSubscribable(mediaHandle, { signal: mediaHandle.disposalSignal }),\n syncMedia,\n );\n mediaCleanup = () => {\n mediaCleanupFn.dispose();\n mediaHandle.dispose();\n };\n } catch (error) {\n if (!(error instanceof SentinelUnavailableError)) throw error;\n }\n });\n },\n { immediate: true },\n );\n\n const stopResizeEffect =\n typeof ResizeObserver === 'function'\n ? (() => {\n const hostSize = createElementSize(el);\n onCleanup(() => hostSize.dispose());\n const hostSizeSignal = fromSubscribable(hostSize, { signal: hostSize.disposalSignal });\n\n const wrapperEl = el.parentElement;\n const containerEl = resolveContainerElement(el);\n const wrapperSize = wrapperEl ? createElementSize(wrapperEl) : undefined;\n const parentSize = containerEl && containerEl !== wrapperEl ? createElementSize(containerEl) : undefined;\n\n if (wrapperSize) {\n onCleanup(() => wrapperSize.dispose());\n }\n if (parentSize) {\n onCleanup(() => parentSize.dispose());\n }\n\n const wrapperSizeSignal = wrapperSize\n ? fromSubscribable(wrapperSize, { signal: wrapperSize.disposalSignal })\n : undefined;\n const parentSizeSignal = parentSize\n ? fromSubscribable(parentSize, { signal: parentSize.disposalSignal })\n : undefined;\n\n return watch(\n computed(() => [\n hostSizeSignal.value?.width,\n hostSizeSignal.value?.height,\n wrapperSizeSignal?.value?.width,\n wrapperSizeSignal?.value?.height,\n parentSizeSignal?.value?.width,\n parentSizeSignal?.value?.height,\n ]),\n () => {\n const width = readContainerWidth(el);\n const parentWidth = resolveContainerElement(el)?.clientWidth ?? 0;\n\n sizeMatches.value = width > 0 && maxWidthPx.value != null ? width <= maxWidthPx.value : false;\n isPreviewMode.value = parentWidth > 0 && parentWidth < window.innerWidth;\n syncMobileMode();\n },\n );\n })()\n : undefined;\n\n return () => {\n stopEscape?.();\n stopScroll?.();\n stopContainerScroll?.();\n stopModeWatch?.dispose();\n stopModeTransitionWatch?.dispose();\n stopMobileMenuSlotWatch?.dispose();\n stopMobileMenuElementsWatch?.dispose();\n stopMobileSidebarWatch?.dispose();\n mobileMenuContentObserver.disconnect();\n mediaCleanup?.();\n mobileSidebarCleanup?.();\n stopResizeEffect?.dispose();\n\n if (modeTransitionTimeout != null) {\n clearTimeout(modeTransitionTimeout);\n modeTransitionTimeout = undefined;\n }\n };\n });\n\n return html`\n <nav part=\"nav\" aria-label=\"${props.label}\">\n <div class=\"navbar\" part=\"bar\">\n <div class=\"navbar-logo\" part=\"logo\" ?hidden=${() => !hasLogo.value}>\n <slot name=\"logo\"></slot>\n </div>\n\n <div class=\"navbar-start\" part=\"start\" ?hidden=${() => !hasStart.value}>\n <slot name=\"start\"></slot>\n </div>\n\n <div class=\"navbar-center\" part=\"center\" ?hidden=${() => !hasCenter.value}>\n <slot></slot>\n </div>\n\n <div class=\"navbar-end\" part=\"end\" ?hidden=${() => !hasEnd.value}>\n <slot name=\"end\"></slot>\n </div>\n\n <div class=\"navbar-persistent\" part=\"persistent\" ?hidden=${() => !hasPersistent.value}>\n <slot name=\"persistent\"></slot>\n </div>\n\n <button\n class=\"mobile-toggle\"\n part=\"mobile-toggle\"\n type=\"button\"\n aria-label=\"${() =>\n (hasMobileMenu.value ? isMobileMenuOpen.value : isExternalMobileOpen.value)\n ? props['menu-close-label'].value\n : props['menu-open-label'].value}\"\n aria-controls=\"mobile-menu-panel\"\n aria-expanded=\"${() => String(hasMobileMenu.value ? isMobileMenuOpen.value : isExternalMobileOpen.value)}\"\n ?hidden=${() => !isMobile.value || (!hasMobileMenu.value && !mobileSidebarTarget.value)}\n @click=\"${toggleMobileMenu}\">\n <ore-icon\n name=\"${() =>\n (hasMobileMenu.value ? isMobileMenuOpen.value : isExternalMobileOpen.value)\n ? props['menu-close-icon'].value\n : props['menu-icon'].value}\"\n size=\"18\"\n stroke-width=\"2.5\"\n aria-hidden=\"true\"></ore-icon>\n </button>\n </div>\n\n <div\n id=\"mobile-menu-panel\"\n class=\"mobile-menu-panel\"\n part=\"mobile-menu\"\n role=\"navigation\"\n aria-label=\"${() => `${props.label.value} mobile menu`}\"\n ?hidden=${() => !isMobile.value || !isMobileMenuOpen.value || !hasMobileMenu.value}>\n <slot name=\"mobile-menu\"></slot>\n </div>\n </nav>\n `;\n },\n styles: [\n colorThemeMixin,\n elevationMixin,\n roundedVariantMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n frostVariantMixin('.navbar'),\n navbarStyles,\n ],\n});\n\n/**\n * `ore-navbar-item`: Navigation item for navbar slots.\n *\n * Renders as an anchor when `href` is provided and item is not disabled.\n *\n * @element ore-navbar-item\n *\n * @attr {string} aria-label - Accessible name for icon-only items, forwarded to the internal control\n * @attr {boolean} active - Marks item as the current active route\n * @attr {boolean} disabled - Disables interaction\n * @attr {string} href - Link URL; renders as `<a>` when set\n * @attr {boolean} icon-only - Compact, symmetric padding for items with no visible label\n * @attr {string} rel - Anchor `rel` attribute (links only)\n * @attr {string} target - Anchor `target` attribute (links only)\n *\n * @slot - Item label\n * @slot icon - Optional icon before the label\n *\n * @part item - Root clickable element (anchor or button).\n * @part item-icon - Leading icon container.\n * @part item-label - Label text container.\n * @part item-end - Trailing content container.\n *\n * @example\n * ```html\n * <ore-navbar-item href=\"/home\">Home</ore-navbar-item>\n * <ore-navbar-item href=\"/docs\" active>Docs</ore-navbar-item>\n * <ore-navbar-item href=\"/about\" target=\"_blank\" rel=\"noopener\">About</ore-navbar-item>\n * <ore-navbar-item disabled>Disabled</ore-navbar-item>\n * <ore-navbar-item icon-only aria-label=\"Search\"><ore-icon slot=\"icon\" name=\"search\"></ore-icon></ore-navbar-item>\n * ```\n */\nexport const NAVBAR_ITEM_TAG = 'ore-navbar-item' as const;\ndefine<OreNavbarItemProps>(NAVBAR_ITEM_TAG, {\n props: {\n active: prop.bool(false),\n ariaLabel: prop.string(),\n disabled: prop.bool(false),\n href: prop.string(),\n 'icon-only': prop.bool(false),\n rel: prop.string(),\n target: prop.string(),\n },\n setup(props) {\n const slots = useSlots();\n\n const hasIcon = () => slots.has('icon').value;\n const hasEnd = () => slots.has('end').value;\n const navbarCtx = inject(NAVBAR_CTX);\n\n bind({\n attr: {\n 'navbar-mobile': () => (navbarCtx?.isMobile.value ? true : undefined),\n 'navbar-mobile-open': () => (navbarCtx?.mobileMenuOpen.value ? true : undefined),\n },\n });\n\n const isLink = () => Boolean(props.href.value) && !props.disabled.value;\n\n // Prevent reverse tabnapping: auto-inject noopener + noreferrer for _blank links.\n const effectiveRel = computed(() => computeSafeRel(props.rel.value, props.target.value));\n\n // The `aria-current` token: `location` for in-page anchors (the current position\n // within this document), `page` for actual page routes. Announcing a section\n // anchor as `page` would claim the section is the current document.\n const ariaCurrent = computed<null | 'location' | 'page'>(() => {\n if (!props.active.value) return null;\n const href = props.href.value;\n return href !== undefined && href.startsWith('#') ? 'location' : 'page';\n });\n\n // Closes the mobile overflow panel when one of its items is activated, otherwise picking a\n // destination (or firing any other action placed in the `mobile-menu` slot) leaves the panel\n // open over the page until the user separately taps the toggle again. Only matters while\n // actually in mobile mode; on desktop `navbarCtx.mobileMenuOpen` is never true to begin with,\n // so `closeMobileMenu()` (idempotent: see `setMobileMenu`'s equality check) is a no-op there.\n const closeMobileMenuIfOpen = (): void => {\n if (navbarCtx?.mobileMenuOpen.value) navbarCtx.closeMobileMenu();\n };\n\n const renderItemContent = () => html`\n <span class=\"item-icon\" part=\"item-icon\" ?hidden=${() => !hasIcon()} aria-hidden=\"true\">\n <slot name=\"icon\"></slot>\n </span>\n <span class=\"item-label\" part=\"item-label\"><slot></slot></span>\n <span class=\"item-end\" part=\"item-end\" ?hidden=${() => !hasEnd()}>\n <slot name=\"end\"></slot>\n </span>\n `;\n\n return html`\n ${() => {\n if (isLink()) {\n return html`\n <a\n class=\"item\"\n part=\"item\"\n href=\"${props.href}\"\n rel=\"${effectiveRel}\"\n target=\"${props.target}\"\n aria-label=\"${() => props.ariaLabel.value ?? null}\"\n aria-current=\"${ariaCurrent}\"\n @click=${closeMobileMenuIfOpen}>\n ${renderItemContent()}\n </a>\n `;\n }\n\n if (props.disabled.value) {\n return html`\n <div\n class=\"item\"\n part=\"item\"\n tabindex=\"-1\"\n aria-label=\"${() => props.ariaLabel.value ?? null}\"\n aria-disabled=\"true\">${renderItemContent()}</div>\n `;\n }\n\n return html`\n <button\n class=\"item\"\n part=\"item\"\n type=\"button\"\n aria-label=\"${() => props.ariaLabel.value ?? null}\"\n @click=${closeMobileMenuIfOpen}>${renderItemContent()}</button>\n `;\n }}\n `;\n },\n styles: [coarsePointerMixin, navbarStyles],\n});\n"],"mappings":"ggBAsCA,IAAM,EAAkB,CAAC,OAAQ,QAAS,WAAY,UAAW,OAAO,EAGlE,QACJ,EACA,EACA,EACA,IAEK,GAEL,EAAG,iBAAiB,EAAM,EAA0B,CAAO,MAE9C,EAAG,oBAAoB,EAAM,EAA0B,CAAO,OAJrD,CAAC,EASnB,kBAAqB,GACrB,EAAE,aAAc,cAEhB,EAAG,QAAQ,SAAS,GAAG,GAEvB,EAAG,kBAAoB,EAAU,IAE7B,EAAG,aAAe,GAAA,CAAI,KAAK,CAAC,CAAC,OAAS,EAG1C,oBAAuB,GAA2C,CACtE,IAAI,EAA8B,EAAM,cAExC,KAAO,GAAS,CAEd,IAAM,EADS,OAAO,iBAAiB,CACrB,CAAA,CAAO,UAEzB,GAAI,IAAc,QAAU,IAAc,UAAY,IAAc,UAClE,OAAO,EAGT,EAAU,EAAQ,aACpB,CAEA,OAAO,IACT,EAUa,GAAA,EAAa,EAAA,cAAA,CAA6B,eAAe,EA8IzD,EAAa,cAC1B,EAAA,EAAA,OAAA,CAAuB,EAAY,CACjC,MAAO,CACL,WAAY,EAAA,KAAK,OAAO,oBAAoB,EAC5C,MAAO,EAAA,KAAK,MAAM,EAAA,YAAY,EAC9B,wBAAyB,EAAA,KAAK,KAAK,EAAK,EACxC,UAAW,EAAA,KAAK,MAAM,EAAA,gBAAgB,EACtC,SAAU,EAAA,KAAK,KAAK,EAAK,EACzB,MAAO,EAAA,KAAK,OAAO,iBAAiB,EACpC,kBAAmB,EAAA,KAAK,OAAO,GAAG,EAClC,mBAAoB,EAAA,KAAK,OAAO,uBAAuB,EACvD,YAAa,EAAA,KAAK,OAAO,MAAM,EAC/B,kBAAmB,EAAA,KAAK,OAAO,sBAAsB,EACrD,iBAAkB,EAAA,KAAK,OAAO,EAC9B,QAAS,EAAA,KAAK,MAAM,EAAA,aAAa,EACjC,mBAAoB,EAAA,KAAK,OAAO,EAAE,EAClC,OAAQ,EAAA,KAAK,KAAK,EAAK,EACvB,QAAS,EAAA,KAAK,MAAM,CAAe,CACrC,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAK,EAAA,QAAA,CAAQ,EACb,GAAA,EAAO,EAAA,QAAA,CAAyB,EAChC,GAAA,EAAQ,EAAA,SAAA,CAAS,EAEjB,EAAU,EAAM,IAAI,MAAM,EAC1B,EAAW,EAAM,IAAI,OAAO,EAC5B,EAAY,EAAM,IAAI,EACtB,EAAS,EAAM,IAAI,KAAK,EACxB,EAAgB,EAAM,IAAI,YAAY,EACtC,EAAqB,EAAM,SAAS,aAAa,EACjD,GAAA,EAAgB,EAAA,OAAA,CAAO,EAAK,EAC5B,GAAA,EAAsB,EAAA,OAAA,CAAoC,IAAI,EAC9D,GAAA,EAAuB,EAAA,OAAA,CAAO,EAAK,EACnC,GAAA,EAAuB,EAAA,OAAA,CAAO,EAAK,EACnC,GAAA,EAAW,EAAA,OAAA,CAAO,EAAK,EACvB,GAAA,EAAmB,EAAA,OAAA,CAAO,EAAK,EAC/B,GAAA,EAAsB,EAAA,OAAA,CAAO,EAAK,EAClC,GAAA,EAAa,EAAA,OAAA,CAAO,EAAK,EACzB,GAAA,EAAa,EAAA,OAAA,CAAO,CAAC,EACrB,GAAA,EAAe,EAAA,OAAA,CAAO,EAAK,EAC3B,GAAA,EAAc,EAAA,OAAA,CAAO,EAAK,EAC1B,GAAA,EAAa,EAAA,OAAA,CAA2B,EAAA,gBAAgB,EAAM,WAAW,KAAK,CAAC,EAC/E,GAAA,EAAgB,EAAA,OAAA,CAAO,EAAK,EAE5B,uBAA2B,CAC/B,IAAM,EAAW,OAAO,EAAM,iBAAiB,CAAC,OAAS,EAAE,CAAC,CAAC,KAAK,EAElE,GAAI,CAAC,EAAU,OAAO,KAEtB,IAAM,EAAO,EAAG,YAAY,EAM5B,OAJE,aAAgB,YAAc,aAAgB,SACzC,EAAK,cAAc,CAAQ,EAC5B,OAEkB,SAAS,cAAc,CAAQ,CACzD,GAEA,EAAA,EAAA,QAAA,CAAQ,EAAY,CAGlB,oBAAuB,gBAAgB,EACvC,UAAA,EAAU,EAAA,SAAA,KAAe,EAAS,KAAK,EACvC,gBAAA,EAAgB,EAAA,SAAA,KAAe,EAAiB,KAAK,CACvD,CAAC,EAED,IAAM,gBAA4C,CAChD,GAAI,EAAM,SAAS,OAAS,EAAM,OAAO,MACvC,OAAO,EAAW,MAAQ,SAAW,WAGvC,GAAI,EAAM,SAAS,MAAO,MAAO,WAEjC,GAAI,EAAM,OAAO,MAAO,MAAO,QAGjC,EAEM,cAAiB,GAAkB,CACvC,IAAM,EAAO,EAAQ,EAErB,GAAI,CAAC,EAAc,MAAO,CACxB,IAAM,EAAS,EAAoB,OAAS,mBAAmB,EAE/D,GAAI,EAAQ,CACN,EACF,EAAO,aAAa,EAEpB,EAAO,cAAc,EAGvB,MACF,CACF,EAEI,IAAS,EAAc,QAEvB,EAAiB,QAAU,IAE/B,EAAiB,MAAQ,EACzB,EAAK,qBAAsB,CAAE,MAAK,CAAC,EACrC,EAEM,oBAAwB,cAAc,EAAK,EAC3C,mBAAuB,cAAc,EAAI,EACzC,qBAAyB,CAC7B,GAAI,CAAC,EAAc,MAAO,CACxB,IAAM,EAAS,EAAoB,OAAS,mBAAmB,EAE/D,GAAI,EAAQ,CACN,OAAO,EAAO,cAAiB,WACjC,EAAO,aAAa,EACX,OAAO,EAAO,YAAe,YAAc,OAAO,EAAO,aAAgB,aACnE,EAAO,aAAa,kBAE/B,EAAQ,EAAO,YAAY,EAC1B,EAAO,WAAW,GAGzB,MACF,CACF,CAEA,cAAc,CAAC,EAAiB,KAAK,CACvC,EAEM,mBAAuB,CAC3B,IAAM,GACH,EAAoB,OAAS,CAAC,EAAc,MAAQ,EAAqB,MAAQ,KAClF,EAAa,OACZ,EAAQ,EAAM,wBAAwB,CAAC,OAAU,EAAY,MAEhE,EAAS,MAAQ,EAEZ,GAAM,gBAAgB,CAC7B,EAEM,iBAAoB,GACpB,EACK,EAAgB,WAAa,EAG/B,OAAO,SAAW,SAAS,gBAAgB,WAAa,EAG3D,gBAAmB,GAAwC,CAC/D,EAAW,MAAQ,iBAAiB,CAAe,CACrD,EAEM,gBAAkB,EAAsC,OAAS,CACrE,GAAI,EAAE,EAAM,SAAS,OAAS,EAAM,OAAO,OAAQ,CACjD,AAAsB,EAAW,QAAQ,GAEzC,MACF,CAEA,IAAM,EAAY,KAAK,IAAI,EAAG,OAAO,EAAM,mBAAmB,CAAC,KAAK,GAAK,CAAC,EAEpE,EADgB,iBAAiB,CACzB,EAAgB,EAAW,MAEzC,EAAW,MAAQ,EAAQ,CAC7B,EAuUA,OArUA,EAAA,EAAA,KAAA,CAAK,CACH,KAAM,CACJ,kBAAsB,EAAS,MAAQ,GAAO,IAAA,GAC9C,uBAA2B,EAAS,OAAS,EAAiB,MAAQ,GAAO,IAAA,GAC7E,gBAAmB,YAAY,GAAK,KACpC,8BAAkC,EAAoB,MAAQ,GAAO,IAAA,GACrE,wBAA4B,EAAc,MAAQ,GAAO,IAAA,GACzD,oBAAwB,EAAM,SAAS,OAAS,EAAM,OAAO,OAAS,EAAW,MAAQ,GAAO,IAAA,EAClG,CACF,CAAC,GAED,EAAA,EAAA,UAAA,KAAgB,CACd,IAAM,EAAW,EACX,EAAkB,oBAAoB,CAAE,EAE9C,EAAS,gBAAkB,gBAC3B,EAAS,eAAiB,eAC1B,EAAS,iBAAmB,iBAE5B,IAAM,EAAa,OACjB,OACA,aACM,CACJ,eAAe,CAAe,CAChC,EACA,CAAE,QAAS,EAAK,CAClB,EACM,EAAsB,EACxB,OACE,EACA,aACM,CACJ,eAAe,CAAe,CAChC,EACA,CAAE,QAAS,EAAK,CAClB,EACA,IAAA,GACE,EAAa,OAAO,EAAI,UAAY,GAAyB,CAC7D,EAAM,MAAQ,UAAa,EAAiB,OAEhD,gBAAgB,CAClB,CAAC,EAEG,aACA,qBACA,EACA,EAEE,+BAAmC,CAOvC,GANA,uBAAuB,EACvB,qBAAuB,IAAA,GACvB,EAAoB,MAAQ,KAC5B,EAAqB,MAAQ,GAC7B,EAAqB,MAAQ,GAEzB,CAAC,OAAO,EAAM,iBAAiB,CAAC,OAAS,EAAE,CAAC,CAAC,KAAK,EAAG,OAEzD,IAAM,EAAS,mBAAmB,EAElC,GAAI,CAAC,EAAQ,CACX,IAAM,EAAO,EAAG,YAAY,EACtB,EAAgB,aAAgB,UAAY,aAAgB,WAAa,EAAO,SAChF,EAAoB,IAAI,qBAAuB,CAC/C,mBAAmB,GAAG,2BAA2B,CACvD,CAAC,EAED,EAAkB,QAAQ,EAAe,CAAE,UAAW,GAAM,QAAS,EAAK,CAAC,EAC3E,yBAA6B,EAAkB,WAAW,EAE1D,MACF,CAEA,IAAM,gBAAmB,GAA2C,CAClE,IAAM,EAAe,EAAO,aAAa,iBAAiB,EACpD,EAAgB,EAAO,aAAa,kBAAkB,EAE5D,EAAqB,MAAQ,EAC7B,EAAqB,MAAQ,GAAO,OAAS,EAAM,OAAO,KAAO,EAK7D,EAAqB,OAAS,CAAC,EAAqB,QACtD,EAAqB,MAAQ,IAG/B,eAAe,CACjB,EAEM,EAAiB,IAAI,qBAAuB,CAChD,gBAAgB,CAClB,CAAC,EAED,gBAAgB,EAChB,EAAe,QAAQ,EAAQ,CAC7B,gBAAiB,CAAC,kBAAmB,kBAAkB,EACvD,WAAY,EACd,CAAC,EACD,EAAO,iBAAiB,gBAAiB,eAAgC,EACzE,yBAA6B,CAC3B,EAAe,WAAW,EAC1B,EAAO,oBAAoB,gBAAiB,eAAgC,CAC9E,EACA,EAAoB,MAAQ,CAC9B,EAEM,0BAA8B,CAClC,EAAoB,MAAQ,GAExB,GAAyB,MAAM,aAAa,CAAqB,EAErE,EAAwB,eAAiB,CACvC,EAAoB,MAAQ,GAC5B,EAAwB,IAAA,EAC1B,EAAG,GAAG,CACR,EAEM,GAAA,EAAgB,EAAA,MAAA,EAAA,EACpB,EAAA,SAAA,KAAe,EAAM,SAAS,OAAS,EAAM,OAAO,KAAK,EACxD,GAAY,CACX,GAAI,CAAC,EAAS,CACZ,AAAsB,EAAW,QAAQ,GAEzC,AAA+B,EAAoB,QAAQ,GAE3D,EAAe,IAAA,GAEX,GAAyB,OAC3B,aAAa,CAAqB,EAClC,EAAwB,IAAA,IAG1B,MACF,CAEA,gBAAgB,CAAe,EAC/B,eAAe,CAAe,EAI9B,EAFoB,YAEL,CACjB,EACA,CAAE,UAAW,EAAK,CACpB,EAEM,GAAA,EAAyB,EAAA,MAAA,CAC7B,EAAM,sBACA,CACJ,2BAA2B,CAC7B,EACA,CAAE,UAAW,EAAK,CACpB,EAEM,GAAA,EAA0B,EAAA,MAAA,EAAA,EAC9B,EAAA,SAAA,KAAe,YAAY,CAAC,EAC3B,GAAa,CAGZ,GAAI,EAFiB,EAAM,SAAS,OAAS,EAAM,OAAO,OAEvC,CACjB,EAAe,GAAY,IAAA,GAE3B,MACF,CAEA,IAAM,EAAO,GAAY,IAAA,GAErB,GAAgB,GAAQ,IAAiB,GAC3C,sBAAsB,EAGxB,EAAe,CACjB,EACA,CAAE,UAAW,EAAK,CACpB,EAEM,GAAA,EAA0B,EAAA,MAAA,CAC9B,EACC,GAAe,CACV,CAAC,GAAc,EAAiB,OAClC,gBAAgB,CAEpB,EACA,CAAE,UAAW,EAAK,CACpB,EACM,0BAA8B,CAClC,EAAc,MAAQ,EAAmB,MAAM,KAAK,iBAAiB,CACvE,EACM,GAAA,EAA8B,EAAA,MAAA,CAAM,EAAoB,sBAAuB,CAAE,UAAW,EAAK,CAAC,EAClG,EAA4B,IAAI,qBAAuB,CAC3D,sBAAsB,CACxB,CAAC,EAID,EAA0B,QAAQ,EAAI,CAAE,UAAW,GAAM,QAAS,EAAK,CAAC,GAExE,EAAA,EAAA,MAAA,CACE,EAAM,WACL,GAAU,EAUT,EAAA,EAAA,QAAA,KAAc,CACZ,eAAe,EACf,aAAe,IAAA,GAEf,IAAM,EAAa,OAAO,GAAS,EAAE,CAAC,CAAC,KAAK,EAE5C,EAAW,MAAQ,EAAA,gBAAgB,CAAU,EAC7C,EAAa,MAAQ,GAErB,IAAM,EAAQ,EAAA,mBAAmB,CAAE,EAEnC,KAAY,MAAQ,EAAQ,GAAK,EAAW,OAAS,MAAO,GAAS,EAAW,MAChF,eAAe,EAEX,IAAM,wBAAwB,CAAC,OAAS,EAAW,OAAS,OAI3D,EAIL,GAAI,CACF,IAAM,GAAA,EAAc,EAAA,iBAAA,CAAiB,CAAU,EACzC,UAAa,GAAsD,CACnE,IACF,EAAa,MAAQ,EAAM,QAC3B,eAAe,EAEnB,EAEA,UAAU,EAAY,YAAY,CAAC,EAEnC,IAAM,GAAA,EAAiB,EAAA,MAAA,EAAA,EACrB,EAAA,iBAAA,CAAiB,EAAa,CAAE,OAAQ,EAAY,cAAe,CAAC,EACpE,SACF,EACA,iBAAqB,CACnB,EAAe,QAAQ,EACvB,EAAY,QAAQ,CACtB,CACF,OAAS,EAAO,CACd,GAAI,EAAE,aAAiB,EAAA,0BAA2B,MAAM,CAC1D,CACF,CAAC,CACH,EACA,CAAE,UAAW,EAAK,CACpB,EAEA,IAAM,EACJ,OAAO,gBAAmB,gBACf,CACL,IAAM,GAAA,EAAW,EAAA,kBAAA,CAAkB,CAAE,GACrC,EAAA,EAAA,UAAA,KAAgB,EAAS,QAAQ,CAAC,EAClC,IAAM,GAAA,EAAiB,EAAA,iBAAA,CAAiB,EAAU,CAAE,OAAQ,EAAS,cAAe,CAAC,EAE/E,EAAY,EAAG,cACf,EAAc,EAAA,wBAAwB,CAAE,EACxC,EAAc,GAAA,EAAY,EAAA,kBAAA,CAAkB,CAAS,EAAI,IAAA,GACzD,EAAa,GAAe,IAAgB,GAAA,EAAY,EAAA,kBAAA,CAAkB,CAAW,EAAI,IAAA,GAE3F,IACF,EAAA,EAAA,UAAA,KAAgB,EAAY,QAAQ,CAAC,EAEnC,IACF,EAAA,EAAA,UAAA,KAAgB,EAAW,QAAQ,CAAC,EAGtC,IAAM,EAAoB,GAAA,EACtB,EAAA,iBAAA,CAAiB,EAAa,CAAE,OAAQ,EAAY,cAAe,CAAC,EACpE,IAAA,GACE,EAAmB,GAAA,EACrB,EAAA,iBAAA,CAAiB,EAAY,CAAE,OAAQ,EAAW,cAAe,CAAC,EAClE,IAAA,GAEJ,OAAA,EAAO,EAAA,MAAA,EAAA,EACL,EAAA,SAAA,KAAe,CACb,EAAe,OAAO,MACtB,EAAe,OAAO,OACtB,GAAmB,OAAO,MAC1B,GAAmB,OAAO,OAC1B,GAAkB,OAAO,MACzB,GAAkB,OAAO,MAC3B,CAAC,MACK,CACJ,IAAM,EAAQ,EAAA,mBAAmB,CAAE,EAC7B,EAAc,EAAA,wBAAwB,CAAE,CAAC,EAAE,aAAe,EAEhE,EAAY,MAAQ,EAAQ,GAAK,EAAW,OAAS,MAAO,GAAS,EAAW,MAChF,EAAc,MAAQ,EAAc,GAAK,EAAc,OAAO,WAC9D,eAAe,CACjB,CACF,CACF,EAAA,CAAG,EACH,IAAA,GAEN,UAAa,CACX,IAAa,EACb,IAAa,EACb,IAAsB,EACtB,GAAe,QAAQ,EACvB,GAAyB,QAAQ,EACjC,GAAyB,QAAQ,EACjC,GAA6B,QAAQ,EACrC,GAAwB,QAAQ,EAChC,EAA0B,WAAW,EACrC,eAAe,EACf,uBAAuB,EACvB,GAAkB,QAAQ,EAEtB,GAAyB,OAC3B,aAAa,CAAqB,EAClC,EAAwB,IAAA,GAE5B,CACF,CAAC,EAEM,EAAA,IAAI;oCACqB,EAAM,MAAM;;6DAEe,CAAC,EAAQ,MAAM;;;;+DAIb,CAAC,EAAS,MAAM;;;;iEAId,CAAC,EAAU,MAAM;;;;2DAIvB,CAAC,EAAO,MAAM;;;;yEAIA,CAAC,EAAc,MAAM;;;;;;;;+BASjF,EAAc,MAAQ,EAAiB,MAAQ,EAAqB,OACjE,EAAM,mBAAmB,CAAC,MAC1B,EAAM,kBAAkB,CAAC,MAAM;;iCAEd,OAAO,EAAc,MAAQ,EAAiB,MAAQ,EAAqB,KAAK,EAAE;0BACzF,CAAC,EAAS,OAAU,CAAC,EAAc,OAAS,CAAC,EAAoB,MAAO;sBAC9E,iBAAiB;;2BAGtB,EAAc,MAAQ,EAAiB,MAAQ,EAAqB,OACjE,EAAM,kBAAkB,CAAC,MACzB,EAAM,YAAY,CAAC,MAAM;;;;;;;;;;;;4BAYf,GAAG,EAAM,MAAM,MAAM,cAAc;wBACvC,CAAC,EAAS,OAAS,CAAC,EAAiB,OAAS,CAAC,EAAc,MAAM;;;;KAK3F,EACA,OAAQ,CACN,EAAA,gBACA,EAAA,eACA,EAAA,oBACA,EAAA,mBACA,EAAA,mBACA,EAAA,kBAAkB,SAAS,EAC3B,EAAA,OACF,CACF,CAAC,EAkCD,IAAa,EAAkB,mBAC/B,EAAA,EAAA,OAAA,CAA2B,EAAiB,CAC1C,MAAO,CACL,OAAQ,EAAA,KAAK,KAAK,EAAK,EACvB,UAAW,EAAA,KAAK,OAAO,EACvB,SAAU,EAAA,KAAK,KAAK,EAAK,EACzB,KAAM,EAAA,KAAK,OAAO,EAClB,YAAa,EAAA,KAAK,KAAK,EAAK,EAC5B,IAAK,EAAA,KAAK,OAAO,EACjB,OAAQ,EAAA,KAAK,OAAO,CACtB,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAQ,EAAA,SAAA,CAAS,EAEjB,YAAgB,EAAM,IAAI,MAAM,CAAC,CAAC,MAClC,WAAe,EAAM,IAAI,KAAK,CAAC,CAAC,MAChC,GAAA,EAAY,EAAA,OAAA,CAAO,CAAU,GAEnC,EAAA,EAAA,KAAA,CAAK,CACH,KAAM,CACJ,oBAAwB,GAAW,SAAS,MAAQ,GAAO,IAAA,GAC3D,yBAA6B,GAAW,eAAe,MAAQ,GAAO,IAAA,EACxE,CACF,CAAC,EAED,IAAM,WAAe,EAAQ,EAAM,KAAK,OAAU,CAAC,EAAM,SAAS,MAG5D,GAAA,EAAe,EAAA,SAAA,KAAe,EAAA,eAAe,EAAM,IAAI,MAAO,EAAM,OAAO,KAAK,CAAC,EAKjF,GAAA,EAAc,EAAA,SAAA,KAA2C,CAC7D,GAAI,CAAC,EAAM,OAAO,MAAO,OAAO,KAChC,IAAM,EAAO,EAAM,KAAK,MACxB,OAAO,IAAS,IAAA,IAAa,EAAK,WAAW,GAAG,EAAI,WAAa,MACnE,CAAC,EAOK,0BAAoC,CACpC,GAAW,eAAe,OAAO,EAAU,gBAAgB,CACjE,EAEM,sBAA0B,EAAA,IAAI;6DACuB,CAAC,QAAQ,EAAE;;;;2DAIb,CAAC,OAAO,EAAE;;;MAKnE,MAAO,GAAA,IAAI;YAEH,OAAO,EACF,EAAA,IAAI;;;;sBAIC,EAAM,KAAK;qBACZ,EAAa;wBACV,EAAM,OAAO;gCACH,EAAM,UAAU,OAAS,KAAK;8BAClC,EAAY;uBACnB,sBAAsB;gBAC7B,kBAAkB,EAAE;;YAKxB,EAAM,SAAS,MACV,EAAA,IAAI;;;;;gCAKa,EAAM,UAAU,OAAS,KAAK;qCAC3B,kBAAkB,EAAE;YAI1C,EAAA,IAAI;;;;;8BAKa,EAAM,UAAU,OAAS,KAAK;qBACzC,sBAAsB,GAAG,kBAAkB,EAAE;UAE1D;KAEN,EACA,OAAQ,CAAC,EAAA,mBAAoB,EAAA,OAAY,CAC3C,CAAC"}
|
|
1
|
+
{"version":3,"file":"navbar.cjs","names":[],"sources":["../src/layout/navbar/navbar.ts"],"sourcesContent":["import {\n bind,\n createContext,\n define,\n getHost,\n html,\n inject,\n onCleanup,\n onMounted,\n prop,\n provide,\n useEmit,\n useSlots,\n} from '@vielzeug/ore';\nimport { computed, fromSubscribable, type Readable, signal, untrack, watch } from '@vielzeug/ripple';\nimport { createElementSize, createMediaQuery, SentinelUnavailableError } from '@vielzeug/sentinel';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n elevationMixin,\n frostVariantMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n} from '../../styles';\nimport {\n ELEVATION_LEVELS,\n type ElevationLevel,\n ROUNDED_SIZES,\n type RoundedSize,\n THEME_COLORS,\n type ThemeColor,\n} from '../../types';\nimport { computeSafeRel } from '../../utils';\nimport { parseMaxWidthPx, readContainerWidth, resolveContainerElement } from '../shared';\nimport navbarStyles from './navbar.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\n\nconst NAVBAR_VARIANTS = ['flat', 'solid', 'bordered', 'outline', 'frost'] as const;\ntype NavbarVariant = (typeof NAVBAR_VARIANTS)[number];\n\nconst listen = <E extends Event = Event>(\n el: EventTarget | null | undefined,\n name: string,\n handler: (e: E) => void,\n options?: AddEventListenerOptions,\n): (() => void) => {\n if (!el) return () => {};\n\n el.addEventListener(name, handler as EventListener, options);\n\n return () => el.removeEventListener(name, handler as EventListener, options);\n};\n\ntype NavbarMode = 'floating' | 'sticky';\n\nconst hasElementContent = (el: Element): boolean => {\n if (!(el instanceof HTMLElement)) return true;\n\n if (el.tagName.includes('-')) return true;\n\n if (el.childElementCount > 0) return true;\n\n return (el.textContent ?? '').trim().length > 0;\n};\n\nconst findScrollContainer = (start: HTMLElement): HTMLElement | null => {\n let current: HTMLElement | null = start.parentElement;\n\n while (current) {\n const styles = window.getComputedStyle(current);\n const overflowY = styles.overflowY;\n\n if (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay') {\n return current;\n }\n\n current = current.parentElement;\n }\n\n return null;\n};\n\n/** Context provided by `ore-navbar` to `ore-navbar-item` children. */\nexport type NavbarContext = {\n closeMobileMenu: () => void;\n isMobile: Readable<boolean>;\n mobileMenuOpen: Readable<boolean>;\n};\n\n/** Injection key for navbar context. */\nexport const NAVBAR_CTX = createContext<NavbarContext>('NavbarContext');\n\n/** Navbar component events */\nexport type OreNavbarEvents = {\n 'mobile-menu-change': { open: boolean };\n};\n\n/** Navbar component element interface */\nexport type NavbarElement = HTMLElement &\n OreNavbarProps & {\n closeMobileMenu(): void;\n openMobileMenu(): void;\n toggleMobileMenu(): void;\n };\n\ntype MobileSidebarElement = HTMLElement & {\n closeDrawer?: () => void;\n openDrawer?: () => void;\n toggleDrawer?: () => void;\n};\n\n/** Navbar component properties */\nexport type OreNavbarProps = {\n /** CSS media query used for mobile layout switching */\n breakpoint?: string;\n /** Theme color */\n color?: ThemeColor;\n /** Evaluate mobile breakpoint against container width only when possible. */\n 'container-breakpoints'?: boolean;\n /** Shadow elevation level (0-5) */\n elevation?: ElevationLevel;\n /** Start as detached floating navbar */\n floating?: boolean;\n /** Accessible nav landmark label */\n label?: string;\n /** Icon shown while the mobile menu is open */\n 'menu-close-icon'?: string;\n 'menu-close-label'?: string;\n /** Icon shown while the mobile menu is closed */\n 'menu-icon'?: string;\n 'menu-open-label'?: string;\n /** CSS selector for an external sidebar toggled by the mobile button */\n 'mobile-sidebar'?: string;\n /** Border radius size */\n rounded?: RoundedSize;\n /** Scroll threshold in px used by floating+sticky transition */\n 'scroll-threshold'?: number;\n /** Stick to top of viewport */\n sticky?: boolean;\n /** Visual style variant */\n variant?: NavbarVariant;\n};\n\n/** Navbar item properties */\nexport type OreNavbarItemProps = {\n /** Accessible name for icon-only items, forwarded to the internal button or anchor */\n ariaLabel?: string;\n /** Whether this item represents the current page */\n active?: boolean;\n /** Whether this item is disabled */\n disabled?: boolean;\n /** Link target URL. Renders an anchor when provided. */\n href?: string;\n /**\n * Icon-only mode: hides the (empty) label region and switches to compact, symmetric\n * padding instead of the icon+label layout's wider horizontal padding. Set this whenever\n * the default slot is intentionally left empty (name conveyed via `aria-label` instead),\n * otherwise the empty label still reserves its inter-element gap and the item ends up\n * with lopsided padding around the icon.\n */\n 'icon-only'?: boolean;\n /** Relationship of linked URL (anchor-only) */\n rel?: string;\n /** Browsing context for link (anchor-only) */\n target?: string;\n};\n\n/**\n * `ore-navbar`: Responsive navigation with sticky/floating modes and a mobile overflow panel.\n *\n * @element ore-navbar\n * @element ore-navbar-item - Navigation link or button placed in the default or mobile-menu slot\n *\n * @attr {string} label - Accessible nav landmark label\n * @attr {string} menu-icon - Icon shown while the mobile menu is closed\n * @attr {string} menu-close-icon - Icon shown while the mobile menu is open\n * @attr {boolean} sticky - Makes navbar sticky at top\n * @attr {boolean} floating - Makes navbar detached and floating\n * @attr {number} scroll-threshold - Scroll threshold for floating+sticky transition\n * @attr {string} breakpoint - CSS media query used for mobile mode\n * @attr {boolean} container-breakpoints - Evaluates parseable max-width breakpoints against container width\n * @attr {string} variant - Visual variant: 'flat' | 'solid' | 'bordered' | 'outline' | 'frost'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} elevation - Shadow elevation: '0' | '1' | '2' | '3' | '4' | '5'\n *\n * @fires mobile-menu-change - Emitted when mobile menu open state changes. detail: { open: boolean }\n *\n * @slot logo - Brand/logo content rendered at the leading edge\n * @slot start - Content rendered in the left/start region\n * @slot - Center navigation content rendered between the start and end regions\n * @slot end - Content rendered in the right/end region\n * @slot persistent - Trailing content that stays in the bar through the mobile collapse (global actions and status icons)\n * @slot mobile-menu - Controls and links rendered inside the mobile overflow panel\n *\n * @cssprop --navbar-height - Navbar bar height\n * @cssprop --navbar-width - Max width of the navbar content\n * @cssprop --navbar-offset - Horizontal offset for floating/sticky positioning\n * @cssprop --navbar-bg - Navbar background color\n * @cssprop --navbar-color - Navbar text color\n * @cssprop --navbar-border-color - Navbar border color\n * @cssprop --navbar-shadow - Navbar box shadow\n * @cssprop --navbar-radius - Border radius (floating/sticky modes)\n * @cssprop --navbar-backdrop-filter - Backdrop filter for blur effect\n * @cssprop --navbar-item-color - Nav item text color\n * @cssprop --navbar-item-hover-color - Nav item hover text color\n * @cssprop --navbar-item-hover-bg - Nav item hover background\n * @cssprop --navbar-item-active-color - Nav item active/current text color\n * @cssprop --navbar-item-active-bg - Nav item active/current background\n * @part nav - Outer navigation landmark\n * @part bar - Main navbar row container\n * @part logo - Logo slot container\n * @part start - Leading action/content region\n * @part center - Center content region\n * @part end - Trailing action/content region\n * @part persistent - Trailing region that survives the mobile collapse\n * @part mobile-toggle - Mobile menu toggle button\n * @part mobile-menu - Mobile overflow panel\n * @part item-icon - Leading icon inside navbar items\n * @part item-label - Label text inside navbar items\n * @part item-end - Trailing content inside navbar items\n * @part item - Clickable navbar item root\n * @example\n * ```html\n * <ore-navbar sticky elevation=\"1\">\n * <a href=\"/\" slot=\"logo\"><strong>MyApp</strong></a>\n * <ore-navbar-item href=\"/dashboard\">Dashboard</ore-navbar-item>\n * <ore-navbar-item href=\"/docs\">Docs</ore-navbar-item>\n * <ore-button slot=\"end\" variant=\"solid\" color=\"primary\" size=\"sm\">Sign in</ore-button>\n * </ore-navbar>\n * ```\n */\nexport const NAVBAR_TAG = 'ore-navbar' as const;\ndefine<OreNavbarProps>(NAVBAR_TAG, {\n props: {\n breakpoint: prop.string('(max-width: 768px)'),\n color: prop.oneOf(THEME_COLORS),\n 'container-breakpoints': prop.bool(false),\n elevation: prop.oneOf(ELEVATION_LEVELS),\n floating: prop.bool(false),\n label: prop.string('Main navigation'),\n 'menu-close-icon': prop.string('x'),\n 'menu-close-label': prop.string('Close navigation menu'),\n 'menu-icon': prop.string('menu'),\n 'menu-open-label': prop.string('Open navigation menu'),\n 'mobile-sidebar': prop.string(),\n rounded: prop.oneOf(ROUNDED_SIZES),\n 'scroll-threshold': prop.number(80),\n sticky: prop.bool(false),\n variant: prop.oneOf(NAVBAR_VARIANTS),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreNavbarEvents>();\n const slots = useSlots();\n\n const hasLogo = slots.has('logo');\n const hasStart = slots.has('start');\n const hasCenter = slots.has();\n const hasEnd = slots.has('end');\n const hasPersistent = slots.has('persistent');\n const mobileMenuElements = slots.elements('mobile-menu');\n const hasMobileMenu = signal(false);\n const mobileSidebarTarget = signal<MobileSidebarElement | null>(null);\n const isExternalMobileMode = signal(false);\n const isExternalMobileOpen = signal(false);\n const isMobile = signal(false);\n const isMobileMenuOpen = signal(false);\n const isModeTransitioning = signal(false);\n const isScrolled = signal(false);\n const scrollBase = signal(0);\n const mediaMatches = signal(false);\n const sizeMatches = signal(false);\n const maxWidthPx = signal<number | undefined>(parseMaxWidthPx(props.breakpoint.value));\n const isPreviewMode = signal(false);\n\n const getExternalSidebar = () => {\n const selector = String(props['mobile-sidebar'].value ?? '').trim();\n\n if (!selector) return null;\n\n const root = el.getRootNode();\n const scopedTarget =\n root instanceof ShadowRoot || root instanceof Document\n ? (root.querySelector(selector) as MobileSidebarElement | null)\n : null;\n\n return scopedTarget ?? (document.querySelector(selector) as MobileSidebarElement | null);\n };\n\n provide(NAVBAR_CTX, {\n // Wrapped, not passed directly: `closeMobileMenu` (below) isn't defined yet at this point\n // in `setup()`; by the time `ore-navbar-item` actually calls this (a later click), it is.\n closeMobileMenu: () => closeMobileMenu(),\n isMobile: computed(() => isMobile.value) as Readable<boolean>,\n mobileMenuOpen: computed(() => isMobileMenuOpen.value) as Readable<boolean>,\n });\n\n const computeMode = (): NavbarMode | undefined => {\n if (props.floating.value && props.sticky.value) {\n return isScrolled.value ? 'sticky' : 'floating';\n }\n\n if (props.floating.value) return 'floating';\n\n if (props.sticky.value) return 'sticky';\n\n return undefined;\n };\n\n const setMobileMenu = (next: boolean) => {\n const open = Boolean(next);\n\n if (!hasMobileMenu.value) {\n const target = mobileSidebarTarget.value ?? getExternalSidebar();\n\n if (target) {\n if (open) {\n target.openDrawer?.();\n } else {\n target.closeDrawer?.();\n }\n\n return;\n }\n }\n\n if (open && !hasMobileMenu.value) return;\n\n if (isMobileMenuOpen.value === open) return;\n\n isMobileMenuOpen.value = open;\n emit('mobile-menu-change', { open });\n };\n\n const closeMobileMenu = () => setMobileMenu(false);\n const openMobileMenu = () => setMobileMenu(true);\n const toggleMobileMenu = () => {\n if (!hasMobileMenu.value) {\n const target = mobileSidebarTarget.value ?? getExternalSidebar();\n\n if (target) {\n if (typeof target.toggleDrawer === 'function') {\n target.toggleDrawer();\n } else if (typeof target.openDrawer === 'function' && typeof target.closeDrawer === 'function') {\n const isOpen = target.hasAttribute('data-drawer-open');\n\n if (isOpen) target.closeDrawer();\n else target.openDrawer();\n }\n\n return;\n }\n }\n\n setMobileMenu(!isMobileMenuOpen.value);\n };\n\n const syncMobileMode = () => {\n const next =\n (mobileSidebarTarget.value && !hasMobileMenu.value ? isExternalMobileMode.value : false) ||\n mediaMatches.value ||\n (Boolean(props['container-breakpoints'].value) && sizeMatches.value);\n\n isMobile.value = next;\n\n if (!next) closeMobileMenu();\n };\n\n const readScrollOffset = (scrollContainer: HTMLElement | null): number => {\n if (scrollContainer) {\n return scrollContainer.scrollTop || 0;\n }\n\n return window.scrollY || document.documentElement.scrollTop || 0;\n };\n\n const resetScrollBase = (scrollContainer: HTMLElement | null) => {\n scrollBase.value = readScrollOffset(scrollContainer);\n };\n\n const updateScrolled = (scrollContainer: HTMLElement | null = null) => {\n if (!(props.floating.value && props.sticky.value)) {\n if (isScrolled.value) isScrolled.value = false;\n\n return;\n }\n\n const threshold = Math.max(0, Number(props['scroll-threshold'].value) || 0);\n const currentOffset = readScrollOffset(scrollContainer);\n const delta = currentOffset - scrollBase.value;\n\n isScrolled.value = delta > threshold;\n };\n\n bind({\n attr: {\n 'data-mobile': () => (isMobile.value ? true : undefined),\n 'data-mobile-open': () => (isMobile.value && isMobileMenuOpen.value ? true : undefined),\n 'data-mode': () => computeMode() ?? null,\n 'data-mode-transitioning': () => (isModeTransitioning.value ? true : undefined),\n 'data-preview-mode': () => (isPreviewMode.value ? true : undefined),\n 'data-scrolled': () => (props.floating.value && props.sticky.value && isScrolled.value ? true : undefined),\n },\n });\n\n onMounted(() => {\n const navbarEl = el as NavbarElement;\n const scrollContainer = findScrollContainer(el);\n\n navbarEl.closeMobileMenu = closeMobileMenu;\n navbarEl.openMobileMenu = openMobileMenu;\n navbarEl.toggleMobileMenu = toggleMobileMenu;\n\n const stopScroll = listen(\n window,\n 'scroll',\n () => {\n updateScrolled(scrollContainer);\n },\n { passive: true },\n );\n const stopContainerScroll = scrollContainer\n ? listen(\n scrollContainer,\n 'scroll',\n () => {\n updateScrolled(scrollContainer);\n },\n { passive: true },\n )\n : undefined;\n const stopEscape = listen(el, 'keydown', (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || !isMobileMenuOpen.value) return;\n\n event.preventDefault();\n event.stopPropagation();\n closeMobileMenu();\n el.shadowRoot?.querySelector<HTMLButtonElement>('[part=\"mobile-toggle\"]')?.focus();\n });\n\n let mediaCleanup: (() => void) | undefined;\n let mobileSidebarCleanup: (() => void) | undefined;\n let modeTransitionTimeout: ReturnType<typeof setTimeout> | undefined;\n let previousMode: NavbarMode | undefined;\n\n const resolveMobileSidebarTarget = () => {\n mobileSidebarCleanup?.();\n mobileSidebarCleanup = undefined;\n mobileSidebarTarget.value = null;\n isExternalMobileMode.value = false;\n isExternalMobileOpen.value = false;\n\n if (!String(props['mobile-sidebar'].value ?? '').trim()) return;\n\n const target = getExternalSidebar();\n\n if (!target) {\n const root = el.getRootNode();\n const discoveryRoot = root instanceof Document || root instanceof ShadowRoot ? root : document;\n const discoveryObserver = new MutationObserver(() => {\n if (getExternalSidebar()) resolveMobileSidebarTarget();\n });\n\n discoveryObserver.observe(discoveryRoot, { childList: true, subtree: true });\n mobileSidebarCleanup = () => discoveryObserver.disconnect();\n\n return;\n }\n\n const syncTargetState = (event?: CustomEvent<{ open: boolean }>) => {\n const hasBottomNav = target.hasAttribute('data-bottom-nav');\n const hasDrawerOpen = target.hasAttribute('data-drawer-open');\n\n isExternalMobileMode.value = hasBottomNav;\n isExternalMobileOpen.value = event?.detail ? event.detail.open : hasDrawerOpen;\n\n // If the sidebar is reporting it's open, it must be in bottom-nav mode\n // (either natural or forced). We honor this to avoid race conditions\n // where attributes haven't reflected yet.\n if (isExternalMobileOpen.value && !isExternalMobileMode.value) {\n isExternalMobileMode.value = true;\n }\n\n syncMobileMode();\n };\n\n const targetObserver = new MutationObserver(() => {\n syncTargetState();\n });\n\n syncTargetState();\n targetObserver.observe(target, {\n attributeFilter: ['data-bottom-nav', 'data-drawer-open'],\n attributes: true,\n });\n target.addEventListener('drawer-change', syncTargetState as EventListener);\n mobileSidebarCleanup = () => {\n targetObserver.disconnect();\n target.removeEventListener('drawer-change', syncTargetState as EventListener);\n };\n mobileSidebarTarget.value = target;\n };\n\n const triggerModeTransition = () => {\n isModeTransitioning.value = true;\n\n if (modeTransitionTimeout != null) clearTimeout(modeTransitionTimeout);\n\n modeTransitionTimeout = setTimeout(() => {\n isModeTransitioning.value = false;\n modeTransitionTimeout = undefined;\n }, 220);\n };\n\n const stopModeWatch = watch(\n computed(() => props.floating.value && props.sticky.value),\n (enabled) => {\n if (!enabled) {\n if (isScrolled.value) isScrolled.value = false;\n\n if (isModeTransitioning.value) isModeTransitioning.value = false;\n\n previousMode = undefined;\n\n if (modeTransitionTimeout != null) {\n clearTimeout(modeTransitionTimeout);\n modeTransitionTimeout = undefined;\n }\n\n return;\n }\n\n resetScrollBase(scrollContainer);\n updateScrolled(scrollContainer);\n\n const currentMode = computeMode();\n\n previousMode = currentMode;\n },\n { immediate: true },\n );\n\n const stopMobileSidebarWatch = watch(\n props['mobile-sidebar'],\n () => {\n resolveMobileSidebarTarget();\n },\n { immediate: true },\n );\n\n const stopModeTransitionWatch = watch(\n computed(() => computeMode()),\n (nextMode) => {\n const comboEnabled = props.floating.value && props.sticky.value;\n\n if (!comboEnabled) {\n previousMode = nextMode ?? undefined;\n\n return;\n }\n\n const next = nextMode ?? undefined;\n\n if (previousMode && next && previousMode !== next) {\n triggerModeTransition();\n }\n\n previousMode = next;\n },\n { immediate: true },\n );\n\n const stopMobileMenuSlotWatch = watch(\n hasMobileMenu,\n (hasContent) => {\n if (!hasContent && isMobileMenuOpen.value) {\n closeMobileMenu();\n }\n },\n { immediate: true },\n );\n const syncMobileMenuContent = () => {\n hasMobileMenu.value = mobileMenuElements.value.some(hasElementContent);\n };\n const stopMobileMenuElementsWatch = watch(mobileMenuElements, syncMobileMenuContent, { immediate: true });\n const mobileMenuContentObserver = new MutationObserver(() => {\n syncMobileMenuContent();\n });\n\n // Parser-created demo content can populate an already-assigned slot wrapper after this\n // custom element connects; `slotchange` alone does not observe that nested content.\n mobileMenuContentObserver.observe(el, { childList: true, subtree: true });\n\n watch(\n props.breakpoint,\n (query) => {\n // Untrack the callback body: the watch already tracks `props.breakpoint` via\n // its internal `read()`. Without untrack, signals read inside (e.g.\n // `mediaMatches` via `syncMobileMode()`) become dependencies of this watch\n // effect. When the sentinel updates `mediaMatches` on a viewport change, the\n // effect re-runs: `EffectNode.run()` disposes the previous scope (killing the\n // mediaHandle and its MediaQueryList listener) but the callback is NOT invoked\n // (source unchanged), so no new handle is created. The navbar is then deaf to\n // further MQ changes and stays stuck in mobile mode after resizing back to\n // desktop.\n untrack(() => {\n mediaCleanup?.();\n mediaCleanup = undefined;\n\n const mediaQuery = String(query ?? '').trim();\n\n maxWidthPx.value = parseMaxWidthPx(mediaQuery);\n mediaMatches.value = false;\n\n const width = readContainerWidth(el);\n\n sizeMatches.value = width > 0 && maxWidthPx.value != null ? width <= maxWidthPx.value : false;\n syncMobileMode();\n\n if (props['container-breakpoints'].value && maxWidthPx.value != null) {\n return;\n }\n\n if (!mediaQuery) {\n return;\n }\n\n try {\n const mediaHandle = createMediaQuery(mediaQuery);\n const syncMedia = (state: ReturnType<typeof mediaHandle.getSnapshot>) => {\n if (state) {\n mediaMatches.value = state.matches;\n syncMobileMode();\n }\n };\n\n syncMedia(mediaHandle.getSnapshot());\n\n const mediaCleanupFn = watch(\n fromSubscribable(mediaHandle, { signal: mediaHandle.disposalSignal }),\n syncMedia,\n );\n mediaCleanup = () => {\n mediaCleanupFn.dispose();\n mediaHandle.dispose();\n };\n } catch (error) {\n if (!(error instanceof SentinelUnavailableError)) throw error;\n }\n });\n },\n { immediate: true },\n );\n\n const stopResizeEffect =\n typeof ResizeObserver === 'function'\n ? (() => {\n const hostSize = createElementSize(el);\n onCleanup(() => hostSize.dispose());\n const hostSizeSignal = fromSubscribable(hostSize, { signal: hostSize.disposalSignal });\n\n const wrapperEl = el.parentElement;\n const containerEl = resolveContainerElement(el);\n const wrapperSize = wrapperEl ? createElementSize(wrapperEl) : undefined;\n const parentSize = containerEl && containerEl !== wrapperEl ? createElementSize(containerEl) : undefined;\n\n if (wrapperSize) {\n onCleanup(() => wrapperSize.dispose());\n }\n if (parentSize) {\n onCleanup(() => parentSize.dispose());\n }\n\n const wrapperSizeSignal = wrapperSize\n ? fromSubscribable(wrapperSize, { signal: wrapperSize.disposalSignal })\n : undefined;\n const parentSizeSignal = parentSize\n ? fromSubscribable(parentSize, { signal: parentSize.disposalSignal })\n : undefined;\n\n return watch(\n computed(() => [\n hostSizeSignal.value?.width,\n hostSizeSignal.value?.height,\n wrapperSizeSignal?.value?.width,\n wrapperSizeSignal?.value?.height,\n parentSizeSignal?.value?.width,\n parentSizeSignal?.value?.height,\n ]),\n () => {\n const width = readContainerWidth(el);\n const parentWidth = resolveContainerElement(el)?.clientWidth ?? 0;\n\n sizeMatches.value = width > 0 && maxWidthPx.value != null ? width <= maxWidthPx.value : false;\n isPreviewMode.value = parentWidth > 0 && parentWidth < window.innerWidth;\n syncMobileMode();\n },\n );\n })()\n : undefined;\n\n return () => {\n stopEscape?.();\n stopScroll?.();\n stopContainerScroll?.();\n stopModeWatch?.dispose();\n stopModeTransitionWatch?.dispose();\n stopMobileMenuSlotWatch?.dispose();\n stopMobileMenuElementsWatch?.dispose();\n stopMobileSidebarWatch?.dispose();\n mobileMenuContentObserver.disconnect();\n mediaCleanup?.();\n mobileSidebarCleanup?.();\n stopResizeEffect?.dispose();\n\n if (modeTransitionTimeout != null) {\n clearTimeout(modeTransitionTimeout);\n modeTransitionTimeout = undefined;\n }\n };\n });\n\n return html`\n <nav part=\"nav\" aria-label=\"${props.label}\">\n <div class=\"navbar\" part=\"bar\">\n <div class=\"navbar-logo\" part=\"logo\" ?hidden=${() => !hasLogo.value}>\n <slot name=\"logo\"></slot>\n </div>\n\n <div class=\"navbar-start\" part=\"start\" ?hidden=${() => !hasStart.value}>\n <slot name=\"start\"></slot>\n </div>\n\n <div class=\"navbar-center\" part=\"center\" ?hidden=${() => !hasCenter.value}>\n <slot></slot>\n </div>\n\n <div class=\"navbar-end\" part=\"end\" ?hidden=${() => !hasEnd.value}>\n <slot name=\"end\"></slot>\n </div>\n\n <div class=\"navbar-persistent\" part=\"persistent\" ?hidden=${() => !hasPersistent.value}>\n <slot name=\"persistent\"></slot>\n </div>\n\n <button\n class=\"mobile-toggle\"\n part=\"mobile-toggle\"\n type=\"button\"\n aria-label=\"${() =>\n (hasMobileMenu.value ? isMobileMenuOpen.value : isExternalMobileOpen.value)\n ? props['menu-close-label'].value\n : props['menu-open-label'].value}\"\n aria-controls=\"mobile-menu-panel\"\n aria-expanded=\"${() => String(hasMobileMenu.value ? isMobileMenuOpen.value : isExternalMobileOpen.value)}\"\n ?hidden=${() => !isMobile.value || (!hasMobileMenu.value && !mobileSidebarTarget.value)}\n @click=\"${toggleMobileMenu}\">\n <ore-icon\n name=\"${() =>\n (hasMobileMenu.value ? isMobileMenuOpen.value : isExternalMobileOpen.value)\n ? props['menu-close-icon'].value\n : props['menu-icon'].value}\"\n size=\"18\"\n stroke-width=\"2.5\"\n aria-hidden=\"true\"></ore-icon>\n </button>\n </div>\n\n <div\n id=\"mobile-menu-panel\"\n class=\"mobile-menu-panel\"\n part=\"mobile-menu\"\n role=\"navigation\"\n aria-label=\"${() => `${props.label.value} mobile menu`}\"\n ?hidden=${() => !isMobile.value || !isMobileMenuOpen.value || !hasMobileMenu.value}>\n <slot name=\"mobile-menu\"></slot>\n </div>\n </nav>\n `;\n },\n styles: [\n colorThemeMixin,\n elevationMixin,\n roundedVariantMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n frostVariantMixin('.navbar'),\n navbarStyles,\n ],\n});\n\n/**\n * `ore-navbar-item`: Navigation item for navbar slots.\n *\n * Renders as an anchor when `href` is provided and item is not disabled.\n *\n * @element ore-navbar-item\n *\n * @attr {string} aria-label - Accessible name for icon-only items, forwarded to the internal control\n * @attr {boolean} active - Marks item as the current active route\n * @attr {boolean} disabled - Disables interaction\n * @attr {string} href - Link URL; renders as `<a>` when set\n * @attr {boolean} icon-only - Compact, symmetric padding for items with no visible label\n * @attr {string} rel - Anchor `rel` attribute (links only)\n * @attr {string} target - Anchor `target` attribute (links only)\n *\n * @slot - Item label\n * @slot icon - Optional icon before the label\n *\n * @part item - Root clickable element (anchor or button).\n * @part item-icon - Leading icon container.\n * @part item-label - Label text container.\n * @part item-end - Trailing content container.\n *\n * @example\n * ```html\n * <ore-navbar-item href=\"/home\">Home</ore-navbar-item>\n * <ore-navbar-item href=\"/docs\" active>Docs</ore-navbar-item>\n * <ore-navbar-item href=\"/about\" target=\"_blank\" rel=\"noopener\">About</ore-navbar-item>\n * <ore-navbar-item disabled>Disabled</ore-navbar-item>\n * <ore-navbar-item icon-only aria-label=\"Search\"><ore-icon slot=\"icon\" name=\"search\"></ore-icon></ore-navbar-item>\n * ```\n */\nexport const NAVBAR_ITEM_TAG = 'ore-navbar-item' as const;\ndefine<OreNavbarItemProps>(NAVBAR_ITEM_TAG, {\n props: {\n active: prop.bool(false),\n ariaLabel: prop.string(),\n disabled: prop.bool(false),\n href: prop.string(),\n 'icon-only': prop.bool(false),\n rel: prop.string(),\n target: prop.string(),\n },\n setup(props) {\n const slots = useSlots();\n\n const hasIcon = () => slots.has('icon').value;\n const hasEnd = () => slots.has('end').value;\n const navbarCtx = inject(NAVBAR_CTX);\n\n bind({\n attr: {\n 'navbar-mobile': () => (navbarCtx?.isMobile.value ? true : undefined),\n 'navbar-mobile-open': () => (navbarCtx?.mobileMenuOpen.value ? true : undefined),\n },\n });\n\n const isLink = () => Boolean(props.href.value) && !props.disabled.value;\n\n // Prevent reverse tabnapping: auto-inject noopener + noreferrer for _blank links.\n const effectiveRel = computed(() => computeSafeRel(props.rel.value, props.target.value));\n\n // The `aria-current` token: `location` for in-page anchors (the current position\n // within this document), `page` for actual page routes. Announcing a section\n // anchor as `page` would claim the section is the current document.\n const ariaCurrent = computed<null | 'location' | 'page'>(() => {\n if (!props.active.value) return null;\n const href = props.href.value;\n return href !== undefined && href.startsWith('#') ? 'location' : 'page';\n });\n\n // Closes the mobile overflow panel when one of its items is activated, otherwise picking a\n // destination (or firing any other action placed in the `mobile-menu` slot) leaves the panel\n // open over the page until the user separately taps the toggle again. Only matters while\n // actually in mobile mode; on desktop `navbarCtx.mobileMenuOpen` is never true to begin with,\n // so `closeMobileMenu()` (idempotent: see `setMobileMenu`'s equality check) is a no-op there.\n const closeMobileMenuIfOpen = (): void => {\n if (navbarCtx?.mobileMenuOpen.value) navbarCtx.closeMobileMenu();\n };\n\n const renderItemContent = () => html`\n <span class=\"item-icon\" part=\"item-icon\" ?hidden=${() => !hasIcon()} aria-hidden=\"true\">\n <slot name=\"icon\"></slot>\n </span>\n <span class=\"item-label\" part=\"item-label\"><slot></slot></span>\n <span class=\"item-end\" part=\"item-end\" ?hidden=${() => !hasEnd()}>\n <slot name=\"end\"></slot>\n </span>\n `;\n\n return html`\n ${() => {\n if (isLink()) {\n return html`\n <a\n class=\"item\"\n part=\"item\"\n href=\"${props.href}\"\n rel=\"${effectiveRel}\"\n target=\"${props.target}\"\n aria-label=\"${() => props.ariaLabel.value ?? null}\"\n aria-current=\"${ariaCurrent}\"\n @click=${closeMobileMenuIfOpen}>\n ${renderItemContent()}\n </a>\n `;\n }\n\n if (props.disabled.value) {\n return html`\n <div\n class=\"item\"\n part=\"item\"\n tabindex=\"-1\"\n aria-label=\"${() => props.ariaLabel.value ?? null}\"\n aria-disabled=\"true\">${renderItemContent()}</div>\n `;\n }\n\n return html`\n <button\n class=\"item\"\n part=\"item\"\n type=\"button\"\n aria-label=\"${() => props.ariaLabel.value ?? null}\"\n @click=${closeMobileMenuIfOpen}>${renderItemContent()}</button>\n `;\n }}\n `;\n },\n styles: [coarsePointerMixin, navbarStyles],\n});\n"],"mappings":"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"}
|
package/dist/navbar.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{coarsePointerMixin as e,reducedMotionMixin as t}from"./styles/mixins/accessibility.css.js";import{roundedVariantMixin as n}from"./styles/mixins/shape.css.js";import{colorThemeMixin as r,elevationMixin as i}from"./styles/mixins/theme.css.js";import{frostVariantMixin as a}from"./styles/mixins/variants.css.js";import{ELEVATION_LEVELS as o,ROUNDED_SIZES as s,THEME_COLORS as c}from"./shared/types.js";import{ICON_TAG as l}from"./icon.js";import{computeSafeRel as u}from"./utils/link.js";import{parseMaxWidthPx as d,readContainerWidth as f,resolveContainerElement as p}from"./layout/shared.js";import m from"./layout/navbar/navbar.css.js";import{computed as h,fromSubscribable as g,signal as _,untrack as v,watch as y}from"@vielzeug/ripple";import{SentinelUnavailableError as b,createElementSize as x,createMediaQuery as S}from"@vielzeug/sentinel";import{bind as C,createContext as w,define as T,getHost as E,html as D,inject as O,onCleanup as k,onMounted as A,prop as j,provide as M,useEmit as N,useSlots as P}from"@vielzeug/ore";var F=[`flat`,`solid`,`bordered`,`outline`,`frost`],listen=(e,t,n,r)=>e?(e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)):()=>{},hasElementContent=e=>!(e instanceof HTMLElement)||e.tagName.includes(`-`)||e.childElementCount>0?!0:(e.textContent??``).trim().length>0,findScrollContainer=e=>{let t=e.parentElement;for(;t;){let e=window.getComputedStyle(t).overflowY;if(e===`auto`||e===`scroll`||e===`overlay`)return t;t=t.parentElement}return null},I=w(`NavbarContext`),L=`ore-navbar`;T(L,{props:{breakpoint:j.string(`(max-width: 768px)`),color:j.oneOf(c),"container-breakpoints":j.bool(!1),elevation:j.oneOf(o),floating:j.bool(!1),label:j.string(`Main navigation`),"menu-close-icon":j.string(`x`),"menu-close-label":j.string(`Close navigation menu`),"menu-icon":j.string(`menu`),"menu-open-label":j.string(`Open navigation menu`),"mobile-sidebar":j.string(),rounded:j.oneOf(s),"scroll-threshold":j.number(80),sticky:j.bool(!1),variant:j.oneOf(F)},setup(e){let t=E(),n=N(),r=P(),i=r.has(`logo`),a=r.has(`start`),o=r.has(),s=r.has(`end`),c=r.has(`persistent`),l=r.elements(`mobile-menu`),u=_(!1),m=_(null),w=_(!1),T=_(!1),O=_(!1),j=_(!1),F=_(!1),L=_(!1),R=_(0),z=_(!1),B=_(!1),V=_(d(e.breakpoint.value)),H=_(!1),getExternalSidebar=()=>{let n=String(e[`mobile-sidebar`].value??``).trim();if(!n)return null;let r=t.getRootNode();return(r instanceof ShadowRoot||r instanceof Document?r.querySelector(n):null)??document.querySelector(n)};M(I,{closeMobileMenu:()=>closeMobileMenu(),isMobile:h(()=>O.value),mobileMenuOpen:h(()=>j.value)});let computeMode=()=>{if(e.floating.value&&e.sticky.value)return L.value?`sticky`:`floating`;if(e.floating.value)return`floating`;if(e.sticky.value)return`sticky`},setMobileMenu=e=>{let t=!!e;if(!u.value){let e=m.value??getExternalSidebar();if(e){t?e.openDrawer?.():e.closeDrawer?.();return}}(!t||u.value)&&j.value!==t&&(j.value=t,n(`mobile-menu-change`,{open:t}))},closeMobileMenu=()=>setMobileMenu(!1),openMobileMenu=()=>setMobileMenu(!0),toggleMobileMenu=()=>{if(!u.value){let e=m.value??getExternalSidebar();if(e){typeof e.toggleDrawer==`function`?e.toggleDrawer():typeof e.openDrawer==`function`&&typeof e.closeDrawer==`function`&&(e.hasAttribute(`data-drawer-open`)?e.closeDrawer():e.openDrawer());return}}setMobileMenu(!j.value)},syncMobileMode=()=>{let t=(m.value&&!u.value?w.value:!1)||z.value||!!e[`container-breakpoints`].value&&B.value;O.value=t,t||closeMobileMenu()},readScrollOffset=e=>e?e.scrollTop||0:window.scrollY||document.documentElement.scrollTop||0,resetScrollBase=e=>{R.value=readScrollOffset(e)},updateScrolled=(t=null)=>{if(!(e.floating.value&&e.sticky.value)){L.value&&=!1;return}let n=Math.max(0,Number(e[`scroll-threshold`].value)||0),r=readScrollOffset(t)-R.value;L.value=r>n};return C({attr:{"data-mobile":()=>O.value?!0:void 0,"data-mobile-open":()=>O.value&&j.value?!0:void 0,"data-mode":()=>computeMode()??null,"data-mode-transitioning":()=>F.value?!0:void 0,"data-preview-mode":()=>H.value?!0:void 0,"data-scrolled":()=>e.floating.value&&e.sticky.value&&L.value?!0:void 0}}),A(()=>{let n=t,r=findScrollContainer(t);n.closeMobileMenu=closeMobileMenu,n.openMobileMenu=openMobileMenu,n.toggleMobileMenu=toggleMobileMenu;let i=listen(window,`scroll`,()=>{updateScrolled(r)},{passive:!0}),a=r?listen(r,`scroll`,()=>{updateScrolled(r)},{passive:!0}):void 0,o=listen(t,`keydown`,e=>{e.key===`Escape`&&j.value&&closeMobileMenu()}),mediaCleanup,mobileSidebarCleanup,s,c,resolveMobileSidebarTarget=()=>{if(mobileSidebarCleanup?.(),mobileSidebarCleanup=void 0,m.value=null,w.value=!1,T.value=!1,!String(e[`mobile-sidebar`].value??``).trim())return;let n=getExternalSidebar();if(!n){let e=t.getRootNode(),n=e instanceof Document||e instanceof ShadowRoot?e:document,r=new MutationObserver(()=>{getExternalSidebar()&&resolveMobileSidebarTarget()});r.observe(n,{childList:!0,subtree:!0}),mobileSidebarCleanup=()=>r.disconnect();return}let syncTargetState=e=>{let t=n.hasAttribute(`data-bottom-nav`),r=n.hasAttribute(`data-drawer-open`);w.value=t,T.value=e?.detail?e.detail.open:r,T.value&&!w.value&&(w.value=!0),syncMobileMode()},r=new MutationObserver(()=>{syncTargetState()});syncTargetState(),r.observe(n,{attributeFilter:[`data-bottom-nav`,`data-drawer-open`],attributes:!0}),n.addEventListener(`drawer-change`,syncTargetState),mobileSidebarCleanup=()=>{r.disconnect(),n.removeEventListener(`drawer-change`,syncTargetState)},m.value=n},triggerModeTransition=()=>{F.value=!0,s!=null&&clearTimeout(s),s=setTimeout(()=>{F.value=!1,s=void 0},220)},_=y(h(()=>e.floating.value&&e.sticky.value),e=>{if(!e){L.value&&=!1,F.value&&=!1,c=void 0,s!=null&&(clearTimeout(s),s=void 0);return}resetScrollBase(r),updateScrolled(r),c=computeMode()},{immediate:!0}),C=y(e[`mobile-sidebar`],()=>{resolveMobileSidebarTarget()},{immediate:!0}),E=y(h(()=>computeMode()),t=>{if(!(e.floating.value&&e.sticky.value)){c=t??void 0;return}let n=t??void 0;c&&n&&c!==n&&triggerModeTransition(),c=n},{immediate:!0}),D=y(u,e=>{!e&&j.value&&closeMobileMenu()},{immediate:!0}),syncMobileMenuContent=()=>{u.value=l.value.some(hasElementContent)},O=y(l,syncMobileMenuContent,{immediate:!0}),A=new MutationObserver(()=>{syncMobileMenuContent()});A.observe(t,{childList:!0,subtree:!0}),y(e.breakpoint,n=>{v(()=>{mediaCleanup?.(),mediaCleanup=void 0;let r=String(n??``).trim();V.value=d(r),z.value=!1;let i=f(t);if(B.value=i>0&&V.value!=null&&i<=V.value,syncMobileMode(),!(e[`container-breakpoints`].value&&V.value!=null)&&r)try{let e=S(r),syncMedia=e=>{e&&(z.value=e.matches,syncMobileMode())};syncMedia(e.getSnapshot());let t=y(g(e,{signal:e.disposalSignal}),syncMedia);mediaCleanup=()=>{t.dispose(),e.dispose()}}catch(e){if(!(e instanceof b))throw e}})},{immediate:!0});let M=typeof ResizeObserver==`function`?(()=>{let e=x(t);k(()=>e.dispose());let n=g(e,{signal:e.disposalSignal}),r=t.parentElement,i=p(t),a=r?x(r):void 0,o=i&&i!==r?x(i):void 0;a&&k(()=>a.dispose()),o&&k(()=>o.dispose());let s=a?g(a,{signal:a.disposalSignal}):void 0,c=o?g(o,{signal:o.disposalSignal}):void 0;return y(h(()=>[n.value?.width,n.value?.height,s?.value?.width,s?.value?.height,c?.value?.width,c?.value?.height]),()=>{let e=f(t),n=p(t)?.clientWidth??0;B.value=e>0&&V.value!=null&&e<=V.value,H.value=n>0&&n<window.innerWidth,syncMobileMode()})})():void 0;return()=>{o?.(),i?.(),a?.(),_?.dispose(),E?.dispose(),D?.dispose(),O?.dispose(),C?.dispose(),A.disconnect(),mediaCleanup?.(),mobileSidebarCleanup?.(),M?.dispose(),s!=null&&(clearTimeout(s),s=void 0)}}),D`
|
|
1
|
+
import{coarsePointerMixin as e,reducedMotionMixin as t}from"./styles/mixins/accessibility.css.js";import{roundedVariantMixin as n}from"./styles/mixins/shape.css.js";import{colorThemeMixin as r,elevationMixin as i}from"./styles/mixins/theme.css.js";import{frostVariantMixin as a}from"./styles/mixins/variants.css.js";import{ELEVATION_LEVELS as o,ROUNDED_SIZES as s,THEME_COLORS as c}from"./shared/types.js";import{ICON_TAG as l}from"./icon.js";import{computeSafeRel as u}from"./utils/link.js";import{parseMaxWidthPx as d,readContainerWidth as f,resolveContainerElement as p}from"./layout/shared.js";import m from"./layout/navbar/navbar.css.js";import{computed as h,fromSubscribable as g,signal as _,untrack as v,watch as y}from"@vielzeug/ripple";import{SentinelUnavailableError as b,createElementSize as x,createMediaQuery as S}from"@vielzeug/sentinel";import{bind as C,createContext as w,define as T,getHost as E,html as D,inject as O,onCleanup as k,onMounted as A,prop as j,provide as M,useEmit as N,useSlots as P}from"@vielzeug/ore";var F=[`flat`,`solid`,`bordered`,`outline`,`frost`],listen=(e,t,n,r)=>e?(e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)):()=>{},hasElementContent=e=>!(e instanceof HTMLElement)||e.tagName.includes(`-`)||e.childElementCount>0?!0:(e.textContent??``).trim().length>0,findScrollContainer=e=>{let t=e.parentElement;for(;t;){let e=window.getComputedStyle(t).overflowY;if(e===`auto`||e===`scroll`||e===`overlay`)return t;t=t.parentElement}return null},I=w(`NavbarContext`),L=`ore-navbar`;T(L,{props:{breakpoint:j.string(`(max-width: 768px)`),color:j.oneOf(c),"container-breakpoints":j.bool(!1),elevation:j.oneOf(o),floating:j.bool(!1),label:j.string(`Main navigation`),"menu-close-icon":j.string(`x`),"menu-close-label":j.string(`Close navigation menu`),"menu-icon":j.string(`menu`),"menu-open-label":j.string(`Open navigation menu`),"mobile-sidebar":j.string(),rounded:j.oneOf(s),"scroll-threshold":j.number(80),sticky:j.bool(!1),variant:j.oneOf(F)},setup(e){let t=E(),n=N(),r=P(),i=r.has(`logo`),a=r.has(`start`),o=r.has(),s=r.has(`end`),c=r.has(`persistent`),l=r.elements(`mobile-menu`),u=_(!1),m=_(null),w=_(!1),T=_(!1),O=_(!1),j=_(!1),F=_(!1),L=_(!1),R=_(0),z=_(!1),B=_(!1),V=_(d(e.breakpoint.value)),H=_(!1),getExternalSidebar=()=>{let n=String(e[`mobile-sidebar`].value??``).trim();if(!n)return null;let r=t.getRootNode();return(r instanceof ShadowRoot||r instanceof Document?r.querySelector(n):null)??document.querySelector(n)};M(I,{closeMobileMenu:()=>closeMobileMenu(),isMobile:h(()=>O.value),mobileMenuOpen:h(()=>j.value)});let computeMode=()=>{if(e.floating.value&&e.sticky.value)return L.value?`sticky`:`floating`;if(e.floating.value)return`floating`;if(e.sticky.value)return`sticky`},setMobileMenu=e=>{let t=!!e;if(!u.value){let e=m.value??getExternalSidebar();if(e){t?e.openDrawer?.():e.closeDrawer?.();return}}(!t||u.value)&&j.value!==t&&(j.value=t,n(`mobile-menu-change`,{open:t}))},closeMobileMenu=()=>setMobileMenu(!1),openMobileMenu=()=>setMobileMenu(!0),toggleMobileMenu=()=>{if(!u.value){let e=m.value??getExternalSidebar();if(e){typeof e.toggleDrawer==`function`?e.toggleDrawer():typeof e.openDrawer==`function`&&typeof e.closeDrawer==`function`&&(e.hasAttribute(`data-drawer-open`)?e.closeDrawer():e.openDrawer());return}}setMobileMenu(!j.value)},syncMobileMode=()=>{let t=(m.value&&!u.value?w.value:!1)||z.value||!!e[`container-breakpoints`].value&&B.value;O.value=t,t||closeMobileMenu()},readScrollOffset=e=>e?e.scrollTop||0:window.scrollY||document.documentElement.scrollTop||0,resetScrollBase=e=>{R.value=readScrollOffset(e)},updateScrolled=(t=null)=>{if(!(e.floating.value&&e.sticky.value)){L.value&&=!1;return}let n=Math.max(0,Number(e[`scroll-threshold`].value)||0),r=readScrollOffset(t)-R.value;L.value=r>n};return C({attr:{"data-mobile":()=>O.value?!0:void 0,"data-mobile-open":()=>O.value&&j.value?!0:void 0,"data-mode":()=>computeMode()??null,"data-mode-transitioning":()=>F.value?!0:void 0,"data-preview-mode":()=>H.value?!0:void 0,"data-scrolled":()=>e.floating.value&&e.sticky.value&&L.value?!0:void 0}}),A(()=>{let n=t,r=findScrollContainer(t);n.closeMobileMenu=closeMobileMenu,n.openMobileMenu=openMobileMenu,n.toggleMobileMenu=toggleMobileMenu;let i=listen(window,`scroll`,()=>{updateScrolled(r)},{passive:!0}),a=r?listen(r,`scroll`,()=>{updateScrolled(r)},{passive:!0}):void 0,o=listen(t,`keydown`,e=>{e.key===`Escape`&&j.value&&(e.preventDefault(),e.stopPropagation(),closeMobileMenu(),t.shadowRoot?.querySelector(`[part="mobile-toggle"]`)?.focus())}),mediaCleanup,mobileSidebarCleanup,s,c,resolveMobileSidebarTarget=()=>{if(mobileSidebarCleanup?.(),mobileSidebarCleanup=void 0,m.value=null,w.value=!1,T.value=!1,!String(e[`mobile-sidebar`].value??``).trim())return;let n=getExternalSidebar();if(!n){let e=t.getRootNode(),n=e instanceof Document||e instanceof ShadowRoot?e:document,r=new MutationObserver(()=>{getExternalSidebar()&&resolveMobileSidebarTarget()});r.observe(n,{childList:!0,subtree:!0}),mobileSidebarCleanup=()=>r.disconnect();return}let syncTargetState=e=>{let t=n.hasAttribute(`data-bottom-nav`),r=n.hasAttribute(`data-drawer-open`);w.value=t,T.value=e?.detail?e.detail.open:r,T.value&&!w.value&&(w.value=!0),syncMobileMode()},r=new MutationObserver(()=>{syncTargetState()});syncTargetState(),r.observe(n,{attributeFilter:[`data-bottom-nav`,`data-drawer-open`],attributes:!0}),n.addEventListener(`drawer-change`,syncTargetState),mobileSidebarCleanup=()=>{r.disconnect(),n.removeEventListener(`drawer-change`,syncTargetState)},m.value=n},triggerModeTransition=()=>{F.value=!0,s!=null&&clearTimeout(s),s=setTimeout(()=>{F.value=!1,s=void 0},220)},_=y(h(()=>e.floating.value&&e.sticky.value),e=>{if(!e){L.value&&=!1,F.value&&=!1,c=void 0,s!=null&&(clearTimeout(s),s=void 0);return}resetScrollBase(r),updateScrolled(r),c=computeMode()},{immediate:!0}),C=y(e[`mobile-sidebar`],()=>{resolveMobileSidebarTarget()},{immediate:!0}),E=y(h(()=>computeMode()),t=>{if(!(e.floating.value&&e.sticky.value)){c=t??void 0;return}let n=t??void 0;c&&n&&c!==n&&triggerModeTransition(),c=n},{immediate:!0}),D=y(u,e=>{!e&&j.value&&closeMobileMenu()},{immediate:!0}),syncMobileMenuContent=()=>{u.value=l.value.some(hasElementContent)},O=y(l,syncMobileMenuContent,{immediate:!0}),A=new MutationObserver(()=>{syncMobileMenuContent()});A.observe(t,{childList:!0,subtree:!0}),y(e.breakpoint,n=>{v(()=>{mediaCleanup?.(),mediaCleanup=void 0;let r=String(n??``).trim();V.value=d(r),z.value=!1;let i=f(t);if(B.value=i>0&&V.value!=null&&i<=V.value,syncMobileMode(),!(e[`container-breakpoints`].value&&V.value!=null)&&r)try{let e=S(r),syncMedia=e=>{e&&(z.value=e.matches,syncMobileMode())};syncMedia(e.getSnapshot());let t=y(g(e,{signal:e.disposalSignal}),syncMedia);mediaCleanup=()=>{t.dispose(),e.dispose()}}catch(e){if(!(e instanceof b))throw e}})},{immediate:!0});let M=typeof ResizeObserver==`function`?(()=>{let e=x(t);k(()=>e.dispose());let n=g(e,{signal:e.disposalSignal}),r=t.parentElement,i=p(t),a=r?x(r):void 0,o=i&&i!==r?x(i):void 0;a&&k(()=>a.dispose()),o&&k(()=>o.dispose());let s=a?g(a,{signal:a.disposalSignal}):void 0,c=o?g(o,{signal:o.disposalSignal}):void 0;return y(h(()=>[n.value?.width,n.value?.height,s?.value?.width,s?.value?.height,c?.value?.width,c?.value?.height]),()=>{let e=f(t),n=p(t)?.clientWidth??0;B.value=e>0&&V.value!=null&&e<=V.value,H.value=n>0&&n<window.innerWidth,syncMobileMode()})})():void 0;return()=>{o?.(),i?.(),a?.(),_?.dispose(),E?.dispose(),D?.dispose(),O?.dispose(),C?.dispose(),A.disconnect(),mediaCleanup?.(),mobileSidebarCleanup?.(),M?.dispose(),s!=null&&(clearTimeout(s),s=void 0)}}),D`
|
|
2
2
|
<nav part="nav" aria-label="${e.label}">
|
|
3
3
|
<div class="navbar" part="bar">
|
|
4
4
|
<div class="navbar-logo" part="logo" ?hidden=${()=>!i.value}>
|