luxen-ui 0.24.0 → 0.26.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 +1 -0
- package/bin/cli.mjs +16 -1
- package/cdn/custom-elements.json +295 -169
- package/cdn/elements/alert-dialog/alert-dialog.d.ts +3 -2
- package/cdn/elements/alert-dialog/alert-dialog.d.ts.map +1 -1
- package/cdn/elements/alert-dialog/alert-dialog.js.map +1 -1
- package/cdn/elements/avatar/avatar.js +1 -1
- package/cdn/elements/avatar/avatar.js.map +1 -1
- package/cdn/elements/carousel/carousel.js +1 -1
- package/cdn/elements/carousel/carousel.js.map +1 -1
- package/cdn/elements/combobox/combobox.d.ts.map +1 -1
- package/cdn/elements/combobox/combobox.js +2 -1
- package/cdn/elements/combobox/combobox.js.map +1 -1
- package/cdn/elements/dialog/dialog.d.ts +2 -1
- package/cdn/elements/dialog/dialog.d.ts.map +1 -1
- package/cdn/elements/dialog/dialog.js.map +1 -1
- package/cdn/elements/dialog/dialog.styles.js +1 -1
- package/cdn/elements/dialog/dialog.styles.js.map +1 -1
- package/cdn/elements/drawer/drawer.d.ts +7 -7
- package/cdn/elements/drawer/drawer.js +1 -1
- package/cdn/elements/drawer/drawer.js.map +1 -1
- package/cdn/elements/dropdown-item/dropdown-item.js +1 -1
- package/cdn/elements/dropdown-item/dropdown-item.js.map +1 -1
- package/cdn/elements/input-otp/input-otp.d.ts +2 -2
- package/cdn/elements/input-otp/input-otp.js.map +1 -1
- package/cdn/elements/prose-editor/prose-editor.js +1 -1
- package/cdn/elements/prose-editor/prose-editor.js.map +1 -1
- package/cdn/elements/rating/rating.js +1 -1
- package/cdn/elements/rating/rating.js.map +1 -1
- package/cdn/elements/select/select.d.ts.map +1 -1
- package/cdn/elements/select/select.js +2 -1
- package/cdn/elements/select/select.js.map +1 -1
- package/cdn/elements/slider/slider.js +1 -1
- package/cdn/elements/slider/slider.js.map +1 -1
- package/cdn/elements/stories-viewer/stories-viewer.js +1 -1
- package/cdn/elements/stories-viewer/stories-viewer.js.map +1 -1
- package/cdn/elements/table/table.meta.d.ts +55 -0
- package/cdn/elements/table/table.meta.d.ts.map +1 -0
- package/cdn/elements/table/table.meta.js +0 -0
- package/cdn/elements/tag/tag.js +1 -1
- package/cdn/elements/tag/tag.js.map +1 -1
- package/cdn/elements/tree/tree.js +1 -1
- package/cdn/elements/tree/tree.js.map +1 -1
- package/cdn/elements/tree-item/tree-item.js +1 -1
- package/cdn/elements/tree-item/tree-item.js.map +1 -1
- package/cdn/shared/controllers/listbox-nav.d.ts +7 -0
- package/cdn/shared/controllers/listbox-nav.d.ts.map +1 -1
- package/cdn/shared/controllers/listbox-nav.js +1 -1
- package/cdn/shared/controllers/listbox-nav.js.map +1 -1
- package/cdn/shared/styles/button-core.styles.js +1 -1
- package/cdn/shared/styles/button-core.styles.js.map +1 -1
- package/cdn/shared/styles/checkbox-appearance.styles.js +1 -1
- package/cdn/shared/styles/checkbox-appearance.styles.js.map +1 -1
- package/cdn/shared/styles/picker-core.styles.js +1 -1
- package/cdn/shared/styles/picker-core.styles.js.map +1 -1
- package/cdn/standalone.css +392 -86
- package/cdn/standalone.js +38 -28
- package/cdn/standalone.js.map +1 -1
- package/cdn/styles/base.css +11 -0
- package/cdn/styles/elements/breadcrumb.css +2 -2
- package/cdn/styles/elements/button.css +2 -2
- package/cdn/styles/elements/checkbox.css +2 -2
- package/cdn/styles/elements/close-button/circle.css +2 -2
- package/cdn/styles/elements/close-button/ring.css +2 -2
- package/cdn/styles/elements/close-button/square.css +2 -2
- package/cdn/styles/elements/disclosure.css +2 -2
- package/cdn/styles/elements/input-otp.css +20 -4
- package/cdn/styles/elements/input-stepper/default.css +2 -2
- package/cdn/styles/elements/input-stepper/pill.css +2 -2
- package/cdn/styles/elements/input-stepper/rounded.css +2 -2
- package/cdn/styles/elements/input.css +50 -17
- package/cdn/styles/elements/prose-editor.css +26 -11
- package/cdn/styles/elements/radio-group.css +2 -2
- package/cdn/styles/elements/radio.css +2 -2
- package/cdn/styles/elements/segmented-control.css +2 -2
- package/cdn/styles/elements/select.css +26 -5
- package/cdn/styles/elements/story.css +2 -2
- package/cdn/styles/elements/switch.css +2 -2
- package/cdn/styles/elements/table.css +186 -0
- package/cdn/styles/elements/tabs/enclosed.css +4 -4
- package/cdn/styles/elements/tabs/line.css +4 -4
- package/cdn/styles/elements/textarea.css +23 -9
- package/cdn/styles/preset.css +21 -2
- package/cdn/styles/tokens/aliases.css +6 -2
- package/cdn/styles/tokens/forms.css +4 -0
- package/cdn/styles/tokens.css +10 -2
- package/dist/css/base.css +11 -0
- package/dist/css/elements/breadcrumb.css +2 -2
- package/dist/css/elements/button.css +2 -2
- package/dist/css/elements/checkbox.css +2 -2
- package/dist/css/elements/close-button/circle.css +2 -2
- package/dist/css/elements/close-button/ring.css +2 -2
- package/dist/css/elements/close-button/square.css +2 -2
- package/dist/css/elements/disclosure.css +2 -2
- package/dist/css/elements/input-otp.css +20 -4
- package/dist/css/elements/input-stepper/default.css +2 -2
- package/dist/css/elements/input-stepper/pill.css +2 -2
- package/dist/css/elements/input-stepper/rounded.css +2 -2
- package/dist/css/elements/input.css +50 -17
- package/dist/css/elements/prose-editor.css +26 -11
- package/dist/css/elements/radio-group.css +2 -2
- package/dist/css/elements/radio.css +2 -2
- package/dist/css/elements/segmented-control.css +2 -2
- package/dist/css/elements/select.css +26 -5
- package/dist/css/elements/story.css +2 -2
- package/dist/css/elements/switch.css +2 -2
- package/dist/css/elements/table.css +186 -0
- package/dist/css/elements/tabs/enclosed.css +4 -4
- package/dist/css/elements/tabs/line.css +4 -4
- package/dist/css/elements/textarea.css +23 -9
- package/dist/css/preset.css +21 -2
- package/dist/css/tokens/aliases.css +6 -2
- package/dist/css/tokens/forms.css +4 -0
- package/dist/css/tokens.css +10 -2
- package/dist/custom-elements.json +295 -169
- package/dist/elements/alert-dialog/alert-dialog.d.ts +3 -2
- package/dist/elements/alert-dialog/alert-dialog.d.ts.map +1 -1
- package/dist/elements/alert-dialog/alert-dialog.js +3 -2
- package/dist/elements/avatar/avatar.css +2 -2
- package/dist/elements/carousel/carousel.css +18 -0
- package/dist/elements/combobox/combobox.css +9 -0
- package/dist/elements/combobox/combobox.d.ts.map +1 -1
- package/dist/elements/combobox/combobox.js +1 -0
- package/dist/elements/dialog/dialog.css +12 -0
- package/dist/elements/dialog/dialog.d.ts +2 -1
- package/dist/elements/dialog/dialog.d.ts.map +1 -1
- package/dist/elements/dialog/dialog.js +2 -1
- package/dist/elements/drawer/drawer.css +11 -1
- package/dist/elements/drawer/drawer.d.ts +7 -7
- package/dist/elements/drawer/drawer.js +7 -7
- package/dist/elements/dropdown-item/dropdown-item.css +5 -4
- package/dist/elements/input-otp/input-otp.d.ts +2 -2
- package/dist/elements/input-otp/input-otp.js +2 -2
- package/dist/elements/prose-editor/prose-editor.css +5 -3
- package/dist/elements/rating/rating.css +9 -10
- package/dist/elements/select/select.css +10 -9
- package/dist/elements/select/select.d.ts.map +1 -1
- package/dist/elements/select/select.js +1 -0
- package/dist/elements/slider/slider.css +5 -1
- package/dist/elements/stories-viewer/stories-viewer.css +10 -2
- package/dist/elements/table/table.meta.d.ts +55 -0
- package/dist/elements/table/table.meta.d.ts.map +1 -0
- package/dist/elements/table/table.meta.js +65 -0
- package/dist/elements/tag/tag.css +4 -4
- package/dist/elements/tree/tree.css +3 -3
- package/dist/elements/tree-item/tree-item.css +5 -3
- package/dist/metadata/alert-dialog.json +90 -85
- package/dist/metadata/alert.json +22 -22
- package/dist/metadata/avatar.json +26 -26
- package/dist/metadata/badge.json +13 -13
- package/dist/metadata/breadcrumb.json +9 -9
- package/dist/metadata/button.json +40 -40
- package/dist/metadata/carousel.json +133 -133
- package/dist/metadata/checkbox.json +13 -13
- package/dist/metadata/close-button.json +15 -15
- package/dist/metadata/color-scheme-icon.json +13 -13
- package/dist/metadata/combobox.json +77 -94
- package/dist/metadata/dialog.json +49 -44
- package/dist/metadata/disclosure.json +17 -17
- package/dist/metadata/divider.json +12 -12
- package/dist/metadata/drawer.json +61 -61
- package/dist/metadata/dropdown-item.json +50 -50
- package/dist/metadata/dropdown.json +32 -32
- package/dist/metadata/form-field.json +15 -15
- package/dist/metadata/icon.json +8 -8
- package/dist/metadata/index.json +1761 -1765
- package/dist/metadata/input-group.json +5 -5
- package/dist/metadata/input-otp.json +17 -17
- package/dist/metadata/input-stepper.json +24 -24
- package/dist/metadata/input.json +31 -31
- package/dist/metadata/popover.json +49 -49
- package/dist/metadata/progress.json +15 -15
- package/dist/metadata/prose-editor.json +110 -116
- package/dist/metadata/radio.json +15 -15
- package/dist/metadata/rating.json +25 -47
- package/dist/metadata/segmented-control.json +24 -46
- package/dist/metadata/select.json +81 -98
- package/dist/metadata/skeleton.json +12 -12
- package/dist/metadata/slider.json +60 -78
- package/dist/metadata/spinner.json +5 -5
- package/dist/metadata/sticky-bar.json +13 -13
- package/dist/metadata/stories-viewer.json +105 -105
- package/dist/metadata/stories.json +17 -17
- package/dist/metadata/story.json +41 -41
- package/dist/metadata/switch.json +9 -9
- package/dist/metadata/table.json +88 -0
- package/dist/metadata/tabs.json +26 -26
- package/dist/metadata/tag.json +50 -50
- package/dist/metadata/textarea.json +28 -28
- package/dist/metadata/toast.json +16 -16
- package/dist/metadata/tooltip.json +49 -49
- package/dist/metadata/tree-item.json +73 -73
- package/dist/metadata/tree.json +39 -39
- package/dist/shared/controllers/listbox-nav.d.ts +7 -0
- package/dist/shared/controllers/listbox-nav.d.ts.map +1 -1
- package/dist/shared/controllers/listbox-nav.js +12 -0
- package/dist/shared/styles/button-core.css +2 -2
- package/dist/shared/styles/checkbox-appearance.css +2 -2
- package/dist/shared/styles/picker-core.css +31 -7
- package/dist/templates/elements/alert-dialog.md +19 -18
- package/dist/templates/elements/alert.md +4 -4
- package/dist/templates/elements/avatar.md +9 -9
- package/dist/templates/elements/badge.md +4 -4
- package/dist/templates/elements/breadcrumb.md +4 -4
- package/dist/templates/elements/button.md +8 -8
- package/dist/templates/elements/carousel.md +32 -32
- package/dist/templates/elements/checkbox.md +4 -4
- package/dist/templates/elements/close-button.md +2 -2
- package/dist/templates/elements/color-scheme-icon.md +13 -13
- package/dist/templates/elements/combobox.md +19 -19
- package/dist/templates/elements/dialog.md +11 -10
- package/dist/templates/elements/disclosure.md +3 -3
- package/dist/templates/elements/divider.md +2 -2
- package/dist/templates/elements/drawer.md +15 -15
- package/dist/templates/elements/dropdown.md +15 -15
- package/dist/templates/elements/form-field.md +3 -3
- package/dist/templates/elements/icon.md +1 -1
- package/dist/templates/elements/input-otp.md +5 -5
- package/dist/templates/elements/input-stepper.md +4 -4
- package/dist/templates/elements/input.md +15 -8
- package/dist/templates/elements/popover.md +8 -8
- package/dist/templates/elements/progress.md +3 -3
- package/dist/templates/elements/prose-editor.md +38 -39
- package/dist/templates/elements/radio.md +6 -6
- package/dist/templates/elements/rating.md +5 -13
- package/dist/templates/elements/segmented-control.md +4 -4
- package/dist/templates/elements/select.md +23 -23
- package/dist/templates/elements/skeleton.md +2 -2
- package/dist/templates/elements/slider.md +13 -13
- package/dist/templates/elements/spinner.md +1 -1
- package/dist/templates/elements/sticky-bar.md +2 -2
- package/dist/templates/elements/stories-viewer.md +29 -29
- package/dist/templates/elements/stories.md +4 -4
- package/dist/templates/elements/story.md +11 -11
- package/dist/templates/elements/switch.md +5 -5
- package/dist/templates/elements/table.md +210 -0
- package/dist/templates/elements/tabs.md +4 -4
- package/dist/templates/elements/tag.md +14 -14
- package/dist/templates/elements/textarea.md +8 -8
- package/dist/templates/elements/toast.md +4 -4
- package/dist/templates/elements/tooltip.md +9 -9
- package/dist/templates/elements/tree-item.md +18 -18
- package/dist/templates/elements/tree.md +13 -13
- package/dist/templates/foundations/focus.md +303 -0
- package/elements.json +9 -0
- package/package.json +1 -1
- package/postcss-plugin-prefix.js +2 -2
- package/templates/SKILL.md.tpl +1 -0
- package/templates/choosing-elements.md.tpl +1 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{i as e}from"../../chunks/lit.js";import{r as t}from"../../chunks/static-html.js";import{n,r,t as i}from"../../chunks/decorate.js";import{t as a}from"../../chunks/query.js";import{t as o}from"../../chunks/lit-html.js";import{t as s}from"../../chunks/map.js";import{LuxenElement as c}from"../../shared/luxen-element.js";import{LocalizeController as l}from"../../shared/localize.js";import{cls as u,tagName as d}from"../../registry.js";import{staticTag as f}from"../../static-tag.js";import{ShowEvent as p}from"../../events/show.js";import{AfterShowEvent as m}from"../../events/after-show.js";import{HideEvent as h}from"../../events/hide.js";import{AfterHideEvent as g}from"../../events/after-hide.js";import _ from"../../shared/styles/host.styles.js";import v from"../../shared/styles/visually-hidden.styles.js";var y=e(`:host{--width:min(420px, 100vw);--aspect-ratio:9 / 16;--progress-color:white;--progress-bg:#ffffff59;--progress-gap:4px;--show-duration:.2s;--hide-duration:.2s;--backdrop:var(--l-backdrop-strong);display:contents}dialog{color:#fff;opacity:0;width:100vw;max-width:none;height:100dvh;max-height:none;transition-property:opacity,display,overlay;transition-duration:var(--hide-duration);transition-behavior:allow-discrete;background:0 0;border:0;place-items:center;margin:0;padding:0;position:fixed;inset:0;overflow:clip}dialog::backdrop{background:var(--backdrop)}dialog[open]{opacity:1;transition-duration:var(--show-duration);display:grid}@starting-style{dialog[open]{opacity:0}}.frame{width:var(--width);max-width:100vw;max-height:100dvh;aspect-ratio:var(--aspect-ratio);isolation:isolate;-webkit-user-select:none;user-select:none;touch-action:pan-y;background:#000;border-radius:.75rem;position:relative;overflow:clip}@media (width<=480px){.frame{aspect-ratio:auto;border-radius:0;width:100vw;height:100dvh}}.video{object-fit:cover;background:#000;width:100%;height:100%;position:absolute;inset:0}.spinner{z-index:1;color:#fff;pointer-events:none;opacity:0;width:2rem;height:2rem;margin:auto;transition:opacity .15s;position:absolute;inset:0}.spinner[data-state=loading]{opacity:1;transition-delay:.2s}@media (prefers-reduced-motion:reduce){.spinner{transition:none}}.progress{z-index:2;gap:var(--progress-gap);pointer-events:none;display:flex;position:absolute;top:.5rem;left:.5rem;right:.5rem}.progress-segment{background:var(--progress-bg);border-radius:999px;flex:1 1 0;height:2.5px;overflow:clip}.progress-fill{background:var(--progress-color);width:100%;height:100%;transform:scaleX(var(--p,0));transform-origin:0;transition:transform 80ms linear;display:block}.progress-segment[data-state=past] .progress-fill{transform:scaleX(1)}@media (prefers-reduced-motion:reduce){.progress-fill{transition:none}:host{--show-duration:0s;--hide-duration:0s}}.header{z-index:2;pointer-events:none;align-items:center;gap:.625rem;min-width:0;display:flex;position:absolute;top:1.25rem;left:.75rem;right:4rem}.header>::slotted(*){pointer-events:auto}.header-thumb{object-fit:cover;pointer-events:auto;border-radius:50%;flex-shrink:0;width:2rem;height:2rem}.header-text{pointer-events:auto;flex-direction:column;min-width:0;display:flex}.header-label{font-size:.875rem;font-weight:var(--l-font-weight-semibold);color:#fff;text-overflow:ellipsis;white-space:nowrap;line-height:1.2;overflow:hidden}.actions{z-index:2;pointer-events:none;flex-direction:column;gap:.75rem;display:flex;position:absolute;top:1.25rem;right:.75rem}.actions>::slotted(*),.actions .btn{pointer-events:auto}.btn{color:#fff;cursor:pointer;background:#00000040;border:0;border-radius:999px;justify-content:center;align-items:center;width:2rem;height:2rem;padding:0;font-size:1rem;transition:background .15s;display:inline-flex;position:relative}.btn:hover{background:#00000073}.btn:focus-visible{outline:2px solid var(--progress-color);outline-offset:2px}.btn:before{content:"";position:absolute;inset:-6px}.btn-nav{z-index:2;width:2.25rem;height:2.25rem;font-size:1.25rem;position:absolute}.btn-nav:before{inset:-4px}.btn-previous,.btn-next{top:50%;transform:translateY(-50%)}@media (width>=768px){.btn-previous{left:calc(50% - var(--width) / 2 - 3rem)}.btn-next{right:calc(50% - var(--width) / 2 - 3rem)}}@media (width<=767px){.btn-previous,.btn-next{display:none}}.tap-zone{z-index:0;position:absolute;top:0;bottom:0}.tap-previous{width:30%;left:0}.tap-next{left:30%;right:0}.overlay{z-index:1;pointer-events:none;padding:1rem;position:absolute;inset:auto 0 0}.overlay>::slotted(*){pointer-events:auto}`),b=class extends Event{constructor(e,t){super(`story-change`,{bubbles:!1,composed:!1,cancelable:!1}),this.index=e,this.story=t}},x=class extends Event{constructor(e){super(`story-end`,{bubbles:!1,composed:!1,cancelable:!1}),this.index=e}},S=class extends Event{constructor(e,t){super(`chapter-change`,{bubbles:!1,composed:!1,cancelable:!1}),this.chapter=e,this.story=t}},C=class extends Event{constructor(e){super(`mute-change`,{bubbles:!1,composed:!1,cancelable:!1}),this.muted=e}},w=Symbol.for(`luxen-stories-viewer-scroll-lock`);if(typeof document<`u`&&!(w in document)){let e=new CSSStyleSheet;e.replaceSync(`html:has(${d(`stories-viewer`)}[data-modal]) { overflow: hidden; scrollbar-gutter: stable; }`),document.adoptedStyleSheets.push(e),Object.defineProperty(document,w,{value:e})}var T=class extends c{constructor(...e){super(...e),this._localize=new l(this),this.open=!1,this.index=0,this.muted=!0,this.loop=!1,this.autoAdvance=!0,this.lightDismiss=!0,this.chapter=0,this.stories=[],this.source=null,this._paused=!1,this._loading=!1,this._raf=0,this._holdTimer=0,this._pointerStart=null,this._segments=[],this._chapterStarts=[0],this._prefetchedSrc=null,this._onKeyDown=e=>{if(this.open)switch(e.key){case`ArrowLeft`:e.preventDefault(),this.previous();break;case`ArrowRight`:e.preventDefault(),this.next();break;case` `:e.preventDefault(),this._video.paused?this.play():this.pause();break;case`m`:case`M`:e.preventDefault(),this.muted=!this.muted,this.dispatchEvent(new C(this.muted))}},this._onFramePointerDown=e=>{e.target.closest(`button, a, [data-no-gesture]`)||(this._pointerStart={x:e.clientX,y:e.clientY,t:performance.now()},this._holdTimer=window.setTimeout(()=>this.pause(),200))},this._onFramePointerUp=e=>{clearTimeout(this._holdTimer);let t=this._pointerStart;if(this._pointerStart=null,!t)return;let n=e.clientX-t.x,r=e.clientY-t.y,i=performance.now()-t.t;i>=200&&Math.abs(n)<10&&Math.abs(r)<10?this._video.paused&&this.play():i<200&&Math.abs(n)<10&&Math.abs(r)<10?e.target.closest(`[data-tap]`)?.dataset.tap===`previous`?this.previous():this.next():Math.abs(n)>Math.abs(r)&&Math.abs(n)>50?n<0?this.nextStory():this.previousStory():r>50&&this.close()},this._tick=()=>{if(!this.open)return;let e=this._video,t=this._segments[this.chapter];if(e&&t){let n=this._chapterStarts[this.chapter]??0,r=this._chapterStarts[this.chapter+1]??e.duration,i=Math.max(0,r-n),a=i?Math.max(0,Math.min((e.currentTime-n)/i,1)):0;t.style.setProperty(`--p`,String(a)),a>=1&&this.chapter<this._lastChapter&&this._advance(1)}this._raf=requestAnimationFrame(this._tick)}}static{this.styles=[_,v,y]}connectedCallback(){super.connectedCallback(),this.addEventListener(`keydown`,this._onKeyDown)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener(`keydown`,this._onKeyDown),cancelAnimationFrame(this._raf),clearTimeout(this._holdTimer),this._clearPrefetchHints()}firstUpdated(){this._dialog.addEventListener(`cancel`,e=>this._onCancel(e)),this._dialog.addEventListener(`close`,()=>this._onNativeClose()),this._dialog.addEventListener(`click`,e=>this._onDialogClick(e)),this._video.addEventListener(`ended`,()=>this._onEnded()),this._video.addEventListener(`loadedmetadata`,()=>this._tick()),this._video.addEventListener(`play`,()=>this._paused=!1),this._video.addEventListener(`pause`,()=>this._paused=!0),this._video.addEventListener(`loadstart`,()=>this._loading=!0),this._video.addEventListener(`waiting`,()=>this._loading=!0),this._video.addEventListener(`playing`,()=>this._loading=!1),this._video.addEventListener(`canplay`,()=>this._loading=!1),this._video.addEventListener(`error`,()=>this._loading=!1),this._video.addEventListener(`timeupdate`,()=>this._maybePrefetchNext())}willUpdate(e){let t=e.has(`open`)&&this.open,n=e.has(`index`);if(t||n){let e=this.stories[this.index];e&&(this._chapterStarts=e.getChapterStarts(),this.chapter=0)}}updated(e){if(e.has(`open`)){if(this.open&&!this._dialog.open){if(!this.dispatchEvent(new p)){queueMicrotask(()=>{this.open=!1});return}this.toggleAttribute(`data-modal`,!0),this._dialog.showModal(),queueMicrotask(()=>this._loadCurrent()),this._emitAfter(`after-show`)}else if(!this.open&&this._dialog.open){if(!this.dispatchEvent(new h)){queueMicrotask(()=>{this.open=!0});return}this._dialog.close()}}if(e.has(`index`)&&this.open){queueMicrotask(()=>this._loadCurrent());let e=this.stories[this.index];e&&this.dispatchEvent(new b(this.index,e))}e.has(`chapter`)&&this.open&&!e.has(`index`)&&this._applyChapterSeek(),e.has(`muted`)&&this._video&&(this._video.muted=this.muted)}_onCancel(e){this.dispatchEvent(new h)||e.preventDefault()}_onNativeClose(){cancelAnimationFrame(this._raf),this._video.pause(),this._video.removeAttribute(`src`),this._video.load(),this._clearPrefetchHints();for(let e=0;e<=this.index&&e<this.stories.length;e++)this.stories[e].seen=!0;this._restoreFocus(),this.open=!1,this.removeAttribute(`data-modal`),this._emitAfter(`after-hide`)}_onDialogClick(e){this.lightDismiss&&e.target===this._dialog&&(this.open=!1)}_refreshSegmentRefs(){this._segments=Array.from(this.shadowRoot.querySelectorAll(`[part~="progress-fill"]`))}_resetSegmentFills(){this._refreshSegmentRefs();for(let e=0;e<this._segments.length;e++){let t=+(e<this.chapter);this._segments[e].style.setProperty(`--p`,String(t))}}_loadCurrent(){if(!this.open||!this._dialog.open)return;let e=this.stories[this.index];e&&(cancelAnimationFrame(this._raf),this._prefetchedSrc=null,this._syncSlots(e),this._video.muted=this.muted,this._video.loop=this.loop,this._video.src===e.src?this._video.currentTime=0:this._video.src=e.src,requestAnimationFrame(()=>this._resetSegmentFills()),this._video.play().catch(()=>{this.muted=!0,this._video.muted=!0,this._video.play().catch(()=>{})}))}_maybePrefetchNext(){let e=this._video;if(!e.duration||e.currentTime/e.duration<.5)return;let t=this.stories[this.index+1];if(!t?.src||this._prefetchedSrc===t.src)return;this._prefetchedSrc=t.src;let n=document.createElement(`link`);n.rel=`preload`,n.as=`video`,n.href=t.src,n.crossOrigin=`anonymous`,n.dataset.luxenStoriesPrefetch=``,document.head.appendChild(n)}_clearPrefetchHints(){let e=document.head.querySelectorAll(`link[data-luxen-stories-prefetch]`);for(let t of e)t.remove();this._prefetchedSrc=null}_applyChapterSeek(){let e=this._chapterStarts[this.chapter]??0;this._video.currentTime=e,this._resetSegmentFills();let t=this.stories[this.index];t&&this.dispatchEvent(new S(this.chapter,t))}get _lastChapter(){return Math.max(0,this._chapterStarts.length-1)}_syncSlots(e){for(let e of Array.from(this.children))e.dataset.storySlot!==void 0&&e.remove();for(let t of Array.from(e.children)){if(!t.getAttribute?.(`slot`))continue;let e=t.cloneNode(!0);e.dataset.storySlot=``,this.appendChild(e)}}_onEnded(){this.stories[this.index]&&this.dispatchEvent(new x(this.index)),this.loop?(this._video.currentTime=this._chapterStarts[0]??0,this.chapter=0,this._video.play().catch(()=>{})):this.autoAdvance&&(this.index<this.stories.length-1?this.index+=1:this.close())}_advance(e){if(e===1){this.chapter<this._lastChapter?this.chapter+=1:this.index<this.stories.length-1?this.index+=1:this.close();return}let t=this._chapterStarts[this.chapter]??0;if(this._video.currentTime>t+1)this._video.currentTime=t,this._resetSegmentFills();else if(this.chapter>0)--this.chapter;else if(this.index>0){let e=this.stories[this.index-1],t=Math.max(0,e.getChapterStarts().length-1);--this.index,queueMicrotask(()=>{this.chapter=t})}}openAt(e,t=0,n=null){this.stories=e,this.source=n,this.index=Math.max(0,Math.min(t,e.length-1)),this.open=!0}close(){this.open=!1}next(){this._advance(1)}previous(){this._advance(-1)}nextStory(){this.index<this.stories.length-1?this.index+=1:this.close()}previousStory(){this.index>0&&--this.index}play(){this._video?.play().catch(()=>{}),this._raf=requestAnimationFrame(this._tick)}pause(){this._video?.pause(),cancelAnimationFrame(this._raf)}async _emitAfter(e){await new Promise(e=>requestAnimationFrame(()=>e(null)));let t=this._dialog.getAnimations({subtree:!1});await Promise.all(t.map(e=>e.finished.catch(()=>{}))),e===`after-show`===this.open&&(this.dispatchEvent(e===`after-show`?new m:new g),e===`after-show`&&(this._raf=requestAnimationFrame(this._tick)))}_restoreFocus(){((this.source?.stories()??this.stories)[this.index]?.querySelector(`button[data-story-trigger]`))?.focus({preventScroll:!1})}render(){let e=this.stories.length,n=this.stories[this.index],r=this.index===0,i=this.index>=e-1,a=this._chapterStarts.length?this._chapterStarts:n?.getChapterStarts()??[0],c=f(`icon`),l=f(`spinner`);return t`
|
|
1
|
+
import{i as e}from"../../chunks/lit.js";import{r as t}from"../../chunks/static-html.js";import{n,r,t as i}from"../../chunks/decorate.js";import{t as a}from"../../chunks/query.js";import{t as o}from"../../chunks/lit-html.js";import{t as s}from"../../chunks/map.js";import{LuxenElement as c}from"../../shared/luxen-element.js";import{LocalizeController as l}from"../../shared/localize.js";import{cls as u,tagName as d}from"../../registry.js";import{staticTag as f}from"../../static-tag.js";import{ShowEvent as p}from"../../events/show.js";import{AfterShowEvent as m}from"../../events/after-show.js";import{HideEvent as h}from"../../events/hide.js";import{AfterHideEvent as g}from"../../events/after-hide.js";import _ from"../../shared/styles/host.styles.js";import v from"../../shared/styles/visually-hidden.styles.js";var y=e(`:host{--width:min(420px, 100vw);--aspect-ratio:9 / 16;--progress-color:white;--progress-bg:#ffffff59;--progress-gap:4px;--show-duration:.2s;--hide-duration:.2s;--backdrop:var(--l-backdrop-strong);display:contents}dialog{color:#fff;opacity:0;width:100vw;max-width:none;height:100dvh;max-height:none;transition-property:opacity,display,overlay;transition-duration:var(--hide-duration);transition-behavior:allow-discrete;background:0 0;border:0;place-items:center;margin:0;padding:0;position:fixed;inset:0;overflow:clip}dialog::backdrop{background:var(--backdrop)}dialog[open]{opacity:1;transition-duration:var(--show-duration);display:grid}@starting-style{dialog[open]{opacity:0}}.frame{width:var(--width);max-width:100vw;max-height:100dvh;aspect-ratio:var(--aspect-ratio);isolation:isolate;-webkit-user-select:none;user-select:none;touch-action:pan-y;background:#000;border-radius:.75rem;position:relative;overflow:clip}@media (width<=480px){.frame{aspect-ratio:auto;border-radius:0;width:100vw;height:100dvh}}.video{object-fit:cover;background:#000;width:100%;height:100%;position:absolute;inset:0}.spinner{z-index:1;color:#fff;pointer-events:none;opacity:0;width:2rem;height:2rem;margin:auto;transition:opacity .15s;position:absolute;inset:0}.spinner[data-state=loading]{opacity:1;transition-delay:.2s}@media (prefers-reduced-motion:reduce){.spinner{transition:none}}.progress{z-index:2;gap:var(--progress-gap);pointer-events:none;display:flex;position:absolute;top:.5rem;left:.5rem;right:.5rem}.progress-segment{background:var(--progress-bg);border-radius:999px;flex:1 1 0;height:2.5px;overflow:clip}.progress-fill{background:var(--progress-color);width:100%;height:100%;transform:scaleX(var(--p,0));transform-origin:0;transition:transform 80ms linear;display:block}.progress-segment[data-state=past] .progress-fill{transform:scaleX(1)}@media (prefers-reduced-motion:reduce){.progress-fill{transition:none}:host{--show-duration:0s;--hide-duration:0s}}.header{z-index:2;pointer-events:none;align-items:center;gap:.625rem;min-width:0;display:flex;position:absolute;top:1.25rem;left:.75rem;right:4rem}.header>::slotted(*){pointer-events:auto}.header-thumb{object-fit:cover;pointer-events:auto;border-radius:50%;flex-shrink:0;width:2rem;height:2rem}.header-text{pointer-events:auto;flex-direction:column;min-width:0;display:flex}.header-label{font-size:.875rem;font-weight:var(--l-font-weight-semibold);color:#fff;text-overflow:ellipsis;white-space:nowrap;line-height:1.2;overflow:hidden}.actions{z-index:2;pointer-events:none;flex-direction:column;gap:.75rem;display:flex;position:absolute;top:1.25rem;right:.75rem}.actions>::slotted(*),.actions .btn{pointer-events:auto}.btn{color:#fff;cursor:pointer;background:#00000040;border:0;border-radius:999px;justify-content:center;align-items:center;width:2rem;height:2rem;padding:0;font-size:1rem;transition:background .15s;display:inline-flex;position:relative}.btn:hover{background:#00000073}.btn:focus-visible{outline:var(--l-focus-ring-width) solid var(--progress-color);outline-offset:var(--l-focus-ring-offset);box-shadow:0 0 0 calc(var(--l-focus-ring-offset) + var(--l-focus-ring-width) * 2) #000000bf}.btn:before{content:"";position:absolute;inset:-6px}.btn-nav{z-index:2;width:2.25rem;height:2.25rem;font-size:1.25rem;position:absolute}.btn-nav:before{inset:-4px}.btn-previous,.btn-next{top:50%;transform:translateY(-50%)}@media (width>=768px){.btn-previous{left:calc(50% - var(--width) / 2 - 3rem)}.btn-next{right:calc(50% - var(--width) / 2 - 3rem)}}@media (width<=767px){.btn-previous,.btn-next{display:none}}.tap-zone{z-index:0;position:absolute;top:0;bottom:0}.tap-previous{width:30%;left:0}.tap-next{left:30%;right:0}.overlay{z-index:1;pointer-events:none;padding:1rem;position:absolute;inset:auto 0 0}.overlay>::slotted(*){pointer-events:auto}`),b=class extends Event{constructor(e,t){super(`story-change`,{bubbles:!1,composed:!1,cancelable:!1}),this.index=e,this.story=t}},x=class extends Event{constructor(e){super(`story-end`,{bubbles:!1,composed:!1,cancelable:!1}),this.index=e}},S=class extends Event{constructor(e,t){super(`chapter-change`,{bubbles:!1,composed:!1,cancelable:!1}),this.chapter=e,this.story=t}},C=class extends Event{constructor(e){super(`mute-change`,{bubbles:!1,composed:!1,cancelable:!1}),this.muted=e}},w=Symbol.for(`luxen-stories-viewer-scroll-lock`);if(typeof document<`u`&&!(w in document)){let e=new CSSStyleSheet;e.replaceSync(`html:has(${d(`stories-viewer`)}[data-modal]) { overflow: hidden; scrollbar-gutter: stable; }`),document.adoptedStyleSheets.push(e),Object.defineProperty(document,w,{value:e})}var T=class extends c{constructor(...e){super(...e),this._localize=new l(this),this.open=!1,this.index=0,this.muted=!0,this.loop=!1,this.autoAdvance=!0,this.lightDismiss=!0,this.chapter=0,this.stories=[],this.source=null,this._paused=!1,this._loading=!1,this._raf=0,this._holdTimer=0,this._pointerStart=null,this._segments=[],this._chapterStarts=[0],this._prefetchedSrc=null,this._onKeyDown=e=>{if(this.open)switch(e.key){case`ArrowLeft`:e.preventDefault(),this.previous();break;case`ArrowRight`:e.preventDefault(),this.next();break;case` `:e.preventDefault(),this._video.paused?this.play():this.pause();break;case`m`:case`M`:e.preventDefault(),this.muted=!this.muted,this.dispatchEvent(new C(this.muted))}},this._onFramePointerDown=e=>{e.target.closest(`button, a, [data-no-gesture]`)||(this._pointerStart={x:e.clientX,y:e.clientY,t:performance.now()},this._holdTimer=window.setTimeout(()=>this.pause(),200))},this._onFramePointerUp=e=>{clearTimeout(this._holdTimer);let t=this._pointerStart;if(this._pointerStart=null,!t)return;let n=e.clientX-t.x,r=e.clientY-t.y,i=performance.now()-t.t;i>=200&&Math.abs(n)<10&&Math.abs(r)<10?this._video.paused&&this.play():i<200&&Math.abs(n)<10&&Math.abs(r)<10?e.target.closest(`[data-tap]`)?.dataset.tap===`previous`?this.previous():this.next():Math.abs(n)>Math.abs(r)&&Math.abs(n)>50?n<0?this.nextStory():this.previousStory():r>50&&this.close()},this._tick=()=>{if(!this.open)return;let e=this._video,t=this._segments[this.chapter];if(e&&t){let n=this._chapterStarts[this.chapter]??0,r=this._chapterStarts[this.chapter+1]??e.duration,i=Math.max(0,r-n),a=i?Math.max(0,Math.min((e.currentTime-n)/i,1)):0;t.style.setProperty(`--p`,String(a)),a>=1&&this.chapter<this._lastChapter&&this._advance(1)}this._raf=requestAnimationFrame(this._tick)}}static{this.styles=[_,v,y]}connectedCallback(){super.connectedCallback(),this.addEventListener(`keydown`,this._onKeyDown)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener(`keydown`,this._onKeyDown),cancelAnimationFrame(this._raf),clearTimeout(this._holdTimer),this._clearPrefetchHints()}firstUpdated(){this._dialog.addEventListener(`cancel`,e=>this._onCancel(e)),this._dialog.addEventListener(`close`,()=>this._onNativeClose()),this._dialog.addEventListener(`click`,e=>this._onDialogClick(e)),this._video.addEventListener(`ended`,()=>this._onEnded()),this._video.addEventListener(`loadedmetadata`,()=>this._tick()),this._video.addEventListener(`play`,()=>this._paused=!1),this._video.addEventListener(`pause`,()=>this._paused=!0),this._video.addEventListener(`loadstart`,()=>this._loading=!0),this._video.addEventListener(`waiting`,()=>this._loading=!0),this._video.addEventListener(`playing`,()=>this._loading=!1),this._video.addEventListener(`canplay`,()=>this._loading=!1),this._video.addEventListener(`error`,()=>this._loading=!1),this._video.addEventListener(`timeupdate`,()=>this._maybePrefetchNext())}willUpdate(e){let t=e.has(`open`)&&this.open,n=e.has(`index`);if(t||n){let e=this.stories[this.index];e&&(this._chapterStarts=e.getChapterStarts(),this.chapter=0)}}updated(e){if(e.has(`open`)){if(this.open&&!this._dialog.open){if(!this.dispatchEvent(new p)){queueMicrotask(()=>{this.open=!1});return}this.toggleAttribute(`data-modal`,!0),this._dialog.showModal(),queueMicrotask(()=>this._loadCurrent()),this._emitAfter(`after-show`)}else if(!this.open&&this._dialog.open){if(!this.dispatchEvent(new h)){queueMicrotask(()=>{this.open=!0});return}this._dialog.close()}}if(e.has(`index`)&&this.open){queueMicrotask(()=>this._loadCurrent());let e=this.stories[this.index];e&&this.dispatchEvent(new b(this.index,e))}e.has(`chapter`)&&this.open&&!e.has(`index`)&&this._applyChapterSeek(),e.has(`muted`)&&this._video&&(this._video.muted=this.muted)}_onCancel(e){this.dispatchEvent(new h)||e.preventDefault()}_onNativeClose(){cancelAnimationFrame(this._raf),this._video.pause(),this._video.removeAttribute(`src`),this._video.load(),this._clearPrefetchHints();for(let e=0;e<=this.index&&e<this.stories.length;e++)this.stories[e].seen=!0;this._restoreFocus(),this.open=!1,this.removeAttribute(`data-modal`),this._emitAfter(`after-hide`)}_onDialogClick(e){this.lightDismiss&&e.target===this._dialog&&(this.open=!1)}_refreshSegmentRefs(){this._segments=Array.from(this.shadowRoot.querySelectorAll(`[part~="progress-fill"]`))}_resetSegmentFills(){this._refreshSegmentRefs();for(let e=0;e<this._segments.length;e++){let t=+(e<this.chapter);this._segments[e].style.setProperty(`--p`,String(t))}}_loadCurrent(){if(!this.open||!this._dialog.open)return;let e=this.stories[this.index];e&&(cancelAnimationFrame(this._raf),this._prefetchedSrc=null,this._syncSlots(e),this._video.muted=this.muted,this._video.loop=this.loop,this._video.src===e.src?this._video.currentTime=0:this._video.src=e.src,requestAnimationFrame(()=>this._resetSegmentFills()),this._video.play().catch(()=>{this.muted=!0,this._video.muted=!0,this._video.play().catch(()=>{})}))}_maybePrefetchNext(){let e=this._video;if(!e.duration||e.currentTime/e.duration<.5)return;let t=this.stories[this.index+1];if(!t?.src||this._prefetchedSrc===t.src)return;this._prefetchedSrc=t.src;let n=document.createElement(`link`);n.rel=`preload`,n.as=`video`,n.href=t.src,n.crossOrigin=`anonymous`,n.dataset.luxenStoriesPrefetch=``,document.head.appendChild(n)}_clearPrefetchHints(){let e=document.head.querySelectorAll(`link[data-luxen-stories-prefetch]`);for(let t of e)t.remove();this._prefetchedSrc=null}_applyChapterSeek(){let e=this._chapterStarts[this.chapter]??0;this._video.currentTime=e,this._resetSegmentFills();let t=this.stories[this.index];t&&this.dispatchEvent(new S(this.chapter,t))}get _lastChapter(){return Math.max(0,this._chapterStarts.length-1)}_syncSlots(e){for(let e of Array.from(this.children))e.dataset.storySlot!==void 0&&e.remove();for(let t of Array.from(e.children)){if(!t.getAttribute?.(`slot`))continue;let e=t.cloneNode(!0);e.dataset.storySlot=``,this.appendChild(e)}}_onEnded(){this.stories[this.index]&&this.dispatchEvent(new x(this.index)),this.loop?(this._video.currentTime=this._chapterStarts[0]??0,this.chapter=0,this._video.play().catch(()=>{})):this.autoAdvance&&(this.index<this.stories.length-1?this.index+=1:this.close())}_advance(e){if(e===1){this.chapter<this._lastChapter?this.chapter+=1:this.index<this.stories.length-1?this.index+=1:this.close();return}let t=this._chapterStarts[this.chapter]??0;if(this._video.currentTime>t+1)this._video.currentTime=t,this._resetSegmentFills();else if(this.chapter>0)--this.chapter;else if(this.index>0){let e=this.stories[this.index-1],t=Math.max(0,e.getChapterStarts().length-1);--this.index,queueMicrotask(()=>{this.chapter=t})}}openAt(e,t=0,n=null){this.stories=e,this.source=n,this.index=Math.max(0,Math.min(t,e.length-1)),this.open=!0}close(){this.open=!1}next(){this._advance(1)}previous(){this._advance(-1)}nextStory(){this.index<this.stories.length-1?this.index+=1:this.close()}previousStory(){this.index>0&&--this.index}play(){this._video?.play().catch(()=>{}),this._raf=requestAnimationFrame(this._tick)}pause(){this._video?.pause(),cancelAnimationFrame(this._raf)}async _emitAfter(e){await new Promise(e=>requestAnimationFrame(()=>e(null)));let t=this._dialog.getAnimations({subtree:!1});await Promise.all(t.map(e=>e.finished.catch(()=>{}))),e===`after-show`===this.open&&(this.dispatchEvent(e===`after-show`?new m:new g),e===`after-show`&&(this._raf=requestAnimationFrame(this._tick)))}_restoreFocus(){((this.source?.stories()??this.stories)[this.index]?.querySelector(`button[data-story-trigger]`))?.focus({preventScroll:!1})}render(){let e=this.stories.length,n=this.stories[this.index],r=this.index===0,i=this.index>=e-1,a=this._chapterStarts.length?this._chapterStarts:n?.getChapterStarts()??[0],c=f(`icon`),l=f(`spinner`);return t`
|
|
2
2
|
<dialog
|
|
3
3
|
part="dialog"
|
|
4
4
|
aria-label=${this._localize.term(`stories`)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stories-viewer.js","names":["unsafeCSS","rawStyles","hostStyles","visuallyHiddenStyles","map","nothing","_decorate","property","query","state","r","u","__decorate","e","A","o","host_styles_default","visually_hidden_styles_default","styles","StoryChangeEvent","index","story","StoryEndEvent","ChapterChangeEvent","chapter","MuteChangeEvent","muted","SCROLL_LOCK_SHEET","sheet","tagName","LuxenStoriesViewer","LuxenElement","_args","LocalizeController","start","dx","dy","elapsed","v","seg","end","span","p","openBecoming","changed","indexChanged","ShowEvent","HideEvent","i","nextStory","link","links","target","node","child","clone","direction","previousStory","previousLast","stories","source","anims","a","name","AfterShowEvent","AfterHideEvent","total","currentStory","isFirstStory","isLastStory","chapterStarts","iconTag","staticTag","spinnerTag","cls","n","r$1"],"sources":["../../../src/html/elements/stories-viewer/stories-viewer.css?inline","../../../src/html/elements/stories-viewer/stories-viewer.ts"],"sourcesContent":[":host {\n --width: min(420px, 100vw);\n --aspect-ratio: 9 / 16;\n --progress-color: white;\n --progress-bg: rgb(255 255 255 / 35%);\n --progress-gap: 4px;\n --show-duration: 200ms;\n --hide-duration: 200ms;\n /* Semi-transparent backdrop so the underlying page (e.g. a product page) stays visible behind the player, but with enough darkness to focus attention on the frame. Override with `--backdrop: …` or set to `oklch(0% 0 0 / 90%)` for an opaque blackout. */\n --backdrop: var(--l-backdrop-strong);\n\n display: contents;\n}\n\ndialog {\n position: fixed;\n inset: 0;\n width: 100vw;\n height: 100dvh;\n max-width: none;\n max-height: none;\n margin: 0;\n padding: 0;\n border: 0;\n background: transparent;\n color: white;\n overflow: clip;\n\n /* Center the frame at all times. `place-items` is on the base rule so it survives the close transition (only `display` and `opacity` are transitioned with `allow-discrete`; non-transitioned properties revert immediately when `[open]` is removed and would un-center the frame to top-left for one frame). */\n place-items: center;\n\n /* EXIT STATE */\n opacity: 0;\n\n transition-property: opacity, display, overlay;\n transition-duration: var(--hide-duration);\n transition-behavior: allow-discrete;\n\n &::backdrop {\n background: var(--backdrop);\n }\n\n /* OPEN STATE */\n &[open] {\n display: grid;\n opacity: 1;\n transition-duration: var(--show-duration);\n }\n\n /* BEFORE-OPEN STATE */\n @starting-style {\n &[open] {\n opacity: 0;\n }\n }\n}\n\n.frame {\n position: relative;\n width: var(--width);\n max-width: 100vw;\n max-height: 100dvh;\n aspect-ratio: var(--aspect-ratio);\n background: black;\n border-radius: 0.75rem;\n overflow: clip;\n isolation: isolate;\n user-select: none;\n -webkit-user-select: none;\n touch-action: pan-y;\n}\n\n@media (max-width: 480px) {\n .frame {\n width: 100vw;\n height: 100dvh;\n aspect-ratio: auto;\n border-radius: 0;\n }\n}\n\n.video {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n background: black;\n}\n\n/* Loading spinner — centered over the video. Delayed fade-in so a fast buffer never visibly flashes the spinner. */\n.spinner {\n position: absolute;\n inset: 0;\n margin: auto;\n z-index: 1;\n width: 2rem;\n height: 2rem;\n color: white;\n pointer-events: none;\n opacity: 0;\n transition: opacity 150ms ease;\n}\n\n.spinner[data-state='loading'] {\n opacity: 1;\n /* Only paint the spinner if loading actually exceeds 200ms. */\n transition-delay: 200ms;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .spinner {\n transition: none;\n }\n}\n\n.progress {\n position: absolute;\n top: 0.5rem;\n left: 0.5rem;\n right: 0.5rem;\n z-index: 2;\n display: flex;\n gap: var(--progress-gap);\n pointer-events: none;\n}\n\n.progress-segment {\n flex: 1 1 0;\n height: 2.5px;\n border-radius: 999px;\n background: var(--progress-bg);\n overflow: clip;\n}\n\n.progress-fill {\n display: block;\n width: 100%;\n height: 100%;\n background: var(--progress-color);\n transform: scaleX(var(--p, 0));\n transform-origin: left;\n transition: transform 80ms linear;\n}\n\n.progress-segment[data-state='past'] .progress-fill {\n transform: scaleX(1);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .progress-fill {\n transition: none;\n }\n :host {\n --show-duration: 0ms;\n --hide-duration: 0ms;\n }\n}\n\n/* Top-left header — story thumbnail + label (slot fallback) or consumer-supplied content. */\n.header {\n position: absolute;\n top: 1.25rem;\n left: 0.75rem;\n right: 4rem; /* leave room for the right-side action stack */\n z-index: 2;\n display: flex;\n align-items: center;\n gap: 0.625rem;\n min-width: 0;\n pointer-events: none;\n}\n\n.header > ::slotted(*) {\n pointer-events: auto;\n}\n\n.header-thumb {\n width: 2rem;\n height: 2rem;\n border-radius: 50%;\n object-fit: cover;\n flex-shrink: 0;\n pointer-events: auto;\n}\n\n.header-text {\n display: flex;\n flex-direction: column;\n min-width: 0;\n pointer-events: auto;\n}\n\n.header-label {\n font-size: 0.875rem;\n font-weight: var(--l-font-weight-semibold);\n line-height: 1.2;\n color: white;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Top-right vertical action stack — close, play/pause, mute. */\n.actions {\n position: absolute;\n top: 1.25rem;\n right: 0.75rem;\n z-index: 2;\n display: flex;\n flex-direction: column;\n /* 12px gap leaves room for the 6px hit-area extension on each adjacent button (see `.btn::before` below). */\n gap: 0.75rem;\n pointer-events: none;\n}\n\n.actions > ::slotted(*),\n.actions .btn {\n pointer-events: auto;\n}\n\n.btn {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n padding: 0;\n border: 0;\n border-radius: 999px;\n background: rgb(0 0 0 / 25%);\n color: white;\n font-size: 1rem;\n cursor: pointer;\n transition: background 150ms ease;\n\n &:hover {\n background: rgb(0 0 0 / 45%);\n }\n\n &:focus-visible {\n outline: 2px solid var(--progress-color);\n outline-offset: 2px;\n }\n\n /* WCAG 2.1 SC 2.5.5 / RGAA 13.4 — 44×44 hit area without changing the visible 32×32 disc. The pseudo carries no paint, only hit testing. */\n &::before {\n content: '';\n position: absolute;\n inset: -6px;\n }\n}\n\n.btn-nav {\n position: absolute;\n z-index: 2;\n width: 2.25rem;\n height: 2.25rem;\n font-size: 1.25rem;\n\n /* Chevrons start at 36×36 — 4px each side reaches 44. */\n &::before {\n inset: -4px;\n }\n}\n\n.btn-previous,\n.btn-next {\n top: 50%;\n transform: translateY(-50%);\n}\n\n/* Desktop ≥ 768px: chevrons sit outside the frame, on the dim backdrop. */\n@media (min-width: 768px) {\n .btn-previous {\n left: calc(50% - var(--width) / 2 - 3rem);\n }\n .btn-next {\n right: calc(50% - var(--width) / 2 - 3rem);\n }\n}\n\n/* Mobile < 768px: hide chevrons entirely — tap zones drive nav. */\n@media (max-width: 767px) {\n .btn-previous,\n .btn-next {\n display: none;\n }\n}\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🆃🅰🅿 zones (invisible click/tap targets — left = previous, rest = next)\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n.tap-zone {\n position: absolute;\n top: 0;\n bottom: 0;\n z-index: 0;\n}\n\n.tap-previous {\n left: 0;\n width: 30%;\n}\n\n.tap-next {\n left: 30%;\n right: 0;\n}\n\n.overlay {\n position: absolute;\n inset: auto 0 0 0;\n z-index: 1;\n padding: 1rem;\n pointer-events: none;\n}\n\n.overlay > ::slotted(*) {\n pointer-events: auto;\n}\n","import { nothing, unsafeCSS, type CSSResultGroup, type PropertyValues } from 'lit';\nimport { html } from 'lit/static-html.js';\nimport { property, query, state } from 'lit/decorators.js';\nimport { map } from 'lit/directives/map.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { LocalizeController } from '../../shared/localize.js';\nimport { cls, tagName } from '../../registry.js';\nimport { staticTag } from '../../static-tag.js';\nimport { ShowEvent, AfterShowEvent, HideEvent, AfterHideEvent } from '../../events/index.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport visuallyHiddenStyles from '../../shared/styles/visually-hidden.styles.js';\nimport type { LuxenStories } from '../stories/stories.js';\nimport type { LuxenStory } from '../story/story.js';\nimport rawStyles from './stories-viewer.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/** Fired when the active story changes. */\nexport class StoryChangeEvent extends Event {\n readonly index: number;\n readonly story: LuxenStory;\n constructor(index: number, story: LuxenStory) {\n super('story-change', { bubbles: false, composed: false, cancelable: false });\n this.index = index;\n this.story = story;\n }\n}\n\n/** Fired when the active story finishes playback. */\nexport class StoryEndEvent extends Event {\n readonly index: number;\n constructor(index: number) {\n super('story-end', { bubbles: false, composed: false, cancelable: false });\n this.index = index;\n }\n}\n\n/** Fired when the active chapter (within a story) changes. */\nexport class ChapterChangeEvent extends Event {\n readonly chapter: number;\n readonly story: LuxenStory;\n constructor(chapter: number, story: LuxenStory) {\n super('chapter-change', { bubbles: false, composed: false, cancelable: false });\n this.chapter = chapter;\n this.story = story;\n }\n}\n\n/** Fired when the mute state changes. */\nexport class MuteChangeEvent extends Event {\n readonly muted: boolean;\n constructor(muted: boolean) {\n super('mute-change', { bubbles: false, composed: false, cancelable: false });\n this.muted = muted;\n }\n}\n\ndeclare global {\n interface GlobalEventHandlersEventMap {\n 'story-change': StoryChangeEvent;\n 'story-end': StoryEndEvent;\n 'chapter-change': ChapterChangeEvent;\n 'mute-change': MuteChangeEvent;\n }\n}\n\nconst SCROLL_LOCK_SHEET = Symbol.for('luxen-stories-viewer-scroll-lock');\nif (typeof document !== 'undefined' && !(SCROLL_LOCK_SHEET in document)) {\n const sheet = new CSSStyleSheet();\n // Runs at import time, after the consumer has called `setPrefix()` (or the\n // vite-plugin baked it into registry.js) — same ordering `define()` relies on.\n sheet.replaceSync(\n `html:has(${tagName('stories-viewer')}[data-modal]) { overflow: hidden; scrollbar-gutter: stable; }`,\n );\n document.adoptedStyleSheets.push(sheet);\n Object.defineProperty(document, SCROLL_LOCK_SHEET, { value: sheet });\n}\n\n/**\n * @summary Fullscreen modal that plays a sequence of `<l-story>` videos with an\n * Instagram-style segmented progress bar, previous/next navigation, mute toggle,\n * and auto-advance.\n *\n * Linked to one or more `<l-stories>` rows via matching `id` ↔ `for`. Open\n * programmatically via `open()` or by clicking a thumbnail in a linked row.\n *\n * @slot cta - Default CTA overlay (e.g. shoppable card). Per-story `slot=\"cta\"` inside `<l-story>` overrides this when that story is active.\n * @slot header - Default header overlay (e.g. avatar + author). Per-story override available the same way.\n * @slot close - Override the default close button.\n *\n * @csspart dialog - The native `<dialog>` element.\n * @csspart frame - The aspect-ratio video frame.\n * @csspart progress - The progress bar wrapper.\n * @csspart progress-segment - A single progress segment.\n * @csspart progress-fill - The fill element inside an active segment.\n * @csspart video - The `<video>` element.\n * @csspart overlay - The overlay wrapper that hosts CTA/header slots.\n * @csspart header - The top-left header area (story thumbnail + label fallback, or consumer-supplied content via the `header` slot).\n * @csspart header-label - The default story label inside the header.\n * @csspart actions - The top-right vertical button stack (close, play/pause, mute).\n * @csspart button-close - The close button.\n * @csspart button-pause - The play/pause toggle.\n * @csspart button-mute - The mute toggle.\n * @csspart button-previous - The previous story button.\n * @csspart button-next - The next story button.\n * @csspart spinner - The loading spinner shown while the current video is buffering.\n *\n * @cssproperty --width - Frame width. Default `min(420px, 100vw)`.\n * @cssproperty --progress-color - Active progress fill color. Default `white`.\n * @cssproperty --progress-bg - Inactive progress segment background. Default `rgb(255 255 255 / 35%)`.\n * @cssproperty --progress-gap - Gap between segments. Default `4px`.\n * @cssproperty --show-duration - Open transition duration. Default `200ms`.\n * @cssproperty --hide-duration - Close transition duration. Default `200ms`.\n * @cssproperty --backdrop - Backdrop color. Default `var(--l-backdrop-strong)` — darker than `--l-backdrop` to focus attention on the immersive frame, but still translucent so the page stays perceptible.\n *\n * @event show - Fired when the viewer is about to open. Cancelable — call `event.preventDefault()` to keep it closed.\n * @event after-show - Fired after the open transition completes. Not cancelable.\n * @event hide - Fired when the viewer is about to close. Cancelable.\n * @event after-hide - Fired after the close transition completes. Not cancelable.\n * @event story-change - Fired when the active story changes. Properties: `index: number`, `story: LuxenStory`.\n * @event story-end - Fired when the active story finishes playback. Properties: `index: number`.\n * @event chapter-change - Fired when the active chapter (within a story) changes. Properties: `chapter: number`, `story: LuxenStory`.\n * @event mute-change - Fired when the mute state changes. Properties: `muted: boolean`.\n *\n * @customElement l-stories-viewer\n */\nexport class LuxenStoriesViewer extends LuxenElement {\n static override styles: CSSResultGroup = [hostStyles, visuallyHiddenStyles, styles];\n\n private _localize = new LocalizeController(this);\n\n /** Whether the viewer is open. */\n @property({ type: Boolean, reflect: true })\n open = false;\n\n /** Active story index (0-based). */\n @property({ type: Number, reflect: true })\n index = 0;\n\n /** Whether playback is muted. Defaults to `true` so autoplay always succeeds across browsers. The user can unmute via the dedicated button or the `m` keyboard shortcut. */\n @property({ type: Boolean, reflect: true })\n muted = true;\n\n /** Loop the active story instead of advancing. */\n @property({ type: Boolean, reflect: true })\n loop = false;\n\n /** Move to the next story when the current one ends; close after the last story. */\n @property({ type: Boolean, reflect: true, attribute: 'auto-advance' })\n autoAdvance = true;\n\n /** Close when the backdrop is clicked. */\n @property({ type: Boolean, reflect: true, attribute: 'light-dismiss' })\n lightDismiss = true;\n\n /** Active chapter index within the current story. Reflected. */\n @property({ type: Number, reflect: true })\n chapter = 0;\n\n /** Internal: the playlist set by the source `<l-stories>`. */\n stories: LuxenStory[] = [];\n\n /** Internal: the source `<l-stories>` element that opened the viewer. */\n source: LuxenStories | null = null;\n\n @query('dialog') private _dialog!: HTMLDialogElement;\n @query('video') private _video!: HTMLVideoElement;\n\n /** Tracks the underlying `<video>` paused state so the play/pause button stays in sync. */\n @state() private _paused = false;\n\n /** True while the video is buffering / waiting for data. Drives the loading spinner. */\n @state() private _loading = false;\n\n private _raf = 0;\n private _holdTimer = 0;\n private _pointerStart: { x: number; y: number; t: number } | null = null;\n private _segments: HTMLElement[] = [];\n private _chapterStarts: number[] = [0];\n /** Tracks the URL we've already injected a prefetch hint for so we don't duplicate. */\n private _prefetchedSrc: string | null = null;\n\n // --- Lifecycle ---\n\n override connectedCallback() {\n super.connectedCallback();\n this.addEventListener('keydown', this._onKeyDown);\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this.removeEventListener('keydown', this._onKeyDown);\n cancelAnimationFrame(this._raf);\n clearTimeout(this._holdTimer);\n this._clearPrefetchHints();\n }\n\n override firstUpdated() {\n this._dialog.addEventListener('cancel', (e) => this._onCancel(e));\n this._dialog.addEventListener('close', () => this._onNativeClose());\n this._dialog.addEventListener('click', (e) => this._onDialogClick(e));\n this._video.addEventListener('ended', () => this._onEnded());\n this._video.addEventListener('loadedmetadata', () => this._tick());\n this._video.addEventListener('play', () => (this._paused = false));\n this._video.addEventListener('pause', () => (this._paused = true));\n // Loading spinner state — shown during initial buffer and any subsequent stalls,\n // hidden as soon as the video is actually painting frames.\n this._video.addEventListener('loadstart', () => (this._loading = true));\n this._video.addEventListener('waiting', () => (this._loading = true));\n this._video.addEventListener('playing', () => (this._loading = false));\n this._video.addEventListener('canplay', () => (this._loading = false));\n this._video.addEventListener('error', () => (this._loading = false));\n // Prefetch the next story's video once the current one is past 50% so the\n // transition feels instantaneous. Idempotent via `_prefetchedSrc`.\n this._video.addEventListener('timeupdate', () => this._maybePrefetchNext());\n }\n\n override willUpdate(changed: PropertyValues<this>) {\n // Reset chapter state synchronously so it folds into the same update cycle —\n // avoids writing reactive props from updated() which would schedule a second update.\n const openBecoming = changed.has('open') && this.open;\n const indexChanged = changed.has('index');\n if (openBecoming || indexChanged) {\n const story = this.stories[this.index];\n if (story) {\n this._chapterStarts = story.getChapterStarts();\n this.chapter = 0;\n }\n }\n }\n\n override updated(changed: PropertyValues<this>) {\n if (changed.has('open')) {\n if (this.open && !this._dialog.open) {\n if (!this.dispatchEvent(new ShowEvent())) {\n // Defer the revert so it lands outside the current update cycle and\n // doesn't schedule a new update from inside updated().\n queueMicrotask(() => {\n this.open = false;\n });\n return;\n }\n this.toggleAttribute('data-modal', true);\n this._dialog.showModal();\n queueMicrotask(() => this._loadCurrent());\n void this._emitAfter('after-show');\n } else if (!this.open && this._dialog.open) {\n if (!this.dispatchEvent(new HideEvent())) {\n // Defer the revert so it lands outside the current update cycle and\n // doesn't schedule a new update from inside updated().\n queueMicrotask(() => {\n this.open = true;\n });\n return;\n }\n this._dialog.close();\n }\n }\n\n // Story change → reload current story (refreshes chapters, segments, slots, video).\n if (changed.has('index') && this.open) {\n queueMicrotask(() => this._loadCurrent());\n const story = this.stories[this.index];\n if (story) this.dispatchEvent(new StoryChangeEvent(this.index, story));\n }\n\n // Chapter change within the current story → seek + refresh segment fills.\n if (changed.has('chapter') && this.open && !changed.has('index')) {\n this._applyChapterSeek();\n }\n\n if (changed.has('muted') && this._video) {\n this._video.muted = this.muted;\n }\n }\n\n // --- Dialog event handlers ---\n\n private _onCancel(e: Event) {\n if (!this.dispatchEvent(new HideEvent())) {\n e.preventDefault();\n }\n }\n\n private _onNativeClose() {\n cancelAnimationFrame(this._raf);\n this._video.pause();\n this._video.removeAttribute('src');\n this._video.load();\n this._clearPrefetchHints();\n // Mark all stories up to and including this.index as seen.\n for (let i = 0; i <= this.index && i < this.stories.length; i++) {\n this.stories[i].seen = true;\n }\n this._restoreFocus();\n this.open = false;\n this.removeAttribute('data-modal');\n void this._emitAfter('after-hide');\n }\n\n private _onDialogClick(e: MouseEvent) {\n if (!this.lightDismiss) return;\n // Backdrop click: target is the <dialog>, not a child.\n if (e.target === this._dialog) {\n this.open = false;\n }\n }\n\n // --- Keyboard ---\n\n private _onKeyDown = (e: KeyboardEvent) => {\n if (!this.open) return;\n switch (e.key) {\n case 'ArrowLeft':\n e.preventDefault();\n this.previous();\n break;\n case 'ArrowRight':\n e.preventDefault();\n this.next();\n break;\n case ' ':\n e.preventDefault();\n if (this._video.paused) this.play();\n else this.pause();\n break;\n case 'm':\n case 'M':\n e.preventDefault();\n this.muted = !this.muted;\n this.dispatchEvent(new MuteChangeEvent(this.muted));\n break;\n }\n };\n\n // --- Pointer / touch (long-press to pause, tap-to-advance, swipe to close) ---\n\n private _onFramePointerDown = (e: PointerEvent) => {\n // Skip buttons, links, and explicitly-marked no-gesture areas.\n if ((e.target as HTMLElement).closest('button, a, [data-no-gesture]')) return;\n this._pointerStart = { x: e.clientX, y: e.clientY, t: performance.now() };\n this._holdTimer = window.setTimeout(() => this.pause(), 200);\n };\n\n private _onFramePointerUp = (e: PointerEvent) => {\n clearTimeout(this._holdTimer);\n const start = this._pointerStart;\n this._pointerStart = null;\n if (!start) return;\n\n const dx = e.clientX - start.x;\n const dy = e.clientY - start.y;\n const elapsed = performance.now() - start.t;\n const isTap = elapsed < 200 && Math.abs(dx) < 10 && Math.abs(dy) < 10;\n const wasLongPress = elapsed >= 200 && Math.abs(dx) < 10 && Math.abs(dy) < 10;\n const SWIPE = 50;\n\n if (wasLongPress) {\n if (this._video.paused) this.play();\n return;\n }\n\n if (isTap) {\n // Tap zones: chapter-aware nav. Left ~30% = previous chapter (or previous\n // story at the chapter boundary), anywhere else = next chapter (or next\n // story at the last chapter). Chevron buttons remain story-to-story.\n const tap = (e.target as HTMLElement).closest<HTMLElement>('[data-tap]');\n if (tap?.dataset.tap === 'previous') this.previous();\n else this.next();\n return;\n }\n\n if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > SWIPE) {\n if (dx < 0) this.nextStory();\n else this.previousStory();\n } else if (dy > SWIPE) {\n this.close();\n }\n };\n\n // --- Segments / progress (chapter-based) ---\n\n private _refreshSegmentRefs() {\n this._segments = Array.from(\n this.shadowRoot!.querySelectorAll<HTMLElement>('[part~=\"progress-fill\"]'),\n );\n }\n\n private _resetSegmentFills() {\n this._refreshSegmentRefs();\n for (let i = 0; i < this._segments.length; i++) {\n const p = i < this.chapter ? 1 : 0;\n this._segments[i].style.setProperty('--p', String(p));\n }\n }\n\n private _loadCurrent() {\n // Guard against a close racing the microtask: if the viewer is no longer\n // open, don't restart the video with stale state.\n if (!this.open || !this._dialog.open) return;\n const story = this.stories[this.index];\n if (!story) return;\n cancelAnimationFrame(this._raf);\n // Reset the prefetch tracker when the active story changes — the next\n // candidate has shifted and may need its own preload hint.\n this._prefetchedSrc = null;\n this._syncSlots(story);\n this._video.muted = this.muted;\n this._video.loop = this.loop;\n if (this._video.src !== story.src) {\n this._video.src = story.src;\n } else {\n this._video.currentTime = 0;\n }\n // Wait one frame for the new segment DOM to render before resetting fills.\n requestAnimationFrame(() => this._resetSegmentFills());\n this._video.play().catch(() => {\n // Autoplay-with-sound rejection: force muted, retry.\n this.muted = true;\n this._video.muted = true;\n this._video.play().catch(() => {});\n });\n }\n\n /**\n * Inject a `<link rel=\"preload\" as=\"video\">` for the next story once the\n * current video crosses 50% playback. Idempotent: tracked by `_prefetchedSrc`,\n * cleaned up on viewer close.\n */\n private _maybePrefetchNext() {\n const v = this._video;\n if (!v.duration || v.currentTime / v.duration < 0.5) return;\n const nextStory = this.stories[this.index + 1];\n if (!nextStory?.src) return;\n if (this._prefetchedSrc === nextStory.src) return;\n this._prefetchedSrc = nextStory.src;\n\n const link = document.createElement('link');\n link.rel = 'preload';\n link.as = 'video';\n link.href = nextStory.src;\n link.crossOrigin = 'anonymous';\n link.dataset.luxenStoriesPrefetch = '';\n document.head.appendChild(link);\n }\n\n /** Remove every prefetch hint we've injected. Called when the viewer closes. */\n private _clearPrefetchHints() {\n const links = document.head.querySelectorAll<HTMLLinkElement>(\n 'link[data-luxen-stories-prefetch]',\n );\n for (const link of links) link.remove();\n this._prefetchedSrc = null;\n }\n\n private _applyChapterSeek() {\n const target = this._chapterStarts[this.chapter] ?? 0;\n this._video.currentTime = target;\n this._resetSegmentFills();\n const story = this.stories[this.index];\n if (story) this.dispatchEvent(new ChapterChangeEvent(this.chapter, story));\n }\n\n private get _lastChapter() {\n return Math.max(0, this._chapterStarts.length - 1);\n }\n\n // Clone the active story's slot-marked children (cta, header) into the\n // viewer's own light DOM so the shadow root's `<slot name=\"cta\">` and\n // `<slot name=\"header\">` project them. Originals stay attached to the\n // story (hidden via CSS) and are reused on every `index` change.\n private _syncSlots(story: LuxenStory) {\n for (const node of Array.from(this.children)) {\n if ((node as HTMLElement).dataset.storySlot !== undefined) node.remove();\n }\n for (const child of Array.from(story.children)) {\n const slotName = (child as HTMLElement).getAttribute?.('slot');\n if (!slotName) continue;\n const clone = child.cloneNode(true) as HTMLElement;\n clone.dataset.storySlot = '';\n this.appendChild(clone);\n }\n }\n\n private _tick = () => {\n if (!this.open) return;\n const v = this._video;\n const seg = this._segments[this.chapter];\n if (v && seg) {\n const start = this._chapterStarts[this.chapter] ?? 0;\n const end = this._chapterStarts[this.chapter + 1] ?? v.duration;\n const span = Math.max(0, end - start);\n const p = span ? Math.max(0, Math.min((v.currentTime - start) / span, 1)) : 0;\n seg.style.setProperty('--p', String(p));\n // Auto-advance chapter when its segment is full and there's a next chapter in this story.\n // (The video element's `ended` event handles the last chapter via `_onEnded`.)\n if (p >= 1 && this.chapter < this._lastChapter) {\n this._advance(1);\n }\n }\n this._raf = requestAnimationFrame(this._tick);\n };\n\n private _onEnded() {\n // Last chapter of the current story finished.\n const story = this.stories[this.index];\n if (story) this.dispatchEvent(new StoryEndEvent(this.index));\n if (this.loop) {\n this._video.currentTime = this._chapterStarts[0] ?? 0;\n this.chapter = 0;\n this._video.play().catch(() => {});\n return;\n }\n if (!this.autoAdvance) return;\n if (this.index < this.stories.length - 1) {\n this.index = this.index + 1;\n } else {\n this.close();\n }\n }\n\n /** chapter-then-story navigation. */\n private _advance(direction: 1 | -1) {\n if (direction === 1) {\n if (this.chapter < this._lastChapter) {\n this.chapter = this.chapter + 1;\n } else if (this.index < this.stories.length - 1) {\n this.index = this.index + 1;\n } else {\n this.close();\n }\n return;\n }\n // direction === -1: Instagram pattern — restart the current chapter if past 1s in.\n const start = this._chapterStarts[this.chapter] ?? 0;\n if (this._video.currentTime > start + 1) {\n this._video.currentTime = start;\n this._resetSegmentFills();\n return;\n }\n if (this.chapter > 0) {\n this.chapter = this.chapter - 1;\n } else if (this.index > 0) {\n // Crossing into the previous story: load it, then jump to its last chapter.\n const previousStory = this.stories[this.index - 1];\n const previousLast = Math.max(0, previousStory.getChapterStarts().length - 1);\n this.index = this.index - 1;\n // After `index` updates, `_loadCurrent()` runs and resets `chapter` to 0.\n // Set `chapter` to the previous story's last chapter on the next microtask.\n queueMicrotask(() => {\n this.chapter = previousLast;\n });\n }\n }\n\n // --- Public API ---\n\n /** Open the viewer at the given index with an explicit playlist. */\n openAt(stories: LuxenStory[], index = 0, source: LuxenStories | null = null) {\n this.stories = stories;\n this.source = source;\n this.index = Math.max(0, Math.min(index, stories.length - 1));\n this.open = true;\n }\n\n close() {\n this.open = false;\n }\n\n /** Advance one chapter, or to the next story at the chapter boundary. */\n next() {\n this._advance(1);\n }\n\n /** Retreat one chapter, restart the current chapter past 1s in, or cross into the previous story. */\n previous() {\n this._advance(-1);\n }\n\n /** Jump to the next story, skipping any remaining chapters in the current story. */\n nextStory() {\n if (this.index < this.stories.length - 1) {\n this.index = this.index + 1;\n } else {\n this.close();\n }\n }\n\n /** Jump to the previous story regardless of current chapter. Lands on chapter 0 of the previous story. */\n previousStory() {\n if (this.index > 0) {\n this.index = this.index - 1;\n }\n }\n\n play() {\n this._video?.play().catch(() => {});\n this._raf = requestAnimationFrame(this._tick);\n }\n\n pause() {\n this._video?.pause();\n cancelAnimationFrame(this._raf);\n }\n\n // --- Helpers ---\n\n private async _emitAfter(name: 'after-show' | 'after-hide') {\n await new Promise((r) => requestAnimationFrame(() => r(null)));\n const anims = this._dialog.getAnimations({ subtree: false });\n await Promise.all(anims.map((a) => a.finished.catch(() => {})));\n if ((name === 'after-show') !== this.open) return;\n this.dispatchEvent(name === 'after-show' ? new AfterShowEvent() : new AfterHideEvent());\n if (name === 'after-show') {\n this._raf = requestAnimationFrame(this._tick);\n }\n }\n\n private _restoreFocus() {\n const stories = this.source?.stories() ?? this.stories;\n const story = stories[this.index];\n const trigger = story?.querySelector<HTMLButtonElement>('button[data-story-trigger]');\n trigger?.focus({ preventScroll: false });\n }\n\n override render() {\n const total = this.stories.length;\n const currentStory = this.stories[this.index];\n // Chevrons navigate story-to-story, so visibility depends on `index` only (not chapter).\n const isFirstStory = this.index === 0;\n const isLastStory = this.index >= total - 1;\n // Render one segment per chapter of the CURRENT story. Use `_chapterStarts` if present\n // (set after `_loadCurrent`), or fall back to the current story's parsed chapters.\n const chapterStarts = this._chapterStarts.length\n ? this._chapterStarts\n : (currentStory?.getChapterStarts() ?? [0]);\n const iconTag = staticTag('icon');\n const spinnerTag = staticTag('spinner');\n\n return html`\n <dialog\n part=\"dialog\"\n aria-label=${this._localize.term('stories')}\n >\n <div\n part=\"frame\"\n class=\"frame\"\n @pointerdown=${this._onFramePointerDown}\n @pointerup=${this._onFramePointerUp}\n @pointercancel=${this._onFramePointerUp}\n >\n <div\n part=\"progress\"\n class=\"progress\"\n role=\"progressbar\"\n aria-label=${this._localize.term('storyProgress')}\n aria-valuenow=${this.chapter + 1}\n aria-valuemin=\"1\"\n aria-valuemax=${chapterStarts.length || 1}\n >\n ${map(\n chapterStarts,\n (_, i) => html`\n <span\n part=\"progress-segment\"\n class=\"progress-segment\"\n data-state=${i < this.chapter ? 'past' : i === this.chapter ? 'active' : 'future'}\n >\n <span\n part=\"progress-fill\"\n class=\"progress-fill\"\n ></span>\n </span>\n `,\n )}\n </div>\n\n <header\n part=\"header\"\n class=\"header\"\n data-no-gesture\n >\n <slot name=\"header\">\n ${\n currentStory?.poster\n ? html`<img\n class=\"header-thumb\"\n src=${currentStory.poster}\n alt=\"\"\n />`\n : nothing\n }\n ${\n currentStory?.label\n ? html`<div class=\"header-text\">\n <div\n part=\"header-label\"\n class=\"header-label\"\n >\n ${currentStory.label}\n </div>\n </div>`\n : nothing\n }\n </slot>\n </header>\n\n <div\n part=\"actions\"\n class=\"actions\"\n data-no-gesture\n >\n <slot name=\"close\">\n <button\n type=\"button\"\n part=\"button-close\"\n class=\"btn btn-close\"\n aria-label=${this._localize.term('close')}\n @click=${() => this.close()}\n >\n <${iconTag} name=\"mdi:close\"></${iconTag}>\n </button>\n </slot>\n <button\n type=\"button\"\n part=\"button-pause\"\n class=\"btn btn-pause\"\n aria-label=${this._paused ? 'Play' : 'Pause'}\n aria-pressed=${this._paused ? 'true' : 'false'}\n @click=${() => (this._paused ? this.play() : this.pause())}\n >\n <${iconTag} name=${this._paused ? 'mdi:play' : 'mdi:pause'}></${iconTag}>\n </button>\n <button\n type=\"button\"\n part=\"button-mute\"\n class=\"btn btn-mute\"\n aria-label=${this.muted ? this._localize.term('unmute') : this._localize.term('mute')}\n aria-pressed=${this.muted ? 'false' : 'true'}\n @click=${() => {\n this.muted = !this.muted;\n this.dispatchEvent(new MuteChangeEvent(this.muted));\n }}\n >\n <${iconTag} name=${this.muted ? 'mdi:volume-off' : 'mdi:volume-high'}></${iconTag}>\n </button>\n </div>\n\n <video\n part=\"video\"\n class=\"video\"\n playsinline\n preload=\"metadata\"\n ?muted=${this.muted}\n ></video>\n\n <${spinnerTag}\n part=\"spinner\"\n class=\"spinner\"\n aria-label=${this._localize.term('loading')}\n data-no-gesture\n data-state=${this._loading ? 'loading' : 'idle'}\n ></${spinnerTag}>\n\n <!-- Tap zones: invisible overlays that translate clicks into previous/next.\n Sit BELOW header / overlay / chevron buttons in z-order, above the video. -->\n <div\n class=\"tap-zone tap-previous\"\n data-tap=\"previous\"\n aria-hidden=\"true\"\n ></div>\n <div\n class=\"tap-zone tap-next\"\n data-tap=\"next\"\n aria-hidden=\"true\"\n ></div>\n\n <div\n part=\"overlay\"\n class=\"overlay\"\n data-no-gesture\n >\n <slot name=\"cta\"></slot>\n </div>\n\n <div\n class=${cls('visually-hidden')}\n aria-live=\"polite\"\n >\n ${\n total\n ? `Story ${this.index + 1} of ${total}${\n this.stories[this.index]?.label ? ` — ${this.stories[this.index].label}` : ''\n }${chapterStarts.length > 1 ? ` — chapter ${this.chapter + 1} of ${chapterStarts.length}` : ''}`\n : ''\n }\n </div>\n </div>\n\n ${\n isFirstStory\n ? nothing\n : html`<button\n type=\"button\"\n part=\"button-previous\"\n class=\"btn btn-nav btn-previous\"\n aria-label=${this._localize.term('previousStory')}\n @click=${() => this.previousStory()}\n >\n <${iconTag} name=\"mdi:chevron-left\"></${iconTag}>\n </button>`\n }\n ${\n isLastStory\n ? nothing\n : html`<button\n type=\"button\"\n part=\"button-next\"\n class=\"btn btn-nav btn-next\"\n aria-label=${this._localize.term('nextStory')}\n @click=${() => this.nextStory()}\n >\n <${iconTag} name=\"mdi:chevron-right\"></${iconTag}>\n </button>`\n }\n </dialog>\n `;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stories-viewer.js","names":["unsafeCSS","rawStyles","hostStyles","visuallyHiddenStyles","map","nothing","_decorate","property","query","state","r","u","__decorate","e","A","o","host_styles_default","visually_hidden_styles_default","styles","StoryChangeEvent","index","story","StoryEndEvent","ChapterChangeEvent","chapter","MuteChangeEvent","muted","SCROLL_LOCK_SHEET","sheet","tagName","LuxenStoriesViewer","LuxenElement","_args","LocalizeController","start","dx","dy","elapsed","v","seg","end","span","p","openBecoming","changed","indexChanged","ShowEvent","HideEvent","i","nextStory","link","links","target","node","child","clone","direction","previousStory","previousLast","stories","source","anims","a","name","AfterShowEvent","AfterHideEvent","total","currentStory","isFirstStory","isLastStory","chapterStarts","iconTag","staticTag","spinnerTag","cls","n","r$1"],"sources":["../../../src/html/elements/stories-viewer/stories-viewer.css?inline","../../../src/html/elements/stories-viewer/stories-viewer.ts"],"sourcesContent":[":host {\n --width: min(420px, 100vw);\n --aspect-ratio: 9 / 16;\n --progress-color: white;\n --progress-bg: rgb(255 255 255 / 35%);\n --progress-gap: 4px;\n --show-duration: 200ms;\n --hide-duration: 200ms;\n /* Semi-transparent backdrop so the underlying page (e.g. a product page) stays visible behind the player, but with enough darkness to focus attention on the frame. Override with `--backdrop: …` or set to `oklch(0% 0 0 / 90%)` for an opaque blackout. */\n --backdrop: var(--l-backdrop-strong);\n\n display: contents;\n}\n\ndialog {\n position: fixed;\n inset: 0;\n width: 100vw;\n height: 100dvh;\n max-width: none;\n max-height: none;\n margin: 0;\n padding: 0;\n border: 0;\n background: transparent;\n color: white;\n overflow: clip;\n\n /* Center the frame at all times. `place-items` is on the base rule so it survives the close transition (only `display` and `opacity` are transitioned with `allow-discrete`; non-transitioned properties revert immediately when `[open]` is removed and would un-center the frame to top-left for one frame). */\n place-items: center;\n\n /* EXIT STATE */\n opacity: 0;\n\n transition-property: opacity, display, overlay;\n transition-duration: var(--hide-duration);\n transition-behavior: allow-discrete;\n\n &::backdrop {\n background: var(--backdrop);\n }\n\n /* OPEN STATE */\n &[open] {\n display: grid;\n opacity: 1;\n transition-duration: var(--show-duration);\n }\n\n /* BEFORE-OPEN STATE */\n @starting-style {\n &[open] {\n opacity: 0;\n }\n }\n}\n\n.frame {\n position: relative;\n width: var(--width);\n max-width: 100vw;\n max-height: 100dvh;\n aspect-ratio: var(--aspect-ratio);\n background: black;\n border-radius: 0.75rem;\n overflow: clip;\n isolation: isolate;\n user-select: none;\n -webkit-user-select: none;\n touch-action: pan-y;\n}\n\n@media (max-width: 480px) {\n .frame {\n width: 100vw;\n height: 100dvh;\n aspect-ratio: auto;\n border-radius: 0;\n }\n}\n\n.video {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n background: black;\n}\n\n/* Loading spinner — centered over the video. Delayed fade-in so a fast buffer never visibly flashes the spinner. */\n.spinner {\n position: absolute;\n inset: 0;\n margin: auto;\n z-index: 1;\n width: 2rem;\n height: 2rem;\n color: white;\n pointer-events: none;\n opacity: 0;\n transition: opacity 150ms ease;\n}\n\n.spinner[data-state='loading'] {\n opacity: 1;\n /* Only paint the spinner if loading actually exceeds 200ms. */\n transition-delay: 200ms;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .spinner {\n transition: none;\n }\n}\n\n.progress {\n position: absolute;\n top: 0.5rem;\n left: 0.5rem;\n right: 0.5rem;\n z-index: 2;\n display: flex;\n gap: var(--progress-gap);\n pointer-events: none;\n}\n\n.progress-segment {\n flex: 1 1 0;\n height: 2.5px;\n border-radius: 999px;\n background: var(--progress-bg);\n overflow: clip;\n}\n\n.progress-fill {\n display: block;\n width: 100%;\n height: 100%;\n background: var(--progress-color);\n transform: scaleX(var(--p, 0));\n transform-origin: left;\n transition: transform 80ms linear;\n}\n\n.progress-segment[data-state='past'] .progress-fill {\n transform: scaleX(1);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .progress-fill {\n transition: none;\n }\n :host {\n --show-duration: 0ms;\n --hide-duration: 0ms;\n }\n}\n\n/* Top-left header — story thumbnail + label (slot fallback) or consumer-supplied content. */\n.header {\n position: absolute;\n top: 1.25rem;\n left: 0.75rem;\n right: 4rem; /* leave room for the right-side action stack */\n z-index: 2;\n display: flex;\n align-items: center;\n gap: 0.625rem;\n min-width: 0;\n pointer-events: none;\n}\n\n.header > ::slotted(*) {\n pointer-events: auto;\n}\n\n.header-thumb {\n width: 2rem;\n height: 2rem;\n border-radius: 50%;\n object-fit: cover;\n flex-shrink: 0;\n pointer-events: auto;\n}\n\n.header-text {\n display: flex;\n flex-direction: column;\n min-width: 0;\n pointer-events: auto;\n}\n\n.header-label {\n font-size: 0.875rem;\n font-weight: var(--l-font-weight-semibold);\n line-height: 1.2;\n color: white;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Top-right vertical action stack — close, play/pause, mute. */\n.actions {\n position: absolute;\n top: 1.25rem;\n right: 0.75rem;\n z-index: 2;\n display: flex;\n flex-direction: column;\n /* 12px gap leaves room for the 6px hit-area extension on each adjacent button (see `.btn::before` below). */\n gap: 0.75rem;\n pointer-events: none;\n}\n\n.actions > ::slotted(*),\n.actions .btn {\n pointer-events: auto;\n}\n\n.btn {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n padding: 0;\n border: 0;\n border-radius: 999px;\n background: rgb(0 0 0 / 25%);\n color: white;\n font-size: 1rem;\n cursor: pointer;\n transition: background 150ms ease;\n\n &:hover {\n background: rgb(0 0 0 / 45%);\n }\n\n /* Two-color ring (WCAG technique C40): these buttons float over the story\n media, whose brightness is unknown, so no single color is sure to\n contrast. A light ring between two dark bands — the shadow fills the gap\n and edges the ring — reads on any frame. Width and offset follow the\n global tokens; in forced colors the shadow is dropped and the outline is\n repainted. */\n &:focus-visible {\n outline: var(--l-focus-ring-width) solid var(--progress-color);\n outline-offset: var(--l-focus-ring-offset);\n box-shadow: 0 0 0 calc(var(--l-focus-ring-offset) + var(--l-focus-ring-width) * 2)\n rgb(0 0 0 / 75%);\n }\n\n /* WCAG 2.1 SC 2.5.5 / RGAA 13.4 — 44×44 hit area without changing the visible 32×32 disc. The pseudo carries no paint, only hit testing. */\n &::before {\n content: '';\n position: absolute;\n inset: -6px;\n }\n}\n\n.btn-nav {\n position: absolute;\n z-index: 2;\n width: 2.25rem;\n height: 2.25rem;\n font-size: 1.25rem;\n\n /* Chevrons start at 36×36 — 4px each side reaches 44. */\n &::before {\n inset: -4px;\n }\n}\n\n.btn-previous,\n.btn-next {\n top: 50%;\n transform: translateY(-50%);\n}\n\n/* Desktop ≥ 768px: chevrons sit outside the frame, on the dim backdrop. */\n@media (min-width: 768px) {\n .btn-previous {\n left: calc(50% - var(--width) / 2 - 3rem);\n }\n .btn-next {\n right: calc(50% - var(--width) / 2 - 3rem);\n }\n}\n\n/* Mobile < 768px: hide chevrons entirely — tap zones drive nav. */\n@media (max-width: 767px) {\n .btn-previous,\n .btn-next {\n display: none;\n }\n}\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🆃🅰🅿 zones (invisible click/tap targets — left = previous, rest = next)\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n.tap-zone {\n position: absolute;\n top: 0;\n bottom: 0;\n z-index: 0;\n}\n\n.tap-previous {\n left: 0;\n width: 30%;\n}\n\n.tap-next {\n left: 30%;\n right: 0;\n}\n\n.overlay {\n position: absolute;\n inset: auto 0 0 0;\n z-index: 1;\n padding: 1rem;\n pointer-events: none;\n}\n\n.overlay > ::slotted(*) {\n pointer-events: auto;\n}\n","import { nothing, unsafeCSS, type CSSResultGroup, type PropertyValues } from 'lit';\nimport { html } from 'lit/static-html.js';\nimport { property, query, state } from 'lit/decorators.js';\nimport { map } from 'lit/directives/map.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { LocalizeController } from '../../shared/localize.js';\nimport { cls, tagName } from '../../registry.js';\nimport { staticTag } from '../../static-tag.js';\nimport { ShowEvent, AfterShowEvent, HideEvent, AfterHideEvent } from '../../events/index.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport visuallyHiddenStyles from '../../shared/styles/visually-hidden.styles.js';\nimport type { LuxenStories } from '../stories/stories.js';\nimport type { LuxenStory } from '../story/story.js';\nimport rawStyles from './stories-viewer.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/** Fired when the active story changes. */\nexport class StoryChangeEvent extends Event {\n readonly index: number;\n readonly story: LuxenStory;\n constructor(index: number, story: LuxenStory) {\n super('story-change', { bubbles: false, composed: false, cancelable: false });\n this.index = index;\n this.story = story;\n }\n}\n\n/** Fired when the active story finishes playback. */\nexport class StoryEndEvent extends Event {\n readonly index: number;\n constructor(index: number) {\n super('story-end', { bubbles: false, composed: false, cancelable: false });\n this.index = index;\n }\n}\n\n/** Fired when the active chapter (within a story) changes. */\nexport class ChapterChangeEvent extends Event {\n readonly chapter: number;\n readonly story: LuxenStory;\n constructor(chapter: number, story: LuxenStory) {\n super('chapter-change', { bubbles: false, composed: false, cancelable: false });\n this.chapter = chapter;\n this.story = story;\n }\n}\n\n/** Fired when the mute state changes. */\nexport class MuteChangeEvent extends Event {\n readonly muted: boolean;\n constructor(muted: boolean) {\n super('mute-change', { bubbles: false, composed: false, cancelable: false });\n this.muted = muted;\n }\n}\n\ndeclare global {\n interface GlobalEventHandlersEventMap {\n 'story-change': StoryChangeEvent;\n 'story-end': StoryEndEvent;\n 'chapter-change': ChapterChangeEvent;\n 'mute-change': MuteChangeEvent;\n }\n}\n\nconst SCROLL_LOCK_SHEET = Symbol.for('luxen-stories-viewer-scroll-lock');\nif (typeof document !== 'undefined' && !(SCROLL_LOCK_SHEET in document)) {\n const sheet = new CSSStyleSheet();\n // Runs at import time, after the consumer has called `setPrefix()` (or the\n // vite-plugin baked it into registry.js) — same ordering `define()` relies on.\n sheet.replaceSync(\n `html:has(${tagName('stories-viewer')}[data-modal]) { overflow: hidden; scrollbar-gutter: stable; }`,\n );\n document.adoptedStyleSheets.push(sheet);\n Object.defineProperty(document, SCROLL_LOCK_SHEET, { value: sheet });\n}\n\n/**\n * @summary Fullscreen modal that plays a sequence of `<l-story>` videos with an\n * Instagram-style segmented progress bar, previous/next navigation, mute toggle,\n * and auto-advance.\n *\n * Linked to one or more `<l-stories>` rows via matching `id` ↔ `for`. Open\n * programmatically via `open()` or by clicking a thumbnail in a linked row.\n *\n * @slot cta - Default CTA overlay (e.g. shoppable card). Per-story `slot=\"cta\"` inside `<l-story>` overrides this when that story is active.\n * @slot header - Default header overlay (e.g. avatar + author). Per-story override available the same way.\n * @slot close - Override the default close button.\n *\n * @csspart dialog - The native `<dialog>` element.\n * @csspart frame - The aspect-ratio video frame.\n * @csspart progress - The progress bar wrapper.\n * @csspart progress-segment - A single progress segment.\n * @csspart progress-fill - The fill element inside an active segment.\n * @csspart video - The `<video>` element.\n * @csspart overlay - The overlay wrapper that hosts CTA/header slots.\n * @csspart header - The top-left header area (story thumbnail + label fallback, or consumer-supplied content via the `header` slot).\n * @csspart header-label - The default story label inside the header.\n * @csspart actions - The top-right vertical button stack (close, play/pause, mute).\n * @csspart button-close - The close button.\n * @csspart button-pause - The play/pause toggle.\n * @csspart button-mute - The mute toggle.\n * @csspart button-previous - The previous story button.\n * @csspart button-next - The next story button.\n * @csspart spinner - The loading spinner shown while the current video is buffering.\n *\n * @cssproperty --width - Frame width. Default `min(420px, 100vw)`.\n * @cssproperty --progress-color - Active progress fill color. Default `white`.\n * @cssproperty --progress-bg - Inactive progress segment background. Default `rgb(255 255 255 / 35%)`.\n * @cssproperty --progress-gap - Gap between segments. Default `4px`.\n * @cssproperty --show-duration - Open transition duration. Default `200ms`.\n * @cssproperty --hide-duration - Close transition duration. Default `200ms`.\n * @cssproperty --backdrop - Backdrop color. Default `var(--l-backdrop-strong)` — darker than `--l-backdrop` to focus attention on the immersive frame, but still translucent so the page stays perceptible.\n *\n * @event show - Fired when the viewer is about to open. Cancelable — call `event.preventDefault()` to keep it closed.\n * @event after-show - Fired after the open transition completes. Not cancelable.\n * @event hide - Fired when the viewer is about to close. Cancelable.\n * @event after-hide - Fired after the close transition completes. Not cancelable.\n * @event story-change - Fired when the active story changes. Properties: `index: number`, `story: LuxenStory`.\n * @event story-end - Fired when the active story finishes playback. Properties: `index: number`.\n * @event chapter-change - Fired when the active chapter (within a story) changes. Properties: `chapter: number`, `story: LuxenStory`.\n * @event mute-change - Fired when the mute state changes. Properties: `muted: boolean`.\n *\n * @customElement l-stories-viewer\n */\nexport class LuxenStoriesViewer extends LuxenElement {\n static override styles: CSSResultGroup = [hostStyles, visuallyHiddenStyles, styles];\n\n private _localize = new LocalizeController(this);\n\n /** Whether the viewer is open. */\n @property({ type: Boolean, reflect: true })\n open = false;\n\n /** Active story index (0-based). */\n @property({ type: Number, reflect: true })\n index = 0;\n\n /** Whether playback is muted. Defaults to `true` so autoplay always succeeds across browsers. The user can unmute via the dedicated button or the `m` keyboard shortcut. */\n @property({ type: Boolean, reflect: true })\n muted = true;\n\n /** Loop the active story instead of advancing. */\n @property({ type: Boolean, reflect: true })\n loop = false;\n\n /** Move to the next story when the current one ends; close after the last story. */\n @property({ type: Boolean, reflect: true, attribute: 'auto-advance' })\n autoAdvance = true;\n\n /** Close when the backdrop is clicked. */\n @property({ type: Boolean, reflect: true, attribute: 'light-dismiss' })\n lightDismiss = true;\n\n /** Active chapter index within the current story. Reflected. */\n @property({ type: Number, reflect: true })\n chapter = 0;\n\n /** Internal: the playlist set by the source `<l-stories>`. */\n stories: LuxenStory[] = [];\n\n /** Internal: the source `<l-stories>` element that opened the viewer. */\n source: LuxenStories | null = null;\n\n @query('dialog') private _dialog!: HTMLDialogElement;\n @query('video') private _video!: HTMLVideoElement;\n\n /** Tracks the underlying `<video>` paused state so the play/pause button stays in sync. */\n @state() private _paused = false;\n\n /** True while the video is buffering / waiting for data. Drives the loading spinner. */\n @state() private _loading = false;\n\n private _raf = 0;\n private _holdTimer = 0;\n private _pointerStart: { x: number; y: number; t: number } | null = null;\n private _segments: HTMLElement[] = [];\n private _chapterStarts: number[] = [0];\n /** Tracks the URL we've already injected a prefetch hint for so we don't duplicate. */\n private _prefetchedSrc: string | null = null;\n\n // --- Lifecycle ---\n\n override connectedCallback() {\n super.connectedCallback();\n this.addEventListener('keydown', this._onKeyDown);\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this.removeEventListener('keydown', this._onKeyDown);\n cancelAnimationFrame(this._raf);\n clearTimeout(this._holdTimer);\n this._clearPrefetchHints();\n }\n\n override firstUpdated() {\n this._dialog.addEventListener('cancel', (e) => this._onCancel(e));\n this._dialog.addEventListener('close', () => this._onNativeClose());\n this._dialog.addEventListener('click', (e) => this._onDialogClick(e));\n this._video.addEventListener('ended', () => this._onEnded());\n this._video.addEventListener('loadedmetadata', () => this._tick());\n this._video.addEventListener('play', () => (this._paused = false));\n this._video.addEventListener('pause', () => (this._paused = true));\n // Loading spinner state — shown during initial buffer and any subsequent stalls,\n // hidden as soon as the video is actually painting frames.\n this._video.addEventListener('loadstart', () => (this._loading = true));\n this._video.addEventListener('waiting', () => (this._loading = true));\n this._video.addEventListener('playing', () => (this._loading = false));\n this._video.addEventListener('canplay', () => (this._loading = false));\n this._video.addEventListener('error', () => (this._loading = false));\n // Prefetch the next story's video once the current one is past 50% so the\n // transition feels instantaneous. Idempotent via `_prefetchedSrc`.\n this._video.addEventListener('timeupdate', () => this._maybePrefetchNext());\n }\n\n override willUpdate(changed: PropertyValues<this>) {\n // Reset chapter state synchronously so it folds into the same update cycle —\n // avoids writing reactive props from updated() which would schedule a second update.\n const openBecoming = changed.has('open') && this.open;\n const indexChanged = changed.has('index');\n if (openBecoming || indexChanged) {\n const story = this.stories[this.index];\n if (story) {\n this._chapterStarts = story.getChapterStarts();\n this.chapter = 0;\n }\n }\n }\n\n override updated(changed: PropertyValues<this>) {\n if (changed.has('open')) {\n if (this.open && !this._dialog.open) {\n if (!this.dispatchEvent(new ShowEvent())) {\n // Defer the revert so it lands outside the current update cycle and\n // doesn't schedule a new update from inside updated().\n queueMicrotask(() => {\n this.open = false;\n });\n return;\n }\n this.toggleAttribute('data-modal', true);\n this._dialog.showModal();\n queueMicrotask(() => this._loadCurrent());\n void this._emitAfter('after-show');\n } else if (!this.open && this._dialog.open) {\n if (!this.dispatchEvent(new HideEvent())) {\n // Defer the revert so it lands outside the current update cycle and\n // doesn't schedule a new update from inside updated().\n queueMicrotask(() => {\n this.open = true;\n });\n return;\n }\n this._dialog.close();\n }\n }\n\n // Story change → reload current story (refreshes chapters, segments, slots, video).\n if (changed.has('index') && this.open) {\n queueMicrotask(() => this._loadCurrent());\n const story = this.stories[this.index];\n if (story) this.dispatchEvent(new StoryChangeEvent(this.index, story));\n }\n\n // Chapter change within the current story → seek + refresh segment fills.\n if (changed.has('chapter') && this.open && !changed.has('index')) {\n this._applyChapterSeek();\n }\n\n if (changed.has('muted') && this._video) {\n this._video.muted = this.muted;\n }\n }\n\n // --- Dialog event handlers ---\n\n private _onCancel(e: Event) {\n if (!this.dispatchEvent(new HideEvent())) {\n e.preventDefault();\n }\n }\n\n private _onNativeClose() {\n cancelAnimationFrame(this._raf);\n this._video.pause();\n this._video.removeAttribute('src');\n this._video.load();\n this._clearPrefetchHints();\n // Mark all stories up to and including this.index as seen.\n for (let i = 0; i <= this.index && i < this.stories.length; i++) {\n this.stories[i].seen = true;\n }\n this._restoreFocus();\n this.open = false;\n this.removeAttribute('data-modal');\n void this._emitAfter('after-hide');\n }\n\n private _onDialogClick(e: MouseEvent) {\n if (!this.lightDismiss) return;\n // Backdrop click: target is the <dialog>, not a child.\n if (e.target === this._dialog) {\n this.open = false;\n }\n }\n\n // --- Keyboard ---\n\n private _onKeyDown = (e: KeyboardEvent) => {\n if (!this.open) return;\n switch (e.key) {\n case 'ArrowLeft':\n e.preventDefault();\n this.previous();\n break;\n case 'ArrowRight':\n e.preventDefault();\n this.next();\n break;\n case ' ':\n e.preventDefault();\n if (this._video.paused) this.play();\n else this.pause();\n break;\n case 'm':\n case 'M':\n e.preventDefault();\n this.muted = !this.muted;\n this.dispatchEvent(new MuteChangeEvent(this.muted));\n break;\n }\n };\n\n // --- Pointer / touch (long-press to pause, tap-to-advance, swipe to close) ---\n\n private _onFramePointerDown = (e: PointerEvent) => {\n // Skip buttons, links, and explicitly-marked no-gesture areas.\n if ((e.target as HTMLElement).closest('button, a, [data-no-gesture]')) return;\n this._pointerStart = { x: e.clientX, y: e.clientY, t: performance.now() };\n this._holdTimer = window.setTimeout(() => this.pause(), 200);\n };\n\n private _onFramePointerUp = (e: PointerEvent) => {\n clearTimeout(this._holdTimer);\n const start = this._pointerStart;\n this._pointerStart = null;\n if (!start) return;\n\n const dx = e.clientX - start.x;\n const dy = e.clientY - start.y;\n const elapsed = performance.now() - start.t;\n const isTap = elapsed < 200 && Math.abs(dx) < 10 && Math.abs(dy) < 10;\n const wasLongPress = elapsed >= 200 && Math.abs(dx) < 10 && Math.abs(dy) < 10;\n const SWIPE = 50;\n\n if (wasLongPress) {\n if (this._video.paused) this.play();\n return;\n }\n\n if (isTap) {\n // Tap zones: chapter-aware nav. Left ~30% = previous chapter (or previous\n // story at the chapter boundary), anywhere else = next chapter (or next\n // story at the last chapter). Chevron buttons remain story-to-story.\n const tap = (e.target as HTMLElement).closest<HTMLElement>('[data-tap]');\n if (tap?.dataset.tap === 'previous') this.previous();\n else this.next();\n return;\n }\n\n if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > SWIPE) {\n if (dx < 0) this.nextStory();\n else this.previousStory();\n } else if (dy > SWIPE) {\n this.close();\n }\n };\n\n // --- Segments / progress (chapter-based) ---\n\n private _refreshSegmentRefs() {\n this._segments = Array.from(\n this.shadowRoot!.querySelectorAll<HTMLElement>('[part~=\"progress-fill\"]'),\n );\n }\n\n private _resetSegmentFills() {\n this._refreshSegmentRefs();\n for (let i = 0; i < this._segments.length; i++) {\n const p = i < this.chapter ? 1 : 0;\n this._segments[i].style.setProperty('--p', String(p));\n }\n }\n\n private _loadCurrent() {\n // Guard against a close racing the microtask: if the viewer is no longer\n // open, don't restart the video with stale state.\n if (!this.open || !this._dialog.open) return;\n const story = this.stories[this.index];\n if (!story) return;\n cancelAnimationFrame(this._raf);\n // Reset the prefetch tracker when the active story changes — the next\n // candidate has shifted and may need its own preload hint.\n this._prefetchedSrc = null;\n this._syncSlots(story);\n this._video.muted = this.muted;\n this._video.loop = this.loop;\n if (this._video.src !== story.src) {\n this._video.src = story.src;\n } else {\n this._video.currentTime = 0;\n }\n // Wait one frame for the new segment DOM to render before resetting fills.\n requestAnimationFrame(() => this._resetSegmentFills());\n this._video.play().catch(() => {\n // Autoplay-with-sound rejection: force muted, retry.\n this.muted = true;\n this._video.muted = true;\n this._video.play().catch(() => {});\n });\n }\n\n /**\n * Inject a `<link rel=\"preload\" as=\"video\">` for the next story once the\n * current video crosses 50% playback. Idempotent: tracked by `_prefetchedSrc`,\n * cleaned up on viewer close.\n */\n private _maybePrefetchNext() {\n const v = this._video;\n if (!v.duration || v.currentTime / v.duration < 0.5) return;\n const nextStory = this.stories[this.index + 1];\n if (!nextStory?.src) return;\n if (this._prefetchedSrc === nextStory.src) return;\n this._prefetchedSrc = nextStory.src;\n\n const link = document.createElement('link');\n link.rel = 'preload';\n link.as = 'video';\n link.href = nextStory.src;\n link.crossOrigin = 'anonymous';\n link.dataset.luxenStoriesPrefetch = '';\n document.head.appendChild(link);\n }\n\n /** Remove every prefetch hint we've injected. Called when the viewer closes. */\n private _clearPrefetchHints() {\n const links = document.head.querySelectorAll<HTMLLinkElement>(\n 'link[data-luxen-stories-prefetch]',\n );\n for (const link of links) link.remove();\n this._prefetchedSrc = null;\n }\n\n private _applyChapterSeek() {\n const target = this._chapterStarts[this.chapter] ?? 0;\n this._video.currentTime = target;\n this._resetSegmentFills();\n const story = this.stories[this.index];\n if (story) this.dispatchEvent(new ChapterChangeEvent(this.chapter, story));\n }\n\n private get _lastChapter() {\n return Math.max(0, this._chapterStarts.length - 1);\n }\n\n // Clone the active story's slot-marked children (cta, header) into the\n // viewer's own light DOM so the shadow root's `<slot name=\"cta\">` and\n // `<slot name=\"header\">` project them. Originals stay attached to the\n // story (hidden via CSS) and are reused on every `index` change.\n private _syncSlots(story: LuxenStory) {\n for (const node of Array.from(this.children)) {\n if ((node as HTMLElement).dataset.storySlot !== undefined) node.remove();\n }\n for (const child of Array.from(story.children)) {\n const slotName = (child as HTMLElement).getAttribute?.('slot');\n if (!slotName) continue;\n const clone = child.cloneNode(true) as HTMLElement;\n clone.dataset.storySlot = '';\n this.appendChild(clone);\n }\n }\n\n private _tick = () => {\n if (!this.open) return;\n const v = this._video;\n const seg = this._segments[this.chapter];\n if (v && seg) {\n const start = this._chapterStarts[this.chapter] ?? 0;\n const end = this._chapterStarts[this.chapter + 1] ?? v.duration;\n const span = Math.max(0, end - start);\n const p = span ? Math.max(0, Math.min((v.currentTime - start) / span, 1)) : 0;\n seg.style.setProperty('--p', String(p));\n // Auto-advance chapter when its segment is full and there's a next chapter in this story.\n // (The video element's `ended` event handles the last chapter via `_onEnded`.)\n if (p >= 1 && this.chapter < this._lastChapter) {\n this._advance(1);\n }\n }\n this._raf = requestAnimationFrame(this._tick);\n };\n\n private _onEnded() {\n // Last chapter of the current story finished.\n const story = this.stories[this.index];\n if (story) this.dispatchEvent(new StoryEndEvent(this.index));\n if (this.loop) {\n this._video.currentTime = this._chapterStarts[0] ?? 0;\n this.chapter = 0;\n this._video.play().catch(() => {});\n return;\n }\n if (!this.autoAdvance) return;\n if (this.index < this.stories.length - 1) {\n this.index = this.index + 1;\n } else {\n this.close();\n }\n }\n\n /** chapter-then-story navigation. */\n private _advance(direction: 1 | -1) {\n if (direction === 1) {\n if (this.chapter < this._lastChapter) {\n this.chapter = this.chapter + 1;\n } else if (this.index < this.stories.length - 1) {\n this.index = this.index + 1;\n } else {\n this.close();\n }\n return;\n }\n // direction === -1: Instagram pattern — restart the current chapter if past 1s in.\n const start = this._chapterStarts[this.chapter] ?? 0;\n if (this._video.currentTime > start + 1) {\n this._video.currentTime = start;\n this._resetSegmentFills();\n return;\n }\n if (this.chapter > 0) {\n this.chapter = this.chapter - 1;\n } else if (this.index > 0) {\n // Crossing into the previous story: load it, then jump to its last chapter.\n const previousStory = this.stories[this.index - 1];\n const previousLast = Math.max(0, previousStory.getChapterStarts().length - 1);\n this.index = this.index - 1;\n // After `index` updates, `_loadCurrent()` runs and resets `chapter` to 0.\n // Set `chapter` to the previous story's last chapter on the next microtask.\n queueMicrotask(() => {\n this.chapter = previousLast;\n });\n }\n }\n\n // --- Public API ---\n\n /** Open the viewer at the given index with an explicit playlist. */\n openAt(stories: LuxenStory[], index = 0, source: LuxenStories | null = null) {\n this.stories = stories;\n this.source = source;\n this.index = Math.max(0, Math.min(index, stories.length - 1));\n this.open = true;\n }\n\n close() {\n this.open = false;\n }\n\n /** Advance one chapter, or to the next story at the chapter boundary. */\n next() {\n this._advance(1);\n }\n\n /** Retreat one chapter, restart the current chapter past 1s in, or cross into the previous story. */\n previous() {\n this._advance(-1);\n }\n\n /** Jump to the next story, skipping any remaining chapters in the current story. */\n nextStory() {\n if (this.index < this.stories.length - 1) {\n this.index = this.index + 1;\n } else {\n this.close();\n }\n }\n\n /** Jump to the previous story regardless of current chapter. Lands on chapter 0 of the previous story. */\n previousStory() {\n if (this.index > 0) {\n this.index = this.index - 1;\n }\n }\n\n play() {\n this._video?.play().catch(() => {});\n this._raf = requestAnimationFrame(this._tick);\n }\n\n pause() {\n this._video?.pause();\n cancelAnimationFrame(this._raf);\n }\n\n // --- Helpers ---\n\n private async _emitAfter(name: 'after-show' | 'after-hide') {\n await new Promise((r) => requestAnimationFrame(() => r(null)));\n const anims = this._dialog.getAnimations({ subtree: false });\n await Promise.all(anims.map((a) => a.finished.catch(() => {})));\n if ((name === 'after-show') !== this.open) return;\n this.dispatchEvent(name === 'after-show' ? new AfterShowEvent() : new AfterHideEvent());\n if (name === 'after-show') {\n this._raf = requestAnimationFrame(this._tick);\n }\n }\n\n private _restoreFocus() {\n const stories = this.source?.stories() ?? this.stories;\n const story = stories[this.index];\n const trigger = story?.querySelector<HTMLButtonElement>('button[data-story-trigger]');\n trigger?.focus({ preventScroll: false });\n }\n\n override render() {\n const total = this.stories.length;\n const currentStory = this.stories[this.index];\n // Chevrons navigate story-to-story, so visibility depends on `index` only (not chapter).\n const isFirstStory = this.index === 0;\n const isLastStory = this.index >= total - 1;\n // Render one segment per chapter of the CURRENT story. Use `_chapterStarts` if present\n // (set after `_loadCurrent`), or fall back to the current story's parsed chapters.\n const chapterStarts = this._chapterStarts.length\n ? this._chapterStarts\n : (currentStory?.getChapterStarts() ?? [0]);\n const iconTag = staticTag('icon');\n const spinnerTag = staticTag('spinner');\n\n return html`\n <dialog\n part=\"dialog\"\n aria-label=${this._localize.term('stories')}\n >\n <div\n part=\"frame\"\n class=\"frame\"\n @pointerdown=${this._onFramePointerDown}\n @pointerup=${this._onFramePointerUp}\n @pointercancel=${this._onFramePointerUp}\n >\n <div\n part=\"progress\"\n class=\"progress\"\n role=\"progressbar\"\n aria-label=${this._localize.term('storyProgress')}\n aria-valuenow=${this.chapter + 1}\n aria-valuemin=\"1\"\n aria-valuemax=${chapterStarts.length || 1}\n >\n ${map(\n chapterStarts,\n (_, i) => html`\n <span\n part=\"progress-segment\"\n class=\"progress-segment\"\n data-state=${i < this.chapter ? 'past' : i === this.chapter ? 'active' : 'future'}\n >\n <span\n part=\"progress-fill\"\n class=\"progress-fill\"\n ></span>\n </span>\n `,\n )}\n </div>\n\n <header\n part=\"header\"\n class=\"header\"\n data-no-gesture\n >\n <slot name=\"header\">\n ${\n currentStory?.poster\n ? html`<img\n class=\"header-thumb\"\n src=${currentStory.poster}\n alt=\"\"\n />`\n : nothing\n }\n ${\n currentStory?.label\n ? html`<div class=\"header-text\">\n <div\n part=\"header-label\"\n class=\"header-label\"\n >\n ${currentStory.label}\n </div>\n </div>`\n : nothing\n }\n </slot>\n </header>\n\n <div\n part=\"actions\"\n class=\"actions\"\n data-no-gesture\n >\n <slot name=\"close\">\n <button\n type=\"button\"\n part=\"button-close\"\n class=\"btn btn-close\"\n aria-label=${this._localize.term('close')}\n @click=${() => this.close()}\n >\n <${iconTag} name=\"mdi:close\"></${iconTag}>\n </button>\n </slot>\n <button\n type=\"button\"\n part=\"button-pause\"\n class=\"btn btn-pause\"\n aria-label=${this._paused ? 'Play' : 'Pause'}\n aria-pressed=${this._paused ? 'true' : 'false'}\n @click=${() => (this._paused ? this.play() : this.pause())}\n >\n <${iconTag} name=${this._paused ? 'mdi:play' : 'mdi:pause'}></${iconTag}>\n </button>\n <button\n type=\"button\"\n part=\"button-mute\"\n class=\"btn btn-mute\"\n aria-label=${this.muted ? this._localize.term('unmute') : this._localize.term('mute')}\n aria-pressed=${this.muted ? 'false' : 'true'}\n @click=${() => {\n this.muted = !this.muted;\n this.dispatchEvent(new MuteChangeEvent(this.muted));\n }}\n >\n <${iconTag} name=${this.muted ? 'mdi:volume-off' : 'mdi:volume-high'}></${iconTag}>\n </button>\n </div>\n\n <video\n part=\"video\"\n class=\"video\"\n playsinline\n preload=\"metadata\"\n ?muted=${this.muted}\n ></video>\n\n <${spinnerTag}\n part=\"spinner\"\n class=\"spinner\"\n aria-label=${this._localize.term('loading')}\n data-no-gesture\n data-state=${this._loading ? 'loading' : 'idle'}\n ></${spinnerTag}>\n\n <!-- Tap zones: invisible overlays that translate clicks into previous/next.\n Sit BELOW header / overlay / chevron buttons in z-order, above the video. -->\n <div\n class=\"tap-zone tap-previous\"\n data-tap=\"previous\"\n aria-hidden=\"true\"\n ></div>\n <div\n class=\"tap-zone tap-next\"\n data-tap=\"next\"\n aria-hidden=\"true\"\n ></div>\n\n <div\n part=\"overlay\"\n class=\"overlay\"\n data-no-gesture\n >\n <slot name=\"cta\"></slot>\n </div>\n\n <div\n class=${cls('visually-hidden')}\n aria-live=\"polite\"\n >\n ${\n total\n ? `Story ${this.index + 1} of ${total}${\n this.stories[this.index]?.label ? ` — ${this.stories[this.index].label}` : ''\n }${chapterStarts.length > 1 ? ` — chapter ${this.chapter + 1} of ${chapterStarts.length}` : ''}`\n : ''\n }\n </div>\n </div>\n\n ${\n isFirstStory\n ? nothing\n : html`<button\n type=\"button\"\n part=\"button-previous\"\n class=\"btn btn-nav btn-previous\"\n aria-label=${this._localize.term('previousStory')}\n @click=${() => this.previousStory()}\n >\n <${iconTag} name=\"mdi:chevron-left\"></${iconTag}>\n </button>`\n }\n ${\n isLastStory\n ? nothing\n : html`<button\n type=\"button\"\n part=\"button-next\"\n class=\"btn btn-nav btn-next\"\n aria-label=${this._localize.term('nextStory')}\n @click=${() => this.nextStory()}\n >\n <${iconTag} name=\"mdi:chevron-right\"></${iconTag}>\n </button>`\n }\n </dialog>\n `;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @summary Tables display data in rows and columns, so it is easy to scan and compare.
|
|
3
|
+
*
|
|
4
|
+
* @nativeElement table
|
|
5
|
+
* @selector .l-table
|
|
6
|
+
*
|
|
7
|
+
* @cssClass .l-table - Applied to the `<table>`. Name it with a `<caption>` (visible or `.l-visually-hidden`) or `aria-label`; head the columns with `<th scope="col">` and each row with the `<th scope="row">` that identifies it.
|
|
8
|
+
*
|
|
9
|
+
* @attribute data-size - sm | lg — Row height, on the control size scale: a control of the same size fits a row. Defaults to the medium size.
|
|
10
|
+
* @attribute data-align - start | center | end — On a column's `<th>` and `<td>`s: aligns the column. Use `end` for figures; digits are tabular across the table, so they line up. A sort button in an `end` header puts its glyph before the label, keeping the label flush with the figures.
|
|
11
|
+
* @attribute aria-sort - ascending | descending — On the sorted column's `<th>`, which holds a `<button>` (or a link, when the server sorts): shows the direction. A disabled button marks a column the current query cannot sort by; an `.l-button` in a header is another action and keeps its own look.
|
|
12
|
+
*
|
|
13
|
+
* @cssproperty [--row-height=calc(var(--l-size-control-md) + 2 * var(--l-spacing-1))] - Minimum row height; content taller than it grows the row.
|
|
14
|
+
* @cssproperty [--background-color=var(--l-color-surface)] - Background of the table. The sticky header repeats it, so it stays opaque over the rows scrolling under it.
|
|
15
|
+
* @cssproperty [--border-color=var(--l-color-divider)] - Rules between rows. The stronger rules under the header and above the totals use `--l-color-border`.
|
|
16
|
+
*
|
|
17
|
+
* @example Basic
|
|
18
|
+
* <table class="l-table">
|
|
19
|
+
* <caption>Products</caption>
|
|
20
|
+
* <thead>
|
|
21
|
+
* <tr>
|
|
22
|
+
* <th scope="col">SKU</th>
|
|
23
|
+
* <th scope="col">Name</th>
|
|
24
|
+
* <th scope="col" data-align="end">In stock</th>
|
|
25
|
+
* </tr>
|
|
26
|
+
* </thead>
|
|
27
|
+
* <tbody>
|
|
28
|
+
* <tr>
|
|
29
|
+
* <th scope="row">SKU-1042</th>
|
|
30
|
+
* <td>Desk lamp</td>
|
|
31
|
+
* <td data-align="end">4,210</td>
|
|
32
|
+
* </tr>
|
|
33
|
+
* </tbody>
|
|
34
|
+
* </table>
|
|
35
|
+
*
|
|
36
|
+
* @example Sortable
|
|
37
|
+
* <table class="l-table">
|
|
38
|
+
* <caption>Products</caption>
|
|
39
|
+
* <thead>
|
|
40
|
+
* <tr>
|
|
41
|
+
* <th scope="col" aria-sort="ascending"><button type="button">SKU</button></th>
|
|
42
|
+
* <th scope="col"><button type="button">Name</button></th>
|
|
43
|
+
* </tr>
|
|
44
|
+
* </thead>
|
|
45
|
+
* <tbody>
|
|
46
|
+
* <tr>
|
|
47
|
+
* <th scope="row">SKU-1042</th>
|
|
48
|
+
* <td>Desk lamp</td>
|
|
49
|
+
* </tr>
|
|
50
|
+
* </tbody>
|
|
51
|
+
* </table>
|
|
52
|
+
*/
|
|
53
|
+
export declare class TableMeta {
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=table.meta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.meta.d.ts","sourceRoot":"","sources":["../../../src/html/elements/table/table.meta.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAIH,qBAAa,SAAS;CAAG"}
|
|
File without changes
|
package/cdn/elements/tag/tag.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{i as e}from"../../chunks/lit.js";import{r as t,t as n}from"../../chunks/decorate.js";import{LuxenElement as r}from"../../shared/luxen-element.js";import{a as i,t as a}from"../../chunks/lit-html.js";import{LocalizeController as o}from"../../shared/localize.js";import{cls as s}from"../../registry.js";import c from"../../shared/styles/host.styles.js";import l from"../../shared/styles/checkbox-appearance.styles.js";var u=e(`@layer components{:host{--_height:var(--height,1.5rem);--_font-size:var(--font-size,var(--l-text-xs));--_padding-inline:var(--padding-inline,.5rem);--_gap:.25rem;--_background:var(--background-color,var(--l-color-bg-fill-neutral-subtle));--_color:var(--text-color,var(--l-color-text-neutral));--_border-strength:14%;--_border-color:var(--border-color,color-mix(in oklab, currentColor var(--_border-strength), transparent));--_border-color-hover:var(--border-color,color-mix(in oklab, currentColor calc(var(--_border-strength) + 12%), transparent));--_border-radius:var(--border-radius,var(--l-radius-full));--_remove-glyph:.75rem;vertical-align:middle;max-inline-size:100%;display:inline-flex}:host([size=sm]){--_padding-inline:var(--padding-inline,.375rem);--_gap:.125rem}:host([size=lg]){--_height:var(--height,1.75rem);--_font-size:var(--font-size,var(--l-text-sm));--_padding-inline:var(--padding-inline,.625rem);--_remove-glyph:.875rem}:host([disabled]) .base{cursor:not-allowed}.base{align-items:center;gap:var(--_gap);min-block-size:var(--_height);padding-inline:var(--_padding-inline);border:1px solid var(--_border-color);border-radius:var(--_border-radius);background-color:var(--_background);color:var(--_color);font-family:var(--l-font-neo-grotesque);font-size:var(--_font-size);font-weight:var(--l-font-weight-medium);min-inline-size:0;line-height:1;transition:background-color .12s,border-color .12s;display:inline-flex}:host([removable]) .base{min-block-size:max(1.5rem, var(--_height))}@media (hover:hover){:host([selectable]:hover:not([disabled])) .base{background-color:color-mix(in oklab, var(--_background), currentColor 7%);border-color:var(--_border-color-hover)}}.content{text-overflow:ellipsis;white-space:nowrap;align-items:center;min-inline-size:0;display:inline-flex;overflow:clip visible}::slotted([slot=prefix]){flex:none;display:inline-flex}::slotted([slot=suffix]){font-weight:var(--l-font-weight-normal);font-variant-numeric:tabular-nums;flex:none}:host([selectable]){--_height:var(--height,1.75rem);--_accent:var(--l-form-control-activated-color);--_selected-color:var(--selected-text-color,var(--lightningcss-light,color-mix(in oklab, var(--_accent), black 14%))var(--lightningcss-dark,color-mix(in oklab, var(--_accent), white 45%)));--_selected-background:var(--selected-background-color,color-mix(in oklab, var(--_selected-color) 10%, var(--l-color-surface)));--_selected-border-color:var(--selected-border-color,color-mix(in oklab, var(--_selected-color) 45%, transparent))}:host([selectable][size=lg]){--_height:var(--height,2rem)}:host([selectable][selected]){--_background:var(--_selected-background);--_color:var(--_selected-color);--_border-color:var(--_selected-border-color);--_border-color-hover:var(--selected-border-color,color-mix(in oklab, var(--_selected-color) 57%, transparent))}:host([selectable]) .base{cursor:pointer;-webkit-user-select:none;user-select:none}:host([selectable][disabled]) .base{cursor:not-allowed}.toggle{align-self:stretch;align-items:center;gap:var(--_gap);min-inline-size:0;margin-inline:calc(var(--_padding-inline) * -1);padding-inline:var(--_padding-inline);color:inherit;font:inherit;text-align:start;cursor:inherit;background:0 0;border:0;flex:auto;display:inline-flex}:host([removable]) .toggle{margin-inline-end:0;padding-inline-end:0}:host([selectable]) .toggle:focus-visible,:host([selectable]) .checkbox:focus-visible{outline:none}.base:has(:is(.toggle,.checkbox):focus-visible){outline:
|
|
1
|
+
import{i as e}from"../../chunks/lit.js";import{r as t,t as n}from"../../chunks/decorate.js";import{LuxenElement as r}from"../../shared/luxen-element.js";import{a as i,t as a}from"../../chunks/lit-html.js";import{LocalizeController as o}from"../../shared/localize.js";import{cls as s}from"../../registry.js";import c from"../../shared/styles/host.styles.js";import l from"../../shared/styles/checkbox-appearance.styles.js";var u=e(`@layer components{:host{--_height:var(--height,1.5rem);--_font-size:var(--font-size,var(--l-text-xs));--_padding-inline:var(--padding-inline,.5rem);--_gap:.25rem;--_background:var(--background-color,var(--l-color-bg-fill-neutral-subtle));--_color:var(--text-color,var(--l-color-text-neutral));--_border-strength:14%;--_border-color:var(--border-color,color-mix(in oklab, currentColor var(--_border-strength), transparent));--_border-color-hover:var(--border-color,color-mix(in oklab, currentColor calc(var(--_border-strength) + 12%), transparent));--_border-radius:var(--border-radius,var(--l-radius-full));--_remove-glyph:.75rem;vertical-align:middle;max-inline-size:100%;display:inline-flex}:host([size=sm]){--_padding-inline:var(--padding-inline,.375rem);--_gap:.125rem}:host([size=lg]){--_height:var(--height,1.75rem);--_font-size:var(--font-size,var(--l-text-sm));--_padding-inline:var(--padding-inline,.625rem);--_remove-glyph:.875rem}:host([disabled]) .base{cursor:not-allowed}.base{align-items:center;gap:var(--_gap);min-block-size:var(--_height);padding-inline:var(--_padding-inline);border:1px solid var(--_border-color);border-radius:var(--_border-radius);background-color:var(--_background);color:var(--_color);font-family:var(--l-font-neo-grotesque);font-size:var(--_font-size);font-weight:var(--l-font-weight-medium);min-inline-size:0;line-height:1;transition:background-color .12s,border-color .12s;display:inline-flex}:host([removable]) .base{min-block-size:max(1.5rem, var(--_height))}@media (hover:hover){:host([selectable]:hover:not([disabled])) .base{background-color:color-mix(in oklab, var(--_background), currentColor 7%);border-color:var(--_border-color-hover)}}.content{text-overflow:ellipsis;white-space:nowrap;align-items:center;min-inline-size:0;display:inline-flex;overflow:clip visible}::slotted([slot=prefix]){flex:none;display:inline-flex}::slotted([slot=suffix]){font-weight:var(--l-font-weight-normal);font-variant-numeric:tabular-nums;flex:none}:host([selectable]){--_height:var(--height,1.75rem);--_accent:var(--l-form-control-activated-color);--_selected-color:var(--selected-text-color,var(--lightningcss-light,color-mix(in oklab, var(--_accent), black 14%))var(--lightningcss-dark,color-mix(in oklab, var(--_accent), white 45%)));--_selected-background:var(--selected-background-color,color-mix(in oklab, var(--_selected-color) 10%, var(--l-color-surface)));--_selected-border-color:var(--selected-border-color,color-mix(in oklab, var(--_selected-color) 45%, transparent))}:host([selectable][size=lg]){--_height:var(--height,2rem)}:host([selectable][selected]){--_background:var(--_selected-background);--_color:var(--_selected-color);--_border-color:var(--_selected-border-color);--_border-color-hover:var(--selected-border-color,color-mix(in oklab, var(--_selected-color) 57%, transparent))}:host([selectable]) .base{cursor:pointer;-webkit-user-select:none;user-select:none}:host([selectable][disabled]) .base{cursor:not-allowed}.toggle{align-self:stretch;align-items:center;gap:var(--_gap);min-inline-size:0;margin-inline:calc(var(--_padding-inline) * -1);padding-inline:var(--_padding-inline);color:inherit;font:inherit;text-align:start;cursor:inherit;background:0 0;border:0;flex:auto;display:inline-flex}:host([removable]) .toggle{margin-inline-end:0;padding-inline-end:0}:host([selectable]) .toggle:focus-visible,:host([selectable]) .checkbox:focus-visible{outline:none}.base:has(:is(.toggle,.checkbox):focus-visible){outline:var(--l-focus-ring-width) solid var(--l-focus-ring-color);outline-offset:var(--l-focus-ring-offset)}.checkbox{--accent:var(--selected-text-color,var(--l-form-control-activated-color));--_halo:var(--l-form-control-border-color-hover);font:inherit;flex:none;transition-property:background-color,border-color,box-shadow}.checkbox:checked,.checkbox:indeterminate{--_halo:var(--accent)}@media (hover:hover){:host([selectable]:not([disabled])) .base:hover .checkbox:not(:disabled){box-shadow:0 0 0 calc(var(--size) * var(--l-form-control-toggle-halo-ratio)) color-mix(in oklab, var(--_halo), transparent 84%)}}@media (forced-colors:active){:host([selectable][selected]) .base{border-color:highlight}}.remove{inline-size:max(1.5rem, var(--_height));margin-block:-1px;margin-inline:calc(var(--_padding-inline) - var(--_gap)) calc(var(--_padding-inline) * -1 - 1px);border:1px solid #0000;border-inline-start-color:var(--_border-color);color:inherit;cursor:pointer;--_press-scale:.86;background-color:#0000;background-clip:padding-box;border-start-end-radius:var(--_border-radius);border-end-end-radius:var(--_border-radius);flex:none;align-self:stretch;place-items:center;padding:0;transition:background-color .12s;display:grid}.remove:after{content:"";inline-size:var(--_remove-glyph);block-size:var(--_remove-glyph);background-color:currentColor;transition:scale .12s;-webkit-mask:url("data:image/svg+xml;utf8,<svg xmlns=\\"http://www.w3.org/2000/svg\\" viewBox=\\"0 0 24 24\\"><path d=\\"M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z\\" /></svg>") 50%/contain no-repeat;mask:url("data:image/svg+xml;utf8,<svg xmlns=\\"http://www.w3.org/2000/svg\\" viewBox=\\"0 0 24 24\\"><path d=\\"M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z\\" /></svg>") 50%/contain no-repeat}@media (hover:hover){.remove:hover:not(:disabled){background-color:color-mix(in oklab, transparent, currentColor 12%)}}.remove:active:not(:disabled){background-color:color-mix(in oklab, transparent, currentColor 22%)}.remove:active:not(:disabled):after{scale:var(--_press-scale)}.remove:focus-visible{outline:var(--l-focus-ring-width) solid var(--l-focus-ring-color);outline-offset:calc(var(--l-focus-ring-width) * -1)}.remove:disabled{cursor:not-allowed;opacity:.4}@media (forced-colors:active){.remove:after{background-color:buttontext}}@media (prefers-reduced-motion:reduce){.base,.checkbox,.remove,.remove:after{transition-duration:0s}.remove{--_press-scale:1}}}`),d=class extends Event{constructor(){super(`remove`,{bubbles:!1,composed:!1,cancelable:!0})}},f=class extends Event{constructor(e){super(`change`,{bubbles:!0,composed:!1,cancelable:!1}),this.selected=e}},p=class extends r{static{this.styles=[c,l,u]}#e;get size(){return this.#e}set size(e){this.#e=e}#t;get removable(){return this.#t}set removable(e){this.#t=e}#n;get selectable(){return this.#n}set selectable(e){this.#n=e}#r;get selected(){return this.#r}set selected(e){this.#r=e}#i;get control(){return this.#i}set control(e){this.#i=e}#a;get disabled(){return this.#a}set disabled(e){this.#a=e}constructor(){super(),this._localize=new o(this),this.#e=`md`,this.#t=!1,this.#n=!1,this.#r=!1,this.#i=`none`,this.#a=!1,this._requestRemove=()=>{if(this.disabled)return;let e=new d;this.dispatchEvent(e),e.defaultPrevented||this.remove()},this._onKeyDown=e=>{this.removable&&!this.disabled&&(e.key===`Backspace`||e.key===`Delete`)&&(e.preventDefault(),this._requestRemove())},this._toggle=()=>{this.disabled||(this.selected=!this.selected,this.dispatchEvent(new f(this.selected)))},this._onHostClick=e=>{this.selectable&&!this.disabled&&e.composedPath()[0]===this&&this._toggle()},this._onCheckboxChange=e=>{this.selected=e.target.checked,this.dispatchEvent(new f(this.selected))},this.addEventListener(`click`,this._onHostClick)}willUpdate(){this.control===`checkbox`&&(this.selectable=!0)}updated(){let e=this.renderRoot.querySelector(`.checkbox`);e&&(e.checked=this.selected)}_label(){return i`
|
|
2
2
|
<slot name="prefix"></slot>
|
|
3
3
|
<span
|
|
4
4
|
class="content"
|