UNPKG

ranui

Version:

A framework-agnostic Web Components UI library built on native custom elements, with TypeScript types, light/dark theming, SSR and PWA support.

176 lines (175 loc) 8.66 kB
import { T as o, _ as a, n as l } from "./factory-WDxu11V0.js"; import { t as h } from "./events-Cqz3ramM.js"; import "./builder-EH76YCFS.js"; import { a as s, i, n as r, o as d, t as u } from "./utils-U-9uH7EX.js"; import { a as v, n as b } from "./ssr-BzykYefm.js"; import { createSpeechRecognizer as g } from "ranuts/utils"; var _ = ':host{display:inline-flex}:host([hidden]){display:none}.ran-voice{display:inline-flex;touch-action:none;align-items:center;justify-content:center;border:1px solid var(--ran-voice-border-color, var(--ran-color-border));border-radius:var(--ran-voice-border-radius, var(--ran-radius-md));background:var(--ran-voice-background, var(--ran-color-bg));padding:0;width:var(--ran-voice-size, var(--ran-size-7));height:var(--ran-voice-size, var(--ran-size-7));color:var(--ran-voice-color, var(--ran-color-text-secondary));cursor:pointer}.ran-voice:hover:not(:disabled){border-color:var(--ran-voice-hover-border-color, var(--ran-color-border-hover));color:var(--ran-voice-hover-color, var(--ran-color-text))}.ran-voice:focus-visible{outline:2px solid var(--ran-voice-focus-color, var(--ran-color-primary));outline-offset:2px}.ran-voice:disabled{color:var(--ran-voice-disabled-color, var(--ran-color-text-disabled));cursor:not-allowed}:host([listening]) .ran-voice{border-color:var(--ran-voice-active-border-color, var(--ran-color-danger));background:var(--ran-voice-active-background, var(--ran-red-200));color:var(--ran-voice-active-color, var(--ran-color-danger))}:host([listening]) .ran-voice:after{position:absolute;border:2px solid var(--ran-voice-active-border-color, var(--ran-color-danger));border-radius:var(--ran-voice-border-radius, var(--ran-radius-md));width:var(--ran-voice-size, var(--ran-size-7));height:var(--ran-voice-size, var(--ran-size-7));content:"";animation:ran-voice-pulse 1.6s ease-out infinite}.ran-voice{position:relative}@keyframes ran-voice-pulse{0%{opacity:.7;transform:scale(1)}to{opacity:0;transform:scale(1.6)}}@media(prefers-reduced-motion:reduce){:host([listening]) .ran-voice:after{animation:none;opacity:0}}.ran-voice-hint{position:absolute;bottom:calc(100% + var(--ran-voice-hint-gap, var(--ran-space-2)));left:50%;z-index:1;transform:translate(-50%);border-radius:var(--ran-voice-hint-radius, var(--ran-radius-md));background:var(--ran-voice-hint-background, var(--ran-color-bg-elevated));box-shadow:var(--ran-voice-hint-shadow, var(--ran-shadow-menu));padding:var(--ran-voice-hint-padding, var(--ran-space-2) var(--ran-space-3));min-width:max-content;color:var(--ran-voice-hint-color, var(--ran-color-text-secondary));font-size:var(--ran-voice-hint-font-size, var(--ran-text-label-3));text-align:center;pointer-events:none}:host(:not([holding])) .ran-voice-hint{display:none}:host([cancelling]) .ran-voice-hint{color:var(--ran-voice-hint-cancel-color, var(--ran-color-danger))}:host([cancelling]) .ran-voice{border-color:var(--ran-voice-active-border-color, var(--ran-color-danger));background:var(--ran-voice-cancel-background, var(--ran-color-bg-subtle))}', p = class c extends b { _events = new h(); _shadowDom; _button; _icon; _hint; _recognizer = null; _holdPointer = null; _holdOrigin = 0; static get observedAttributes() { return [ "disabled", "label", "active-label", "hold-hint", "cancel-hint", "listening", "holding", "cancelling", "sheet" ]; } constructor() { super(), this._shadowDom = u(this, _); const t = o(), e = o(), n = l().class("ran-voice").attr("part", "button").attr("type", "button").attr("aria-pressed", "false").children(a("r-icon").ref(t).attr("name", "mic").attr("part", "icon").build(), a("span").class("ran-voice-hint").ref(e).attr("part", "hint").build()).build(); this._shadowDom.appendChild(n), this._button = n, this._icon = s(t, "r icon"), this._hint = s(e, "hint"); } get listening() { return this.hasAttribute("listening"); } get supported() { return this._ensureRecognizer().supported; } get lang() { return r(this, "lang") || document.documentElement.lang || navigator.language; } set lang(t) { i(this, "lang", t); } get continuous() { return r(this, "continuous", "true") !== "false"; } set continuous(t) { i(this, "continuous", t ? "true" : "false"); } get disabled() { return this.hasAttribute("disabled"); } set disabled(t) { t ? this.setAttribute("disabled", "") : this.removeAttribute("disabled"); } get label() { return r(this, "label", "Start voice input"); } set label(t) { i(this, "label", t); } get activeLabel() { return r(this, "active-label", "Stop voice input"); } set activeLabel(t) { i(this, "active-label", t); } get holdHint() { return r(this, "hold-hint", "Release to keep · slide up to cancel"); } set holdHint(t) { i(this, "hold-hint", t); } get cancelHint() { return r(this, "cancel-hint", "Release to cancel"); } set cancelHint(t) { i(this, "cancel-hint", t); } get sheet() { return r(this, "sheet"); } set sheet(t) { i(this, "sheet", t); } start() { this.disabled || this._ensureRecognizer().start(); } stop() { this._recognizer?.stop(); } abort() { this._recognizer?.abort(); } toggle() { const t = this._ensureRecognizer(); t.active ? t.stop() : this.start(); } connectedCallback() { this.handlerExternalCss(), this._events.on(this._button, "click", this._onClick), this._events.on(this._button, "pointerdown", this._onPointerDown), this._events.on(this._button, "pointermove", this._onPointerMove), this._events.on(this._button, "pointerup", this._onPointerUp), this._events.on(this._button, "pointercancel", this._onPointerCancel), this._events.on(this, "keydown", this._onKeydown), this._syncButton(), this.supported || (this.hidden = !0); } disconnectedCallback() { this._events.abort(), this._endHold(), this._recognizer?.abort(); } attributeChangedCallback(t, e, n) { e !== n && (t === "sheet" ? this.handlerExternalCss() : this._syncButton()); } handlerExternalCss = () => { d(this, this._shadowDom, "sheet", null, this.sheet); }; static CANCEL_DISTANCE = 48; _onClick = () => { this._holdPointer === null && this.toggle(); }; _onPointerDown = (t) => { t.pointerType !== "touch" || this.disabled || this.listening || (t.preventDefault(), this._holdPointer = t.pointerId, this._holdOrigin = t.clientY, this.setAttribute("holding", ""), this._button.setPointerCapture?.(t.pointerId), this._syncButton(), this.start()); }; _onPointerMove = (t) => { if (t.pointerId !== this._holdPointer) return; const e = this._holdOrigin - t.clientY > c.CANCEL_DISTANCE; e !== this.hasAttribute("cancelling") && (e ? this.setAttribute("cancelling", "") : this.removeAttribute("cancelling"), this._syncButton()); }; _onPointerUp = (t) => { if (t.pointerId !== this._holdPointer) return; const e = this.hasAttribute("cancelling"); this._endHold(), e ? this.abort() : this.stop(); }; _onPointerCancel = (t) => { t.pointerId === this._holdPointer && (this._endHold(), this.abort()); }; _endHold() { this._holdPointer !== null && this._button.releasePointerCapture?.(this._holdPointer), this._holdPointer = null, this.removeAttribute("holding"), this.removeAttribute("cancelling"), this._syncButton(); } _onKeydown = (t) => { t.key !== "Escape" || !this.listening || (t.stopPropagation(), this.abort()); }; _emit(t, e) { this.dispatchEvent(new CustomEvent(t, { detail: e, bubbles: !0, composed: !0 })); } _ensureRecognizer() { return this._recognizer ??= g({ lang: () => this.lang, continuous: this.continuous, onStart: () => { this.setAttribute("listening", ""), this._syncButton(), this._emit("voicestart"); }, onResult: (t, e) => { this._emit("voiceresult", { transcript: t, isFinal: e }); }, onError: (t) => { this._emit("voiceerror", t); }, onEnd: () => { this.removeAttribute("listening"), this._syncButton(), this._emit("voiceend"); } }), this._recognizer; } _syncButton() { const t = this.listening; this._button.disabled = this.disabled, this._button.setAttribute("aria-pressed", t ? "true" : "false"), this._button.setAttribute("aria-label", t ? this.activeLabel : this.label), this._icon.setAttribute("name", t ? "mic-off" : "mic"), this._hint.textContent = this.hasAttribute("cancelling") ? this.cancelHint : this.holdHint; } }; v("r-voice-button", p); export { p as t };