@duskmoon-dev/el-bottom-navigation 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -0
- package/dist/cjs/index.js +40 -46
- package/dist/cjs/index.js.map +3 -3
- package/dist/cjs/register.js +40 -46
- package/dist/cjs/register.js.map +3 -3
- package/dist/esm/index.js +38 -44
- package/dist/esm/index.js.map +3 -3
- package/dist/esm/register.js +38 -44
- package/dist/esm/register.js.map +3 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/el-dm-bottom-navigation.d.ts +9 -1
- package/dist/types/el-dm-bottom-navigation.d.ts.map +1 -1
- package/package.json +6 -5
package/dist/cjs/register.js.map
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/el-dm-bottom-navigation.ts", "../../src/index.ts", "../../src/register.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * DuskMoon Bottom Navigation Element\n *\n * A mobile-first bottom navigation bar component with nav items and icons.\n * Fixed position at the bottom of the viewport for easy thumb access.\n *\n * @element el-dm-bottom-navigation\n *\n * @attr {Array} items - Array of navigation items with { value, label, icon? } structure\n * @attr {string} value - Currently selected item value\n * @attr {string} color - Color theme: primary, secondary, or custom color\n *\n * @slot - Default slot for custom nav items (el-dm-bottom-navigation-item)\n *\n * @csspart container - The navigation container\n * @csspart item - Individual navigation item\n * @csspart icon - Item icon container\n * @csspart label - Item label\n *\n * @fires change - Fired when selection changes, detail: { value, item }\n */\n\nimport { BaseElement, css } from '@duskmoon-dev/el-core';\n\n/**\n * Navigation item structure\n */\nexport interface BottomNavigationItem {\n /** Unique identifier for the item */\n value: string;\n /** Display label */\n label: string;\n /** Icon HTML or SVG string */\n icon?: string;\n /** Whether the item is disabled */\n disabled?: boolean;\n /** Optional href for link behavior */\n href?: string;\n}\n\nconst styles = css`\n :host {\n --bottom-nav-height: 56px;\n --bottom-nav-bg: var(--color-surface, #ffffff);\n --bottom-nav-border: var(--color-border, #e5e7eb);\n --bottom-nav-text: var(--color-text-secondary, #6b7280);\n --bottom-nav-text-active: var(--color-primary, #3b82f6);\n --bottom-nav-icon-size: 24px;\n --bottom-nav-label-size: 0.75rem;\n --bottom-nav-shadow: 0 -1px 3px rgba(0, 0, 0, 0.1);\n\n display: block;\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 1000;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n :host([position='static']) {\n position: static;\n }\n\n :host([position='sticky']) {\n position: sticky;\n }\n\n
|
|
5
|
+
"/**\n * DuskMoon Bottom Navigation Element\n *\n * A mobile-first bottom navigation bar component with nav items and icons.\n * Fixed position at the bottom of the viewport for easy thumb access.\n *\n * @element el-dm-bottom-navigation\n *\n * @attr {Array} items - Array of navigation items with { value, label, icon? } structure\n * @attr {string} value - Currently selected item value\n * @attr {string} color - Color theme: primary, secondary, or custom color\n *\n * @slot - Default slot for custom nav items (el-dm-bottom-navigation-item)\n *\n * @csspart container - The navigation container\n * @csspart item - Individual navigation item\n * @csspart icon - Item icon container\n * @csspart label - Item label\n *\n * @fires change - Fired when selection changes, detail: { value, item }\n */\n\nimport { BaseElement, css } from '@duskmoon-dev/el-base';\nimport { css as bottomNavCSS } from '@duskmoon-dev/core/components/bottom-navigation';\n\n/**\n * Navigation item structure\n */\nexport interface BottomNavigationItem {\n /** Unique identifier for the item */\n value: string;\n /** Display label */\n label: string;\n /** Icon HTML or SVG string */\n icon?: string;\n /** Whether the item is disabled */\n disabled?: boolean;\n /** Optional href for link behavior */\n href?: string;\n}\n\n// Strip @layer wrapper for Shadow DOM compatibility\nconst coreStyles = bottomNavCSS.replace(/@layer\\s+components\\s*\\{/, '').replace(/\\}\\s*$/, '');\n\nconst styles = css`\n :host {\n --bottom-nav-height: 56px;\n --bottom-nav-bg: var(--color-surface, #ffffff);\n --bottom-nav-border: var(--color-border, #e5e7eb);\n --bottom-nav-text: var(--color-text-secondary, #6b7280);\n --bottom-nav-text-active: var(--color-primary, #3b82f6);\n --bottom-nav-icon-size: 24px;\n --bottom-nav-label-size: 0.75rem;\n --bottom-nav-shadow: 0 -1px 3px rgba(0, 0, 0, 0.1);\n\n display: block;\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 1000;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n :host([position='static']) {\n position: static;\n }\n\n :host([position='sticky']) {\n position: sticky;\n }\n\n /* Import core bottom-navigation styles */\n ${coreStyles}\n\n /* Override core's fixed positioning — :host handles it */\n .bottom-nav {\n position: static;\n height: var(--bottom-nav-height);\n min-height: auto;\n background: var(--bottom-nav-bg);\n border-top: 1px solid var(--bottom-nav-border);\n box-shadow: var(--bottom-nav-shadow);\n }\n\n /* Override core item styles with our custom properties */\n .bottom-nav-item {\n gap: 2px;\n padding: 6px 12px;\n min-width: 0;\n max-width: 168px;\n height: 100%;\n color: var(--bottom-nav-text);\n transition:\n color 0.2s ease,\n transform 0.1s ease;\n -webkit-tap-highlight-color: transparent;\n user-select: none;\n }\n\n .bottom-nav-item:focus {\n outline: none;\n }\n\n .bottom-nav-item:focus-visible {\n outline: 2px solid var(--bottom-nav-text-active);\n outline-offset: -2px;\n border-radius: 4px;\n }\n\n .bottom-nav-item:active:not([disabled]) {\n transform: scale(0.95);\n }\n\n .bottom-nav-item[aria-selected='true'],\n .bottom-nav-item.active {\n color: var(--bottom-nav-text-active);\n }\n\n .bottom-nav-item[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n }\n\n .bottom-nav-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--bottom-nav-icon-size);\n height: var(--bottom-nav-icon-size);\n flex-shrink: 0;\n }\n\n .bottom-nav-icon ::slotted(*),\n .bottom-nav-icon svg,\n .bottom-nav-icon img {\n width: 100%;\n height: 100%;\n }\n\n .bottom-nav-label {\n font-size: var(--bottom-nav-label-size);\n font-weight: 500;\n line-height: 1.2;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n }\n\n /* Hide labels on very small screens */\n @media (max-width: 320px) {\n .bottom-nav-label {\n display: none;\n }\n\n .bottom-nav-icon {\n width: 28px;\n height: 28px;\n }\n }\n\n /* Color variants */\n :host([color='secondary']) {\n --bottom-nav-text-active: var(--color-secondary, #8b5cf6);\n }\n\n :host([color='success']) {\n --bottom-nav-text-active: var(--color-success, #22c55e);\n }\n\n :host([color='warning']) {\n --bottom-nav-text-active: var(--color-warning, #f59e0b);\n }\n\n :host([color='error']) {\n --bottom-nav-text-active: var(--color-error, #ef4444);\n }\n\n /* Badge indicator for items */\n .bottom-nav-badge {\n position: absolute;\n top: 4px;\n right: calc(50% - 18px);\n min-width: 8px;\n height: 8px;\n background: var(--color-error, #ef4444);\n border-radius: 50%;\n }\n\n .bottom-nav-item-wrapper {\n position: relative;\n flex: 1;\n display: flex;\n justify-content: center;\n max-width: 168px;\n }\n\n /* Slot for custom items */\n ::slotted(el-dm-bottom-navigation-item) {\n flex: 1;\n }\n`;\n\nexport class ElDmBottomNavigation extends BaseElement {\n static properties = {\n items: { type: Array, reflect: false, default: [] },\n value: { type: String, reflect: true },\n color: { type: String, reflect: true, default: 'primary' },\n position: { type: String, reflect: true, default: 'fixed' },\n navLabel: { type: String, reflect: true, attribute: 'nav-label', default: 'Bottom navigation' },\n };\n\n /** Array of navigation items */\n declare items: BottomNavigationItem[];\n\n /** Currently selected item value */\n declare value: string;\n\n /** Color theme */\n declare color: 'primary' | 'secondary' | 'success' | 'warning' | 'error' | string;\n\n /** Position of the navigation bar */\n declare position: 'fixed' | 'static' | 'sticky';\n\n /** Accessible label for the navigation landmark */\n declare navLabel: string;\n\n constructor() {\n super();\n this.attachStyles(styles);\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener('click', this._handleClick.bind(this));\n this.addEventListener('keydown', this._handleKeydown.bind(this));\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('click', this._handleClick.bind(this));\n this.removeEventListener('keydown', this._handleKeydown.bind(this));\n }\n\n /**\n * Handle click events on nav items\n */\n private _handleClick(event: MouseEvent): void {\n const target = event.target as HTMLElement;\n const navItem = target.closest('[data-value]') as HTMLElement | null;\n\n if (!navItem || navItem.hasAttribute('disabled')) {\n return;\n }\n\n const value = navItem.dataset.value;\n if (value && value !== this.value) {\n const item = this.items.find((i) => i.value === value);\n this.value = value;\n this.emit('change', { value, item });\n }\n }\n\n /**\n * Handle keyboard navigation\n */\n private _handleKeydown(event: KeyboardEvent): void {\n const target = event.target as HTMLElement;\n if (!target.classList.contains('bottom-nav-item')) return;\n\n const items = Array.from(this.shadowRoot.querySelectorAll('.bottom-nav-item:not([disabled])'));\n const currentIndex = items.indexOf(target);\n\n let nextIndex = -1;\n\n switch (event.key) {\n case 'ArrowLeft':\n case 'ArrowUp':\n event.preventDefault();\n nextIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;\n break;\n case 'ArrowRight':\n case 'ArrowDown':\n event.preventDefault();\n nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;\n break;\n case 'Home':\n event.preventDefault();\n nextIndex = 0;\n break;\n case 'End':\n event.preventDefault();\n nextIndex = items.length - 1;\n break;\n case 'Enter':\n case ' ':\n event.preventDefault();\n target.click();\n return;\n }\n\n if (nextIndex >= 0 && items[nextIndex]) {\n (items[nextIndex] as HTMLElement).focus();\n }\n }\n\n /**\n * Render a single navigation item\n */\n private _renderItem(item: BottomNavigationItem): string {\n const isSelected = item.value === this.value;\n const Tag = item.href ? 'a' : 'button';\n const hrefAttr = item.href ? `href=\"${item.href}\"` : '';\n const disabledAttr = item.disabled ? 'disabled' : '';\n const typeAttr = Tag === 'button' ? 'type=\"button\"' : '';\n\n return `\n <div class=\"bottom-nav-item-wrapper\">\n <${Tag}\n class=\"bottom-nav-item\"\n part=\"item\"\n role=\"tab\"\n tabindex=\"${item.disabled ? '-1' : '0'}\"\n aria-selected=\"${isSelected}\"\n data-value=\"${item.value}\"\n ${hrefAttr}\n ${disabledAttr}\n ${typeAttr}\n >\n ${\n item.icon\n ? `\n <span class=\"bottom-nav-icon\" part=\"icon\">\n ${item.icon}\n </span>\n `\n : ''\n }\n <span class=\"bottom-nav-label\" part=\"label\">${item.label}</span>\n </${Tag}>\n </div>\n `;\n }\n\n render(): string {\n const hasItems = this.items && this.items.length > 0;\n\n return `\n <nav class=\"bottom-nav\" part=\"container\" role=\"tablist\" aria-label=\"${this.navLabel || 'Bottom navigation'}\">\n ${hasItems ? this.items.map((item) => this._renderItem(item)).join('') : '<slot></slot>'}\n </nav>\n `;\n }\n}\n",
|
|
6
6
|
"/**\n * @duskmoon-dev/el-bottom-navigation\n *\n * DuskMoon Bottom Navigation custom element\n */\n\nimport { ElDmBottomNavigation } from './el-dm-bottom-navigation.js';\n\nexport { ElDmBottomNavigation };\nexport type { BottomNavigationItem } from './el-dm-bottom-navigation.js';\n\n/**\n * Register the el-dm-bottom-navigation custom element\n *\n * @example\n * ```ts\n * import { register } from '@duskmoon-dev/el-bottom-navigation';\n * register();\n * ```\n */\nexport function register(): void {\n if (!customElements.get('el-dm-bottom-navigation')) {\n customElements.define('el-dm-bottom-navigation', ElDmBottomNavigation);\n }\n}\n",
|
|
7
7
|
"/**\n * Auto-register el-dm-bottom-navigation custom element\n *\n * @example\n * ```ts\n * import '@duskmoon-dev/el-bottom-navigation/register';\n *\n * // Now you can use <el-dm-bottom-navigation> in HTML\n * ```\n */\nimport { register } from './index.js';\n\nregister();\n"
|
|
8
8
|
],
|
|
9
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBiC,IAAjC;
|
|
10
|
-
"debugId": "
|
|
9
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBiC,IAAjC;AACoC,IAApC;AAmBA,IAAM,aAAa,6BAAa,QAAQ,4BAA4B,EAAE,EAAE,QAAQ,UAAU,EAAE;AAE5F,IAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAgCX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoIG,MAAM,6BAA6B,2BAAY;AAAA,SAC7C,aAAa;AAAA,IAClB,OAAO,EAAE,MAAM,OAAO,SAAS,OAAO,SAAS,CAAC,EAAE;AAAA,IAClD,OAAO,EAAE,MAAM,QAAQ,SAAS,KAAK;AAAA,IACrC,OAAO,EAAE,MAAM,QAAQ,SAAS,MAAM,SAAS,UAAU;AAAA,IACzD,UAAU,EAAE,MAAM,QAAQ,SAAS,MAAM,SAAS,QAAQ;AAAA,IAC1D,UAAU,EAAE,MAAM,QAAQ,SAAS,MAAM,WAAW,aAAa,SAAS,oBAAoB;AAAA,EAChG;AAAA,EAiBA,WAAW,GAAG;AAAA,IACZ,MAAM;AAAA,IACN,KAAK,aAAa,MAAM;AAAA;AAAA,EAG1B,iBAAiB,GAAS;AAAA,IACxB,MAAM,kBAAkB;AAAA,IACxB,KAAK,iBAAiB,SAAS,KAAK,aAAa,KAAK,IAAI,CAAC;AAAA,IAC3D,KAAK,iBAAiB,WAAW,KAAK,eAAe,KAAK,IAAI,CAAC;AAAA;AAAA,EAGjE,oBAAoB,GAAS;AAAA,IAC3B,MAAM,qBAAqB;AAAA,IAC3B,KAAK,oBAAoB,SAAS,KAAK,aAAa,KAAK,IAAI,CAAC;AAAA,IAC9D,KAAK,oBAAoB,WAAW,KAAK,eAAe,KAAK,IAAI,CAAC;AAAA;AAAA,EAM5D,YAAY,CAAC,OAAyB;AAAA,IAC5C,MAAM,SAAS,MAAM;AAAA,IACrB,MAAM,UAAU,OAAO,QAAQ,cAAc;AAAA,IAE7C,IAAI,CAAC,WAAW,QAAQ,aAAa,UAAU,GAAG;AAAA,MAChD;AAAA,IACF;AAAA,IAEA,MAAM,QAAQ,QAAQ,QAAQ;AAAA,IAC9B,IAAI,SAAS,UAAU,KAAK,OAAO;AAAA,MACjC,MAAM,OAAO,KAAK,MAAM,KAAK,CAAC,MAAM,EAAE,UAAU,KAAK;AAAA,MACrD,KAAK,QAAQ;AAAA,MACb,KAAK,KAAK,UAAU,EAAE,OAAO,KAAK,CAAC;AAAA,IACrC;AAAA;AAAA,EAMM,cAAc,CAAC,OAA4B;AAAA,IACjD,MAAM,SAAS,MAAM;AAAA,IACrB,IAAI,CAAC,OAAO,UAAU,SAAS,iBAAiB;AAAA,MAAG;AAAA,IAEnD,MAAM,QAAQ,MAAM,KAAK,KAAK,WAAW,iBAAiB,kCAAkC,CAAC;AAAA,IAC7F,MAAM,eAAe,MAAM,QAAQ,MAAM;AAAA,IAEzC,IAAI,YAAY;AAAA,IAEhB,QAAQ,MAAM;AAAA,WACP;AAAA,WACA;AAAA,QACH,MAAM,eAAe;AAAA,QACrB,YAAY,eAAe,IAAI,eAAe,IAAI,MAAM,SAAS;AAAA,QACjE;AAAA,WACG;AAAA,WACA;AAAA,QACH,MAAM,eAAe;AAAA,QACrB,YAAY,eAAe,MAAM,SAAS,IAAI,eAAe,IAAI;AAAA,QACjE;AAAA,WACG;AAAA,QACH,MAAM,eAAe;AAAA,QACrB,YAAY;AAAA,QACZ;AAAA,WACG;AAAA,QACH,MAAM,eAAe;AAAA,QACrB,YAAY,MAAM,SAAS;AAAA,QAC3B;AAAA,WACG;AAAA,WACA;AAAA,QACH,MAAM,eAAe;AAAA,QACrB,OAAO,MAAM;AAAA,QACb;AAAA;AAAA,IAGJ,IAAI,aAAa,KAAK,MAAM,YAAY;AAAA,MACrC,MAAM,WAA2B,MAAM;AAAA,IAC1C;AAAA;AAAA,EAMM,WAAW,CAAC,MAAoC;AAAA,IACtD,MAAM,aAAa,KAAK,UAAU,KAAK;AAAA,IACvC,MAAM,MAAM,KAAK,OAAO,MAAM;AAAA,IAC9B,MAAM,WAAW,KAAK,OAAO,SAAS,KAAK,UAAU;AAAA,IACrD,MAAM,eAAe,KAAK,WAAW,aAAa;AAAA,IAClD,MAAM,WAAW,QAAQ,WAAW,kBAAkB;AAAA,IAEtD,OAAO;AAAA;AAAA,WAEA;AAAA;AAAA;AAAA;AAAA,sBAIW,KAAK,WAAW,OAAO;AAAA,2BAClB;AAAA,wBACH,KAAK;AAAA,YACjB;AAAA,YACA;AAAA,YACA;AAAA;AAAA,YAGA,KAAK,OACD;AAAA;AAAA,gBAEA,KAAK;AAAA;AAAA,cAGL;AAAA,wDAEwC,KAAK;AAAA,YACjD;AAAA;AAAA;AAAA;AAAA,EAKV,MAAM,GAAW;AAAA,IACf,MAAM,WAAW,KAAK,SAAS,KAAK,MAAM,SAAS;AAAA,IAEnD,OAAO;AAAA,4EACiE,KAAK,YAAY;AAAA,UACnF,WAAW,KAAK,MAAM,IAAI,CAAC,SAAS,KAAK,YAAY,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI;AAAA;AAAA;AAAA;AAIjF;;;AClVO,SAAS,QAAQ,GAAS;AAAA,EAC/B,IAAI,CAAC,eAAe,IAAI,yBAAyB,GAAG;AAAA,IAClD,eAAe,OAAO,2BAA2B,oBAAoB;AAAA,EACvE;AAAA;;;ACXF,SAAS;",
|
|
10
|
+
"debugId": "90D3108BFBBBEEE564756E2164756E21",
|
|
11
11
|
"names": []
|
|
12
12
|
}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// src/el-dm-bottom-navigation.ts
|
|
2
|
-
import { BaseElement, css } from "@duskmoon-dev/el-
|
|
2
|
+
import { BaseElement, css } from "@duskmoon-dev/el-base";
|
|
3
|
+
import { css as bottomNavCSS } from "@duskmoon-dev/core/components/bottom-navigation";
|
|
4
|
+
var coreStyles = bottomNavCSS.replace(/@layer\s+components\s*\{/, "").replace(/\}\s*$/, "");
|
|
3
5
|
var styles = css`
|
|
4
6
|
:host {
|
|
5
7
|
--bottom-nav-height: 56px;
|
|
@@ -31,36 +33,27 @@ var styles = css`
|
|
|
31
33
|
position: sticky;
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
/* Import core bottom-navigation styles */
|
|
37
|
+
${coreStyles}
|
|
38
|
+
|
|
39
|
+
/* Override core's fixed positioning — :host handles it */
|
|
34
40
|
.bottom-nav {
|
|
35
|
-
|
|
36
|
-
align-items: center;
|
|
37
|
-
justify-content: space-around;
|
|
41
|
+
position: static;
|
|
38
42
|
height: var(--bottom-nav-height);
|
|
43
|
+
min-height: auto;
|
|
39
44
|
background: var(--bottom-nav-bg);
|
|
40
45
|
border-top: 1px solid var(--bottom-nav-border);
|
|
41
46
|
box-shadow: var(--bottom-nav-shadow);
|
|
42
|
-
padding: 0;
|
|
43
|
-
margin: 0;
|
|
44
|
-
/* Safe area for iOS devices */
|
|
45
|
-
padding-bottom: env(safe-area-inset-bottom, 0);
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
display: flex;
|
|
51
|
-
flex-direction: column;
|
|
52
|
-
align-items: center;
|
|
53
|
-
justify-content: center;
|
|
49
|
+
/* Override core item styles with our custom properties */
|
|
50
|
+
.bottom-nav-item {
|
|
54
51
|
gap: 2px;
|
|
55
52
|
padding: 6px 12px;
|
|
56
53
|
min-width: 0;
|
|
57
54
|
max-width: 168px;
|
|
58
55
|
height: 100%;
|
|
59
|
-
background: transparent;
|
|
60
|
-
border: none;
|
|
61
|
-
cursor: pointer;
|
|
62
56
|
color: var(--bottom-nav-text);
|
|
63
|
-
text-decoration: none;
|
|
64
57
|
transition:
|
|
65
58
|
color 0.2s ease,
|
|
66
59
|
transform 0.1s ease;
|
|
@@ -68,32 +61,32 @@ var styles = css`
|
|
|
68
61
|
user-select: none;
|
|
69
62
|
}
|
|
70
63
|
|
|
71
|
-
.nav-item:focus {
|
|
64
|
+
.bottom-nav-item:focus {
|
|
72
65
|
outline: none;
|
|
73
66
|
}
|
|
74
67
|
|
|
75
|
-
.nav-item:focus-visible {
|
|
68
|
+
.bottom-nav-item:focus-visible {
|
|
76
69
|
outline: 2px solid var(--bottom-nav-text-active);
|
|
77
70
|
outline-offset: -2px;
|
|
78
71
|
border-radius: 4px;
|
|
79
72
|
}
|
|
80
73
|
|
|
81
|
-
.nav-item:active:not([disabled]) {
|
|
74
|
+
.bottom-nav-item:active:not([disabled]) {
|
|
82
75
|
transform: scale(0.95);
|
|
83
76
|
}
|
|
84
77
|
|
|
85
|
-
.nav-item[aria-selected='true'],
|
|
86
|
-
.nav-item.active {
|
|
78
|
+
.bottom-nav-item[aria-selected='true'],
|
|
79
|
+
.bottom-nav-item.active {
|
|
87
80
|
color: var(--bottom-nav-text-active);
|
|
88
81
|
}
|
|
89
82
|
|
|
90
|
-
.nav-item[disabled] {
|
|
83
|
+
.bottom-nav-item[disabled] {
|
|
91
84
|
opacity: 0.5;
|
|
92
85
|
cursor: not-allowed;
|
|
93
86
|
pointer-events: none;
|
|
94
87
|
}
|
|
95
88
|
|
|
96
|
-
.nav-icon {
|
|
89
|
+
.bottom-nav-icon {
|
|
97
90
|
display: flex;
|
|
98
91
|
align-items: center;
|
|
99
92
|
justify-content: center;
|
|
@@ -102,14 +95,14 @@ var styles = css`
|
|
|
102
95
|
flex-shrink: 0;
|
|
103
96
|
}
|
|
104
97
|
|
|
105
|
-
.nav-icon ::slotted(*),
|
|
106
|
-
.nav-icon svg,
|
|
107
|
-
.nav-icon img {
|
|
98
|
+
.bottom-nav-icon ::slotted(*),
|
|
99
|
+
.bottom-nav-icon svg,
|
|
100
|
+
.bottom-nav-icon img {
|
|
108
101
|
width: 100%;
|
|
109
102
|
height: 100%;
|
|
110
103
|
}
|
|
111
104
|
|
|
112
|
-
.nav-label {
|
|
105
|
+
.bottom-nav-label {
|
|
113
106
|
font-size: var(--bottom-nav-label-size);
|
|
114
107
|
font-weight: 500;
|
|
115
108
|
line-height: 1.2;
|
|
@@ -121,11 +114,11 @@ var styles = css`
|
|
|
121
114
|
|
|
122
115
|
/* Hide labels on very small screens */
|
|
123
116
|
@media (max-width: 320px) {
|
|
124
|
-
.nav-label {
|
|
117
|
+
.bottom-nav-label {
|
|
125
118
|
display: none;
|
|
126
119
|
}
|
|
127
120
|
|
|
128
|
-
.nav-icon {
|
|
121
|
+
.bottom-nav-icon {
|
|
129
122
|
width: 28px;
|
|
130
123
|
height: 28px;
|
|
131
124
|
}
|
|
@@ -149,7 +142,7 @@ var styles = css`
|
|
|
149
142
|
}
|
|
150
143
|
|
|
151
144
|
/* Badge indicator for items */
|
|
152
|
-
.nav-
|
|
145
|
+
.bottom-nav-badge {
|
|
153
146
|
position: absolute;
|
|
154
147
|
top: 4px;
|
|
155
148
|
right: calc(50% - 18px);
|
|
@@ -159,7 +152,7 @@ var styles = css`
|
|
|
159
152
|
border-radius: 50%;
|
|
160
153
|
}
|
|
161
154
|
|
|
162
|
-
.nav-item-wrapper {
|
|
155
|
+
.bottom-nav-item-wrapper {
|
|
163
156
|
position: relative;
|
|
164
157
|
flex: 1;
|
|
165
158
|
display: flex;
|
|
@@ -178,7 +171,8 @@ class ElDmBottomNavigation extends BaseElement {
|
|
|
178
171
|
items: { type: Array, reflect: false, default: [] },
|
|
179
172
|
value: { type: String, reflect: true },
|
|
180
173
|
color: { type: String, reflect: true, default: "primary" },
|
|
181
|
-
position: { type: String, reflect: true, default: "fixed" }
|
|
174
|
+
position: { type: String, reflect: true, default: "fixed" },
|
|
175
|
+
navLabel: { type: String, reflect: true, attribute: "nav-label", default: "Bottom navigation" }
|
|
182
176
|
};
|
|
183
177
|
constructor() {
|
|
184
178
|
super();
|
|
@@ -209,9 +203,9 @@ class ElDmBottomNavigation extends BaseElement {
|
|
|
209
203
|
}
|
|
210
204
|
_handleKeydown(event) {
|
|
211
205
|
const target = event.target;
|
|
212
|
-
if (!target.classList.contains("nav-item"))
|
|
206
|
+
if (!target.classList.contains("bottom-nav-item"))
|
|
213
207
|
return;
|
|
214
|
-
const items = Array.from(this.shadowRoot.querySelectorAll(".nav-item:not([disabled])"));
|
|
208
|
+
const items = Array.from(this.shadowRoot.querySelectorAll(".bottom-nav-item:not([disabled])"));
|
|
215
209
|
const currentIndex = items.indexOf(target);
|
|
216
210
|
let nextIndex = -1;
|
|
217
211
|
switch (event.key) {
|
|
@@ -243,16 +237,16 @@ class ElDmBottomNavigation extends BaseElement {
|
|
|
243
237
|
items[nextIndex].focus();
|
|
244
238
|
}
|
|
245
239
|
}
|
|
246
|
-
_renderItem(item
|
|
240
|
+
_renderItem(item) {
|
|
247
241
|
const isSelected = item.value === this.value;
|
|
248
242
|
const Tag = item.href ? "a" : "button";
|
|
249
243
|
const hrefAttr = item.href ? `href="${item.href}"` : "";
|
|
250
244
|
const disabledAttr = item.disabled ? "disabled" : "";
|
|
251
245
|
const typeAttr = Tag === "button" ? 'type="button"' : "";
|
|
252
246
|
return `
|
|
253
|
-
<div class="nav-item-wrapper">
|
|
247
|
+
<div class="bottom-nav-item-wrapper">
|
|
254
248
|
<${Tag}
|
|
255
|
-
class="nav-item"
|
|
249
|
+
class="bottom-nav-item"
|
|
256
250
|
part="item"
|
|
257
251
|
role="tab"
|
|
258
252
|
tabindex="${item.disabled ? "-1" : "0"}"
|
|
@@ -263,11 +257,11 @@ class ElDmBottomNavigation extends BaseElement {
|
|
|
263
257
|
${typeAttr}
|
|
264
258
|
>
|
|
265
259
|
${item.icon ? `
|
|
266
|
-
<span class="nav-icon" part="icon">
|
|
260
|
+
<span class="bottom-nav-icon" part="icon">
|
|
267
261
|
${item.icon}
|
|
268
262
|
</span>
|
|
269
263
|
` : ""}
|
|
270
|
-
<span class="nav-label" part="label">${item.label}</span>
|
|
264
|
+
<span class="bottom-nav-label" part="label">${item.label}</span>
|
|
271
265
|
</${Tag}>
|
|
272
266
|
</div>
|
|
273
267
|
`;
|
|
@@ -275,8 +269,8 @@ class ElDmBottomNavigation extends BaseElement {
|
|
|
275
269
|
render() {
|
|
276
270
|
const hasItems = this.items && this.items.length > 0;
|
|
277
271
|
return `
|
|
278
|
-
<nav class="bottom-nav" part="container" role="tablist" aria-label="Bottom navigation">
|
|
279
|
-
${hasItems ? this.items.map((item
|
|
272
|
+
<nav class="bottom-nav" part="container" role="tablist" aria-label="${this.navLabel || "Bottom navigation"}">
|
|
273
|
+
${hasItems ? this.items.map((item) => this._renderItem(item)).join("") : "<slot></slot>"}
|
|
280
274
|
</nav>
|
|
281
275
|
`;
|
|
282
276
|
}
|
|
@@ -293,5 +287,5 @@ export {
|
|
|
293
287
|
ElDmBottomNavigation
|
|
294
288
|
};
|
|
295
289
|
|
|
296
|
-
//# debugId=
|
|
290
|
+
//# debugId=EA50E9812C948AA164756E2164756E21
|
|
297
291
|
//# sourceMappingURL=index.js.map
|
package/dist/esm/index.js.map
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/el-dm-bottom-navigation.ts", "../../src/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * DuskMoon Bottom Navigation Element\n *\n * A mobile-first bottom navigation bar component with nav items and icons.\n * Fixed position at the bottom of the viewport for easy thumb access.\n *\n * @element el-dm-bottom-navigation\n *\n * @attr {Array} items - Array of navigation items with { value, label, icon? } structure\n * @attr {string} value - Currently selected item value\n * @attr {string} color - Color theme: primary, secondary, or custom color\n *\n * @slot - Default slot for custom nav items (el-dm-bottom-navigation-item)\n *\n * @csspart container - The navigation container\n * @csspart item - Individual navigation item\n * @csspart icon - Item icon container\n * @csspart label - Item label\n *\n * @fires change - Fired when selection changes, detail: { value, item }\n */\n\nimport { BaseElement, css } from '@duskmoon-dev/el-core';\n\n/**\n * Navigation item structure\n */\nexport interface BottomNavigationItem {\n /** Unique identifier for the item */\n value: string;\n /** Display label */\n label: string;\n /** Icon HTML or SVG string */\n icon?: string;\n /** Whether the item is disabled */\n disabled?: boolean;\n /** Optional href for link behavior */\n href?: string;\n}\n\nconst styles = css`\n :host {\n --bottom-nav-height: 56px;\n --bottom-nav-bg: var(--color-surface, #ffffff);\n --bottom-nav-border: var(--color-border, #e5e7eb);\n --bottom-nav-text: var(--color-text-secondary, #6b7280);\n --bottom-nav-text-active: var(--color-primary, #3b82f6);\n --bottom-nav-icon-size: 24px;\n --bottom-nav-label-size: 0.75rem;\n --bottom-nav-shadow: 0 -1px 3px rgba(0, 0, 0, 0.1);\n\n display: block;\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 1000;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n :host([position='static']) {\n position: static;\n }\n\n :host([position='sticky']) {\n position: sticky;\n }\n\n
|
|
5
|
+
"/**\n * DuskMoon Bottom Navigation Element\n *\n * A mobile-first bottom navigation bar component with nav items and icons.\n * Fixed position at the bottom of the viewport for easy thumb access.\n *\n * @element el-dm-bottom-navigation\n *\n * @attr {Array} items - Array of navigation items with { value, label, icon? } structure\n * @attr {string} value - Currently selected item value\n * @attr {string} color - Color theme: primary, secondary, or custom color\n *\n * @slot - Default slot for custom nav items (el-dm-bottom-navigation-item)\n *\n * @csspart container - The navigation container\n * @csspart item - Individual navigation item\n * @csspart icon - Item icon container\n * @csspart label - Item label\n *\n * @fires change - Fired when selection changes, detail: { value, item }\n */\n\nimport { BaseElement, css } from '@duskmoon-dev/el-base';\nimport { css as bottomNavCSS } from '@duskmoon-dev/core/components/bottom-navigation';\n\n/**\n * Navigation item structure\n */\nexport interface BottomNavigationItem {\n /** Unique identifier for the item */\n value: string;\n /** Display label */\n label: string;\n /** Icon HTML or SVG string */\n icon?: string;\n /** Whether the item is disabled */\n disabled?: boolean;\n /** Optional href for link behavior */\n href?: string;\n}\n\n// Strip @layer wrapper for Shadow DOM compatibility\nconst coreStyles = bottomNavCSS.replace(/@layer\\s+components\\s*\\{/, '').replace(/\\}\\s*$/, '');\n\nconst styles = css`\n :host {\n --bottom-nav-height: 56px;\n --bottom-nav-bg: var(--color-surface, #ffffff);\n --bottom-nav-border: var(--color-border, #e5e7eb);\n --bottom-nav-text: var(--color-text-secondary, #6b7280);\n --bottom-nav-text-active: var(--color-primary, #3b82f6);\n --bottom-nav-icon-size: 24px;\n --bottom-nav-label-size: 0.75rem;\n --bottom-nav-shadow: 0 -1px 3px rgba(0, 0, 0, 0.1);\n\n display: block;\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 1000;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n :host([position='static']) {\n position: static;\n }\n\n :host([position='sticky']) {\n position: sticky;\n }\n\n /* Import core bottom-navigation styles */\n ${coreStyles}\n\n /* Override core's fixed positioning — :host handles it */\n .bottom-nav {\n position: static;\n height: var(--bottom-nav-height);\n min-height: auto;\n background: var(--bottom-nav-bg);\n border-top: 1px solid var(--bottom-nav-border);\n box-shadow: var(--bottom-nav-shadow);\n }\n\n /* Override core item styles with our custom properties */\n .bottom-nav-item {\n gap: 2px;\n padding: 6px 12px;\n min-width: 0;\n max-width: 168px;\n height: 100%;\n color: var(--bottom-nav-text);\n transition:\n color 0.2s ease,\n transform 0.1s ease;\n -webkit-tap-highlight-color: transparent;\n user-select: none;\n }\n\n .bottom-nav-item:focus {\n outline: none;\n }\n\n .bottom-nav-item:focus-visible {\n outline: 2px solid var(--bottom-nav-text-active);\n outline-offset: -2px;\n border-radius: 4px;\n }\n\n .bottom-nav-item:active:not([disabled]) {\n transform: scale(0.95);\n }\n\n .bottom-nav-item[aria-selected='true'],\n .bottom-nav-item.active {\n color: var(--bottom-nav-text-active);\n }\n\n .bottom-nav-item[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n }\n\n .bottom-nav-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--bottom-nav-icon-size);\n height: var(--bottom-nav-icon-size);\n flex-shrink: 0;\n }\n\n .bottom-nav-icon ::slotted(*),\n .bottom-nav-icon svg,\n .bottom-nav-icon img {\n width: 100%;\n height: 100%;\n }\n\n .bottom-nav-label {\n font-size: var(--bottom-nav-label-size);\n font-weight: 500;\n line-height: 1.2;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n }\n\n /* Hide labels on very small screens */\n @media (max-width: 320px) {\n .bottom-nav-label {\n display: none;\n }\n\n .bottom-nav-icon {\n width: 28px;\n height: 28px;\n }\n }\n\n /* Color variants */\n :host([color='secondary']) {\n --bottom-nav-text-active: var(--color-secondary, #8b5cf6);\n }\n\n :host([color='success']) {\n --bottom-nav-text-active: var(--color-success, #22c55e);\n }\n\n :host([color='warning']) {\n --bottom-nav-text-active: var(--color-warning, #f59e0b);\n }\n\n :host([color='error']) {\n --bottom-nav-text-active: var(--color-error, #ef4444);\n }\n\n /* Badge indicator for items */\n .bottom-nav-badge {\n position: absolute;\n top: 4px;\n right: calc(50% - 18px);\n min-width: 8px;\n height: 8px;\n background: var(--color-error, #ef4444);\n border-radius: 50%;\n }\n\n .bottom-nav-item-wrapper {\n position: relative;\n flex: 1;\n display: flex;\n justify-content: center;\n max-width: 168px;\n }\n\n /* Slot for custom items */\n ::slotted(el-dm-bottom-navigation-item) {\n flex: 1;\n }\n`;\n\nexport class ElDmBottomNavigation extends BaseElement {\n static properties = {\n items: { type: Array, reflect: false, default: [] },\n value: { type: String, reflect: true },\n color: { type: String, reflect: true, default: 'primary' },\n position: { type: String, reflect: true, default: 'fixed' },\n navLabel: { type: String, reflect: true, attribute: 'nav-label', default: 'Bottom navigation' },\n };\n\n /** Array of navigation items */\n declare items: BottomNavigationItem[];\n\n /** Currently selected item value */\n declare value: string;\n\n /** Color theme */\n declare color: 'primary' | 'secondary' | 'success' | 'warning' | 'error' | string;\n\n /** Position of the navigation bar */\n declare position: 'fixed' | 'static' | 'sticky';\n\n /** Accessible label for the navigation landmark */\n declare navLabel: string;\n\n constructor() {\n super();\n this.attachStyles(styles);\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener('click', this._handleClick.bind(this));\n this.addEventListener('keydown', this._handleKeydown.bind(this));\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('click', this._handleClick.bind(this));\n this.removeEventListener('keydown', this._handleKeydown.bind(this));\n }\n\n /**\n * Handle click events on nav items\n */\n private _handleClick(event: MouseEvent): void {\n const target = event.target as HTMLElement;\n const navItem = target.closest('[data-value]') as HTMLElement | null;\n\n if (!navItem || navItem.hasAttribute('disabled')) {\n return;\n }\n\n const value = navItem.dataset.value;\n if (value && value !== this.value) {\n const item = this.items.find((i) => i.value === value);\n this.value = value;\n this.emit('change', { value, item });\n }\n }\n\n /**\n * Handle keyboard navigation\n */\n private _handleKeydown(event: KeyboardEvent): void {\n const target = event.target as HTMLElement;\n if (!target.classList.contains('bottom-nav-item')) return;\n\n const items = Array.from(this.shadowRoot.querySelectorAll('.bottom-nav-item:not([disabled])'));\n const currentIndex = items.indexOf(target);\n\n let nextIndex = -1;\n\n switch (event.key) {\n case 'ArrowLeft':\n case 'ArrowUp':\n event.preventDefault();\n nextIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;\n break;\n case 'ArrowRight':\n case 'ArrowDown':\n event.preventDefault();\n nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;\n break;\n case 'Home':\n event.preventDefault();\n nextIndex = 0;\n break;\n case 'End':\n event.preventDefault();\n nextIndex = items.length - 1;\n break;\n case 'Enter':\n case ' ':\n event.preventDefault();\n target.click();\n return;\n }\n\n if (nextIndex >= 0 && items[nextIndex]) {\n (items[nextIndex] as HTMLElement).focus();\n }\n }\n\n /**\n * Render a single navigation item\n */\n private _renderItem(item: BottomNavigationItem): string {\n const isSelected = item.value === this.value;\n const Tag = item.href ? 'a' : 'button';\n const hrefAttr = item.href ? `href=\"${item.href}\"` : '';\n const disabledAttr = item.disabled ? 'disabled' : '';\n const typeAttr = Tag === 'button' ? 'type=\"button\"' : '';\n\n return `\n <div class=\"bottom-nav-item-wrapper\">\n <${Tag}\n class=\"bottom-nav-item\"\n part=\"item\"\n role=\"tab\"\n tabindex=\"${item.disabled ? '-1' : '0'}\"\n aria-selected=\"${isSelected}\"\n data-value=\"${item.value}\"\n ${hrefAttr}\n ${disabledAttr}\n ${typeAttr}\n >\n ${\n item.icon\n ? `\n <span class=\"bottom-nav-icon\" part=\"icon\">\n ${item.icon}\n </span>\n `\n : ''\n }\n <span class=\"bottom-nav-label\" part=\"label\">${item.label}</span>\n </${Tag}>\n </div>\n `;\n }\n\n render(): string {\n const hasItems = this.items && this.items.length > 0;\n\n return `\n <nav class=\"bottom-nav\" part=\"container\" role=\"tablist\" aria-label=\"${this.navLabel || 'Bottom navigation'}\">\n ${hasItems ? this.items.map((item) => this._renderItem(item)).join('') : '<slot></slot>'}\n </nav>\n `;\n }\n}\n",
|
|
6
6
|
"/**\n * @duskmoon-dev/el-bottom-navigation\n *\n * DuskMoon Bottom Navigation custom element\n */\n\nimport { ElDmBottomNavigation } from './el-dm-bottom-navigation.js';\n\nexport { ElDmBottomNavigation };\nexport type { BottomNavigationItem } from './el-dm-bottom-navigation.js';\n\n/**\n * Register the el-dm-bottom-navigation custom element\n *\n * @example\n * ```ts\n * import { register } from '@duskmoon-dev/el-bottom-navigation';\n * register();\n * ```\n */\nexport function register(): void {\n if (!customElements.get('el-dm-bottom-navigation')) {\n customElements.define('el-dm-bottom-navigation', ElDmBottomNavigation);\n }\n}\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": ";AAsBA;
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "EA50E9812C948AA164756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
package/dist/esm/register.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// src/el-dm-bottom-navigation.ts
|
|
2
|
-
import { BaseElement, css } from "@duskmoon-dev/el-
|
|
2
|
+
import { BaseElement, css } from "@duskmoon-dev/el-base";
|
|
3
|
+
import { css as bottomNavCSS } from "@duskmoon-dev/core/components/bottom-navigation";
|
|
4
|
+
var coreStyles = bottomNavCSS.replace(/@layer\s+components\s*\{/, "").replace(/\}\s*$/, "");
|
|
3
5
|
var styles = css`
|
|
4
6
|
:host {
|
|
5
7
|
--bottom-nav-height: 56px;
|
|
@@ -31,36 +33,27 @@ var styles = css`
|
|
|
31
33
|
position: sticky;
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
/* Import core bottom-navigation styles */
|
|
37
|
+
${coreStyles}
|
|
38
|
+
|
|
39
|
+
/* Override core's fixed positioning — :host handles it */
|
|
34
40
|
.bottom-nav {
|
|
35
|
-
|
|
36
|
-
align-items: center;
|
|
37
|
-
justify-content: space-around;
|
|
41
|
+
position: static;
|
|
38
42
|
height: var(--bottom-nav-height);
|
|
43
|
+
min-height: auto;
|
|
39
44
|
background: var(--bottom-nav-bg);
|
|
40
45
|
border-top: 1px solid var(--bottom-nav-border);
|
|
41
46
|
box-shadow: var(--bottom-nav-shadow);
|
|
42
|
-
padding: 0;
|
|
43
|
-
margin: 0;
|
|
44
|
-
/* Safe area for iOS devices */
|
|
45
|
-
padding-bottom: env(safe-area-inset-bottom, 0);
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
display: flex;
|
|
51
|
-
flex-direction: column;
|
|
52
|
-
align-items: center;
|
|
53
|
-
justify-content: center;
|
|
49
|
+
/* Override core item styles with our custom properties */
|
|
50
|
+
.bottom-nav-item {
|
|
54
51
|
gap: 2px;
|
|
55
52
|
padding: 6px 12px;
|
|
56
53
|
min-width: 0;
|
|
57
54
|
max-width: 168px;
|
|
58
55
|
height: 100%;
|
|
59
|
-
background: transparent;
|
|
60
|
-
border: none;
|
|
61
|
-
cursor: pointer;
|
|
62
56
|
color: var(--bottom-nav-text);
|
|
63
|
-
text-decoration: none;
|
|
64
57
|
transition:
|
|
65
58
|
color 0.2s ease,
|
|
66
59
|
transform 0.1s ease;
|
|
@@ -68,32 +61,32 @@ var styles = css`
|
|
|
68
61
|
user-select: none;
|
|
69
62
|
}
|
|
70
63
|
|
|
71
|
-
.nav-item:focus {
|
|
64
|
+
.bottom-nav-item:focus {
|
|
72
65
|
outline: none;
|
|
73
66
|
}
|
|
74
67
|
|
|
75
|
-
.nav-item:focus-visible {
|
|
68
|
+
.bottom-nav-item:focus-visible {
|
|
76
69
|
outline: 2px solid var(--bottom-nav-text-active);
|
|
77
70
|
outline-offset: -2px;
|
|
78
71
|
border-radius: 4px;
|
|
79
72
|
}
|
|
80
73
|
|
|
81
|
-
.nav-item:active:not([disabled]) {
|
|
74
|
+
.bottom-nav-item:active:not([disabled]) {
|
|
82
75
|
transform: scale(0.95);
|
|
83
76
|
}
|
|
84
77
|
|
|
85
|
-
.nav-item[aria-selected='true'],
|
|
86
|
-
.nav-item.active {
|
|
78
|
+
.bottom-nav-item[aria-selected='true'],
|
|
79
|
+
.bottom-nav-item.active {
|
|
87
80
|
color: var(--bottom-nav-text-active);
|
|
88
81
|
}
|
|
89
82
|
|
|
90
|
-
.nav-item[disabled] {
|
|
83
|
+
.bottom-nav-item[disabled] {
|
|
91
84
|
opacity: 0.5;
|
|
92
85
|
cursor: not-allowed;
|
|
93
86
|
pointer-events: none;
|
|
94
87
|
}
|
|
95
88
|
|
|
96
|
-
.nav-icon {
|
|
89
|
+
.bottom-nav-icon {
|
|
97
90
|
display: flex;
|
|
98
91
|
align-items: center;
|
|
99
92
|
justify-content: center;
|
|
@@ -102,14 +95,14 @@ var styles = css`
|
|
|
102
95
|
flex-shrink: 0;
|
|
103
96
|
}
|
|
104
97
|
|
|
105
|
-
.nav-icon ::slotted(*),
|
|
106
|
-
.nav-icon svg,
|
|
107
|
-
.nav-icon img {
|
|
98
|
+
.bottom-nav-icon ::slotted(*),
|
|
99
|
+
.bottom-nav-icon svg,
|
|
100
|
+
.bottom-nav-icon img {
|
|
108
101
|
width: 100%;
|
|
109
102
|
height: 100%;
|
|
110
103
|
}
|
|
111
104
|
|
|
112
|
-
.nav-label {
|
|
105
|
+
.bottom-nav-label {
|
|
113
106
|
font-size: var(--bottom-nav-label-size);
|
|
114
107
|
font-weight: 500;
|
|
115
108
|
line-height: 1.2;
|
|
@@ -121,11 +114,11 @@ var styles = css`
|
|
|
121
114
|
|
|
122
115
|
/* Hide labels on very small screens */
|
|
123
116
|
@media (max-width: 320px) {
|
|
124
|
-
.nav-label {
|
|
117
|
+
.bottom-nav-label {
|
|
125
118
|
display: none;
|
|
126
119
|
}
|
|
127
120
|
|
|
128
|
-
.nav-icon {
|
|
121
|
+
.bottom-nav-icon {
|
|
129
122
|
width: 28px;
|
|
130
123
|
height: 28px;
|
|
131
124
|
}
|
|
@@ -149,7 +142,7 @@ var styles = css`
|
|
|
149
142
|
}
|
|
150
143
|
|
|
151
144
|
/* Badge indicator for items */
|
|
152
|
-
.nav-
|
|
145
|
+
.bottom-nav-badge {
|
|
153
146
|
position: absolute;
|
|
154
147
|
top: 4px;
|
|
155
148
|
right: calc(50% - 18px);
|
|
@@ -159,7 +152,7 @@ var styles = css`
|
|
|
159
152
|
border-radius: 50%;
|
|
160
153
|
}
|
|
161
154
|
|
|
162
|
-
.nav-item-wrapper {
|
|
155
|
+
.bottom-nav-item-wrapper {
|
|
163
156
|
position: relative;
|
|
164
157
|
flex: 1;
|
|
165
158
|
display: flex;
|
|
@@ -178,7 +171,8 @@ class ElDmBottomNavigation extends BaseElement {
|
|
|
178
171
|
items: { type: Array, reflect: false, default: [] },
|
|
179
172
|
value: { type: String, reflect: true },
|
|
180
173
|
color: { type: String, reflect: true, default: "primary" },
|
|
181
|
-
position: { type: String, reflect: true, default: "fixed" }
|
|
174
|
+
position: { type: String, reflect: true, default: "fixed" },
|
|
175
|
+
navLabel: { type: String, reflect: true, attribute: "nav-label", default: "Bottom navigation" }
|
|
182
176
|
};
|
|
183
177
|
constructor() {
|
|
184
178
|
super();
|
|
@@ -209,9 +203,9 @@ class ElDmBottomNavigation extends BaseElement {
|
|
|
209
203
|
}
|
|
210
204
|
_handleKeydown(event) {
|
|
211
205
|
const target = event.target;
|
|
212
|
-
if (!target.classList.contains("nav-item"))
|
|
206
|
+
if (!target.classList.contains("bottom-nav-item"))
|
|
213
207
|
return;
|
|
214
|
-
const items = Array.from(this.shadowRoot.querySelectorAll(".nav-item:not([disabled])"));
|
|
208
|
+
const items = Array.from(this.shadowRoot.querySelectorAll(".bottom-nav-item:not([disabled])"));
|
|
215
209
|
const currentIndex = items.indexOf(target);
|
|
216
210
|
let nextIndex = -1;
|
|
217
211
|
switch (event.key) {
|
|
@@ -243,16 +237,16 @@ class ElDmBottomNavigation extends BaseElement {
|
|
|
243
237
|
items[nextIndex].focus();
|
|
244
238
|
}
|
|
245
239
|
}
|
|
246
|
-
_renderItem(item
|
|
240
|
+
_renderItem(item) {
|
|
247
241
|
const isSelected = item.value === this.value;
|
|
248
242
|
const Tag = item.href ? "a" : "button";
|
|
249
243
|
const hrefAttr = item.href ? `href="${item.href}"` : "";
|
|
250
244
|
const disabledAttr = item.disabled ? "disabled" : "";
|
|
251
245
|
const typeAttr = Tag === "button" ? 'type="button"' : "";
|
|
252
246
|
return `
|
|
253
|
-
<div class="nav-item-wrapper">
|
|
247
|
+
<div class="bottom-nav-item-wrapper">
|
|
254
248
|
<${Tag}
|
|
255
|
-
class="nav-item"
|
|
249
|
+
class="bottom-nav-item"
|
|
256
250
|
part="item"
|
|
257
251
|
role="tab"
|
|
258
252
|
tabindex="${item.disabled ? "-1" : "0"}"
|
|
@@ -263,11 +257,11 @@ class ElDmBottomNavigation extends BaseElement {
|
|
|
263
257
|
${typeAttr}
|
|
264
258
|
>
|
|
265
259
|
${item.icon ? `
|
|
266
|
-
<span class="nav-icon" part="icon">
|
|
260
|
+
<span class="bottom-nav-icon" part="icon">
|
|
267
261
|
${item.icon}
|
|
268
262
|
</span>
|
|
269
263
|
` : ""}
|
|
270
|
-
<span class="nav-label" part="label">${item.label}</span>
|
|
264
|
+
<span class="bottom-nav-label" part="label">${item.label}</span>
|
|
271
265
|
</${Tag}>
|
|
272
266
|
</div>
|
|
273
267
|
`;
|
|
@@ -275,8 +269,8 @@ class ElDmBottomNavigation extends BaseElement {
|
|
|
275
269
|
render() {
|
|
276
270
|
const hasItems = this.items && this.items.length > 0;
|
|
277
271
|
return `
|
|
278
|
-
<nav class="bottom-nav" part="container" role="tablist" aria-label="Bottom navigation">
|
|
279
|
-
${hasItems ? this.items.map((item
|
|
272
|
+
<nav class="bottom-nav" part="container" role="tablist" aria-label="${this.navLabel || "Bottom navigation"}">
|
|
273
|
+
${hasItems ? this.items.map((item) => this._renderItem(item)).join("") : "<slot></slot>"}
|
|
280
274
|
</nav>
|
|
281
275
|
`;
|
|
282
276
|
}
|
|
@@ -292,5 +286,5 @@ function register() {
|
|
|
292
286
|
// src/register.ts
|
|
293
287
|
register();
|
|
294
288
|
|
|
295
|
-
//# debugId=
|
|
289
|
+
//# debugId=DF7F5FE13A481E4164756E2164756E21
|
|
296
290
|
//# sourceMappingURL=register.js.map
|