UNPKG

sober

Version:

Sober is a library of UI components designed with reference to Material You.

162 lines (161 loc) 4.59 kB
import { useElement, useProps } from './core/element.js'; import { Select } from './core/utils/select.js'; import './ripple.js'; const name = 's-navigation'; const props = useProps({ mode: ['bottom', 'rail'], $value: '' }); const style = /*css*/ ` :host{ display: flex; justify-content: center; align-items: center; overflow: hidden; background: var(--s-color-surface, ${"#F8F9FB" /* Theme.colorSurface */}); box-shadow: var(--s-elevation-level2, ${"0 2px 4px -1px rgba(0, 0, 0, .2), 0 4px 5px 0 rgba(0, 0, 0, .14), 0 1px 10px 0 rgba(0, 0, 0, .12)" /* Theme.elevationLevel2 */}); position: relative; padding-bottom: env(safe-area-inset-bottom); } :host([mode=rail]){ flex-direction: column; justify-content: flex-start; width: 80px; box-shadow: none; height: 100%; background: none; padding-bottom: 0; } ::slotted(s-navigation-item){ height: 64px; } :host([mode=rail]) ::slotted(s-navigation-item){ height: 72px; } :host([mode=rail]) ::slotted(s-icon-button[slot=start]){ width: 56px; height: 56px; margin: 16px 0 8px 0; border-radius: 12px; } :host([mode=rail]) ::slotted([slot=end]){ flex-grow: 1; } `; const template = /*html*/ ` <slot name="start"></slot> <slot id="slot"></slot> <slot name="end"></slot> `; export class Navigation extends useElement({ style, template, props, setup(shadowRoot) { const slot = shadowRoot.querySelector('#slot'); const select = new Select({ context: this, class: NavigationItem, slot }); return { expose: { get options() { return select.list; }, get selectedIndex() { return select.selectedIndex; } }, value: { get: () => select.value, set: (value) => select.value = value } }; } }) { } const itemName = 's-navigation-item'; const itemProps = useProps({ selected: false, $value: '' }); const itemStyle = /*css*/ ` :host{ display: flex; align-items: center; justify-content: center; flex-direction: column; cursor: pointer; position: relative; font-size: .75rem; font-weight: 500; box-sizing: border-box; width: 100%; max-width: 80px; text-transform: capitalize; transition: color var(--s-motion-duration-short4, ${"400ms" /* Theme.motionDurationMedium4 */}) var(--s-motion-easing-emphasized, ${"cubic-bezier(0.2, 0, 0, 1.0)" /* Theme.motionEasingEmphasized */}); color: var(--s-color-on-surface, ${"#191C1E" /* Theme.colorOnSurface */}); } :host([selected=true]){ color: var(--s-color-primary, ${"#006782" /* Theme.colorPrimary */}); } .icon{ position: relative; display: flex; justify-content: center; align-items: center; height: 28px; width: 48px; border-radius: 14px; transition: background-color var(--s-motion-duration-short4, ${"400ms" /* Theme.motionDurationMedium4 */}) var(--s-motion-easing-emphasized, ${"cubic-bezier(0.2, 0, 0, 1.0)" /* Theme.motionEasingEmphasized */}); } :host([selected=true]) .icon{ background: var(--s-color-secondary-container, ${"#CFE6F1" /* Theme.colorSecondaryContainer */}); } ::slotted(*){ flex-shrink: 0; } ::slotted(svg){ color: var(--s-color-on-surface-variant, ${"#40484C" /* Theme.colorOnSurfaceVariant */}); fill: currentColor; width: 24px; height: 24px; } :host([selected=true]) ::slotted(:is(svg, s-icon)){ color: currentColor; } ::slotted([slot=badge]){ position: absolute; right: 4px; top: 0; } ::slotted([slot=text]){ margin-top: 4px; } `; const itemTemplate = /*html*/ ` <s-ripple attached="true" class="icon" part="icon"> <slot name="icon"></slot> <slot name="badge"></slot> </s-ripple> <slot name="text"></slot> `; export class NavigationItem extends useElement({ style: itemStyle, template: itemTemplate, props: itemProps, setup() { this.addEventListener('click', () => { if (this.selected) return; if (!(this.parentNode instanceof Navigation)) return; this.dispatchEvent(new Event(`${name}:select`, { bubbles: true })); }); return { selected: () => { if (!(this.parentNode instanceof Navigation)) return; this.dispatchEvent(new Event(`${name}:update`, { bubbles: true })); } }; } }) { } Navigation.define(name); NavigationItem.define(itemName);