@brytdesigns/web-component-keen-slider 3.0.0 → 3.1.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.
@@ -1,4 +1,4 @@
1
- import { constrain } from "./utils.js";
1
+ import { constrain, formatSlideLabel } from "./utils.js";
2
2
  import _KeenSlider from "keen-slider";
3
3
  //#region src/KeenSlider.ts
4
4
  function attachMoreDetails(prevSlide) {
@@ -9,6 +9,17 @@ function attachMoreDetails(prevSlide) {
9
9
  };
10
10
  }
11
11
  /**
12
+ * The `aria-label` this package last wrote on each slide.
13
+ *
14
+ * Module-level because the slider is rebuilt - by every `addPlugin`, by
15
+ * `refresh-on-children-change`, and by a new `config` or `slide-label` - and
16
+ * each rebuild is a fresh plugin with no memory of the last. Without this, a
17
+ * rebuilt slider saw the labels its predecessor had written, took every one
18
+ * for an authored label, and never touched them again: a changed template was
19
+ * ignored, and a slide count that changed left "1 of 3" on a carousel of four.
20
+ */
21
+ var writtenSlideLabels = /* @__PURE__ */ new WeakMap();
22
+ /**
12
23
  * APG's carousel pattern: a labelled group whose role description tells
13
24
  * assistive tech what it is, with each slide a group of its own.
14
25
  *
@@ -22,12 +33,19 @@ function attachMoreDetails(prevSlide) {
22
33
  * constructor gets here to subscribe, so on a carousel nobody ever interacted
23
34
  * with the slides got no ARIA at all — which is exactly why the container
24
35
  * advertised owned `option` children it did not have.
36
+ *
37
+ * `slideLabel` is the `slide-label` template. Each slide's `aria-label` was
38
+ * hard-coded English, so a theme on any other locale could only translate it
39
+ * by authoring a label on every slide by hand.
25
40
  */
26
- var attachAriaAttributes = (slider) => {
41
+ var attachAriaAttributes = (slider, slideLabel) => {
27
42
  const container = slider.container;
28
43
  const authoredContainerLabel = container.hasAttribute("aria-label");
29
44
  const authoredSlideLabels = /* @__PURE__ */ new WeakSet();
30
- for (const slide of slider.slides) if (slide.hasAttribute("aria-label")) authoredSlideLabels.add(slide);
45
+ for (const slide of slider.slides) {
46
+ const label = slide.getAttribute("aria-label");
47
+ if (label !== null && label !== writtenSlideLabels.get(slide)) authoredSlideLabels.add(slide);
48
+ }
31
49
  container.setAttribute("role", "group");
32
50
  container.setAttribute("aria-roledescription", "carousel");
33
51
  if (!authoredContainerLabel) container.setAttribute("aria-label", "Carousel");
@@ -55,7 +73,11 @@ var attachAriaAttributes = (slider) => {
55
73
  slider.slides.forEach((slide, index) => {
56
74
  slide.setAttribute("role", "group");
57
75
  slide.setAttribute("aria-roledescription", "slide");
58
- if (!authoredSlideLabels.has(slide)) slide.setAttribute("aria-label", `${index + 1} of ${total}`);
76
+ if (!authoredSlideLabels.has(slide)) {
77
+ const label = formatSlideLabel(slideLabel, index + 1, total);
78
+ slide.setAttribute("aria-label", label);
79
+ writtenSlideLabels.set(slide, label);
80
+ }
59
81
  slide.removeAttribute("aria-selected");
60
82
  slide.removeAttribute("tabindex");
61
83
  const visible = disabled || visiblePortion(index) > 0;
@@ -141,12 +163,12 @@ var attachKeyboardNavigation = (slider) => {
141
163
  };
142
164
  var KeenSlider = class extends _KeenSlider {
143
165
  direction = null;
144
- constructor(element, config, plugins = []) {
166
+ constructor(element, config, plugins = [], accessibility = {}) {
145
167
  super(element, config, plugins);
146
168
  const prevSlide = 0;
147
169
  this.direction = null;
148
170
  this.on("slideChanged", attachMoreDetails(prevSlide));
149
- attachAriaAttributes(this);
171
+ attachAriaAttributes(this, accessibility.slideLabel);
150
172
  attachKeyboardNavigation(this);
151
173
  const _next = this.next, _prev = this.prev;
152
174
  this.next = function() {
@@ -1 +1 @@
1
- {"version":3,"file":"KeenSlider.js","names":[],"sources":["../../src/KeenSlider.ts"],"sourcesContent":["import type { ICustomElement } from \"component-register\";\n\nimport _KeenSlider, {\n\ttype KeenSliderHooks as _KeenSliderHooks,\n\ttype KeenSliderInstance as _KeenSliderInstance,\n\ttype KeenSliderOptions as _KeenSliderOptions,\n\ttype KeenSliderPlugin as _KeenSliderPlugin,\n} from \"keen-slider\";\n\nimport { constrain } from \"./utils\";\n\nexport type KeenSliderHooks = _KeenSliderHooks;\n\nexport type KeenSliderInstance<\n\tO = {},\n\tP = {},\n\tH extends _KeenSliderHooks = _KeenSliderHooks,\n> = _KeenSliderInstance<O, P, H>;\n\ntype ExtendedSlidesOption = NonNullable<\n\t_KeenSliderInstance[\"options\"][\"slides\"]\n> & {\n\tperScroll?: number;\n};\n\ntype ExtendedKeenSliderOptions = Omit<\n\t_KeenSliderInstance[\"options\"],\n\t\"slides\"\n> & {\n\tslides?: ExtendedSlidesOption;\n};\n\nexport type KeenSliderOptions<\n\tO = {},\n\tP = {},\n\tH extends _KeenSliderHooks = _KeenSliderHooks,\n> = _KeenSliderOptions<O, P, H> & {\n\tslides: {\n\t\tperScroll?: number;\n\t} & _KeenSliderOptions<O, P, H>[\"slides\"];\n};\n\nexport type KeenSliderPlugin<\n\tO = {},\n\tP = {},\n\tH extends _KeenSliderHooks = _KeenSliderHooks,\n> = _KeenSliderPlugin<O, P, H>;\n\nfunction attachMoreDetails(prevSlide: number) {\n\treturn (slider: KeenSliderInstance) => {\n\t\tif (slider.track.details.rel !== slider.animator.targetIdx) return;\n\n\t\t(slider as KeenSlider).direction =\n\t\t\tslider.track.details.rel > prevSlide ? \"forward\" : \"backward\";\n\n\t\tprevSlide = slider.track.details.rel;\n\t};\n}\n\n/**\n * APG's carousel pattern: a labelled group whose role description tells\n * assistive tech what it is, with each slide a group of its own.\n *\n * What this replaces was `role=\"listbox\"` with `role=\"option\"` slides, which\n * told a screen-reader user they were inside a form control they could select\n * from. It also made `aria-valuemin/max/now` invalid — a listbox has no value —\n * and left `aria-required-children` permanently unsatisfiable.\n *\n * The slide attributes used to be applied only from `created`, `slideChanged`,\n * `optionsChanged` and `updated`. `created` fires inside `super()`, before the\n * constructor gets here to subscribe, so on a carousel nobody ever interacted\n * with the slides got no ARIA at all — which is exactly why the container\n * advertised owned `option` children it did not have.\n */\nconst attachAriaAttributes: KeenSliderPlugin = (slider) => {\n\tconst container = slider.container;\n\n\t// Anything the markup already said is the author's. Recorded before the\n\t// first write, because after that every label looks authored.\n\tconst authoredContainerLabel = container.hasAttribute(\"aria-label\");\n\tconst authoredSlideLabels = new WeakSet<Element>();\n\tfor (const slide of slider.slides) {\n\t\tif (slide.hasAttribute(\"aria-label\")) authoredSlideLabels.add(slide);\n\t}\n\n\tcontainer.setAttribute(\"role\", \"group\");\n\tcontainer.setAttribute(\"aria-roledescription\", \"carousel\");\n\tif (!authoredContainerLabel) {\n\t\tcontainer.setAttribute(\"aria-label\", \"Carousel\");\n\t}\n\t// `false`, so moving a slide announces that slide rather than re-reading\n\t// the whole carousel. `aria-live` itself belongs to the auto-rotation\n\t// handling, which knows whether announcing is wanted at all.\n\tcontainer.setAttribute(\"aria-atomic\", \"false\");\n\tif (!container.hasAttribute(\"tabindex\")) {\n\t\tcontainer.setAttribute(\"tabindex\", \"0\");\n\t}\n\n\t// These were written by the previous version of this plugin and are not\n\t// valid on `group`. A theme has no reason to have set them itself.\n\tfor (const attribute of [\n\t\t\"aria-multiselectable\",\n\t\t\"aria-valuemin\",\n\t\t\"aria-valuemax\",\n\t\t\"aria-valuenow\",\n\t\t\"aria-orientation\",\n\t\t\"aria-busy\",\n\t]) {\n\t\tcontainer.removeAttribute(attribute);\n\t}\n\n\t/** The fraction of a slide that is in view, 0 when it is fully outside. */\n\tfunction visiblePortion(index: number) {\n\t\treturn slider.track?.details?.slides?.[index]?.portion ?? 0;\n\t}\n\n\tfunction updateTabIndexes(slide: HTMLElement, tabindex: string) {\n\t\tconst focusable = Array.from(\n\t\t\tslide.querySelectorAll(\n\t\t\t\t\"input, a, button:not(:disabled), textarea, select, [tabindex]\",\n\t\t\t),\n\t\t);\n\t\tfor (const element of focusable) {\n\t\t\telement.setAttribute(\"tabindex\", tabindex);\n\t\t}\n\t}\n\n\tfunction updateSlideAriaAttributes(slider: _KeenSliderInstance) {\n\t\tconst total = slider.slides.length;\n\t\tconst disabled = Boolean(slider.options?.disabled);\n\n\t\tslider.slides.forEach((slide, index) => {\n\t\t\tslide.setAttribute(\"role\", \"group\");\n\t\t\tslide.setAttribute(\"aria-roledescription\", \"slide\");\n\t\t\tif (!authoredSlideLabels.has(slide)) {\n\t\t\t\tslide.setAttribute(\"aria-label\", `${index + 1} of ${total}`);\n\t\t\t}\n\t\t\t// Carried over from the listbox model; meaningless on a slide.\n\t\t\tslide.removeAttribute(\"aria-selected\");\n\t\t\t// A slide is a group, not a tab stop. Its contents still are.\n\t\t\tslide.removeAttribute(\"tabindex\");\n\n\t\t\tconst visible = disabled || visiblePortion(index) > 0;\n\n\t\t\t// The styling contract. ARIA is not one: keying CSS off `aria-hidden`\n\t\t\t// is what made removing it a silent breaking change for every theme\n\t\t\t// descended from the skeleton.\n\t\t\tslide.setAttribute(\"data-slide-visible\", `${visible}`);\n\n\t\t\t// `aria-hidden` only where the slide is genuinely out of view, and\n\t\t\t// never over the focused element — hiding focused content strands the\n\t\t\t// user, which is what axe's `aria-hidden-focus` rule is about. The\n\t\t\t// old code hid every inactive slide, so in any multi-slide-per-view\n\t\t\t// carousel it hid slides the user could plainly see.\n\t\t\tconst holdsFocus =\n\t\t\t\tdocument.activeElement !== null &&\n\t\t\t\tslide.contains(document.activeElement);\n\t\t\tif (!visible && !holdsFocus) slide.setAttribute(\"aria-hidden\", \"true\");\n\t\t\telse slide.removeAttribute(\"aria-hidden\");\n\n\t\t\tupdateTabIndexes(slide, visible ? \"0\" : \"-1\");\n\t\t});\n\t}\n\n\tslider.on(\"slideChanged\", updateSlideAriaAttributes);\n\tslider.on(\"created\", updateSlideAriaAttributes);\n\tslider.on(\"optionsChanged\", updateSlideAriaAttributes);\n\tslider.on(\"updated\", updateSlideAriaAttributes);\n\n\t// `created` has already fired by the time this plugin is attached, so the\n\t// subscriptions above would otherwise leave the first render unwritten.\n\tupdateSlideAriaAttributes(slider);\n};\n\n/**\n * Anything that takes typed input. Arrow keys inside one of these belong to\n * the field, not the carousel — without this guard a search box inside a slide\n * cannot be navigated with the keyboard.\n */\nconst TEXT_ENTRY =\n\t'input, textarea, select, [contenteditable=\"\"], [contenteditable=\"true\"]';\n\n/**\n * Arrow-key navigation, and the `aria-live` rule for auto-rotation.\n *\n * The container has carried `tabindex=\"0\"` for as long as the ARIA plugin has\n * existed, with nothing listening — so it took a tab stop and did nothing with\n * it, which is worse than not being focusable. This is the handler that makes\n * the tab stop worth having.\n *\n * `aria-live` was previously hardcoded to `polite` on the container (twice).\n * APG is explicit that a live region on an auto-rotating carousel announces\n * slides the user never asked for, so the announcement is tied to the rotation\n * state instead: silent while rotating, polite once paused and the user is\n * driving.\n */\nconst attachKeyboardNavigation: KeenSliderPlugin = (slider) => {\n\tconst container = slider.container;\n\tconst controller = new AbortController();\n\n\tconst isVertical = () => Boolean(slider.options?.vertical);\n\n\tfunction handleKeydown(event: KeyboardEvent) {\n\t\tif (slider.options?.disabled) return;\n\n\t\tconst target = event.target;\n\t\tif (target instanceof Element && target.closest(TEXT_ENTRY)) return;\n\n\t\tconst back = isVertical() ? \"ArrowUp\" : \"ArrowLeft\";\n\t\tconst forward = isVertical() ? \"ArrowDown\" : \"ArrowRight\";\n\n\t\tswitch (event.key) {\n\t\t\tcase back:\n\t\t\t\tevent.preventDefault();\n\t\t\t\tslider.prev();\n\t\t\t\treturn;\n\t\t\tcase forward:\n\t\t\t\tevent.preventDefault();\n\t\t\t\tslider.next();\n\t\t\t\treturn;\n\t\t\tcase \"Home\":\n\t\t\t\tevent.preventDefault();\n\t\t\t\tslider.moveToIdx(0);\n\t\t\t\treturn;\n\t\t\tcase \"End\": {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tconst details = slider.track?.details;\n\t\t\t\tconst last = Number.isFinite(details?.maxIdx)\n\t\t\t\t\t? (details?.maxIdx ?? 0)\n\t\t\t\t\t: slider.slides.length - 1;\n\t\t\t\tslider.moveToIdx(last);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * `keen-slider-auto-switch` already dispatches this, and the autoplay\n\t * package publishes its pause state the same way, so both drive one code\n\t * path here rather than each teaching the carousel about itself.\n\t */\n\tfunction handleRotationChange(event: Event) {\n\t\tconst detail = (event as CustomEvent<{ isPaused?: boolean }>).detail;\n\t\tconst rotating = detail?.isPaused === false;\n\t\tcontainer.setAttribute(\"aria-live\", rotating ? \"off\" : \"polite\");\n\t}\n\n\tcontainer.addEventListener(\"keydown\", handleKeydown, {\n\t\tsignal: controller.signal,\n\t});\n\tcontainer.addEventListener(\"autoswitch:update\", handleRotationChange, {\n\t\tsignal: controller.signal,\n\t});\n\tcontainer.addEventListener(\"autoplay:update\", handleRotationChange, {\n\t\tsignal: controller.signal,\n\t});\n\n\t// Nothing is auto-rotating until something says so, and a carousel the user\n\t// drives should announce where they have got to.\n\tif (!container.hasAttribute(\"aria-live\")) {\n\t\tcontainer.setAttribute(\"aria-live\", \"polite\");\n\t}\n\n\tslider.on(\"destroyed\", () => controller.abort());\n};\n\nexport default class KeenSlider extends _KeenSlider {\n\tdirection: \"forward\" | \"backward\" | null = null;\n\tconstructor(\n\t\telement: ICustomElement & HTMLElement,\n\t\tconfig: KeenSliderOptions,\n\t\tplugins: _KeenSliderPlugin[] = [],\n\t) {\n\t\tsuper(element, config, plugins);\n\t\tconst prevSlide = 0;\n\n\t\tthis.direction = null;\n\t\tthis.on(\"slideChanged\", attachMoreDetails(prevSlide) as any);\n\n\t\tattachAriaAttributes(this);\n\t\tattachKeyboardNavigation(this);\n\n\t\tconst _next = this.next,\n\t\t\t_prev = this.prev;\n\n\t\tthis.next = function () {\n\t\t\tif (\n\t\t\t\ttypeof this.options.slides === \"number\" ||\n\t\t\t\ttypeof this.options.slides === \"function\"\n\t\t\t)\n\t\t\t\treturn _next();\n\t\t\tlet amount: number = 1;\n\t\t\tif (this.options?.slides && \"perScroll\" in this.options.slides === true) {\n\t\t\t\tamount = (this.options?.slides?.perScroll as number) || 1;\n\t\t\t}\n\t\t\tif (!this.options.loop) {\n\t\t\t\treturn this.moveToIdx(\n\t\t\t\t\tconstrain(\n\t\t\t\t\t\tthis.track.details.rel + amount,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\tthis.track.details.maxIdx,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn this.moveToIdx(constrain(this.track.details.abs + amount), true);\n\t\t};\n\n\t\tthis.prev = function () {\n\t\t\tif (\n\t\t\t\ttypeof this.options.slides === \"number\" ||\n\t\t\t\ttypeof this.options.slides === \"function\"\n\t\t\t)\n\t\t\t\treturn _prev();\n\t\t\tlet amount: number = 1;\n\t\t\tif (this.options?.slides && \"perScroll\" in this.options.slides === true) {\n\t\t\t\tamount = (this.options?.slides?.perScroll as number) || 1;\n\t\t\t}\n\t\t\tif (!this.options.loop) {\n\t\t\t\tthis.moveToIdx(\n\t\t\t\t\tconstrain(\n\t\t\t\t\t\tthis.track.details.rel - amount,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\tthis.track.details.maxIdx,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn this.moveToIdx(constrain(this.track.details.abs - amount), true);\n\t\t};\n\t}\n}\n"],"mappings":";;;AAgDA,SAAS,kBAAkB,WAAmB;CAC7C,QAAQ,WAA+B;EACtC,IAAI,OAAO,MAAM,QAAQ,QAAQ,OAAO,SAAS,WAAW;EAE5D,OAAuB,YACtB,OAAO,MAAM,QAAQ,MAAM,YAAY,YAAY;EAEpD,YAAY,OAAO,MAAM,QAAQ;CAClC;AACD;;;;;;;;;;;;;;;;AAiBA,IAAM,wBAA0C,WAAW;CAC1D,MAAM,YAAY,OAAO;CAIzB,MAAM,yBAAyB,UAAU,aAAa,YAAY;CAClE,MAAM,sCAAsB,IAAI,QAAiB;CACjD,KAAK,MAAM,SAAS,OAAO,QAC1B,IAAI,MAAM,aAAa,YAAY,GAAG,oBAAoB,IAAI,KAAK;CAGpE,UAAU,aAAa,QAAQ,OAAO;CACtC,UAAU,aAAa,wBAAwB,UAAU;CACzD,IAAI,CAAC,wBACJ,UAAU,aAAa,cAAc,UAAU;CAKhD,UAAU,aAAa,eAAe,OAAO;CAC7C,IAAI,CAAC,UAAU,aAAa,UAAU,GACrC,UAAU,aAAa,YAAY,GAAG;CAKvC,KAAK,MAAM,aAAa;EACvB;EACA;EACA;EACA;EACA;EACA;CACD,GACC,UAAU,gBAAgB,SAAS;;CAIpC,SAAS,eAAe,OAAe;EACtC,OAAO,OAAO,OAAO,SAAS,SAAS,MAAM,EAAE,WAAW;CAC3D;CAEA,SAAS,iBAAiB,OAAoB,UAAkB;EAC/D,MAAM,YAAY,MAAM,KACvB,MAAM,iBACL,+DACD,CACD;EACA,KAAK,MAAM,WAAW,WACrB,QAAQ,aAAa,YAAY,QAAQ;CAE3C;CAEA,SAAS,0BAA0B,QAA6B;EAC/D,MAAM,QAAQ,OAAO,OAAO;EAC5B,MAAM,WAAW,QAAQ,OAAO,SAAS,QAAQ;EAEjD,OAAO,OAAO,SAAS,OAAO,UAAU;GACvC,MAAM,aAAa,QAAQ,OAAO;GAClC,MAAM,aAAa,wBAAwB,OAAO;GAClD,IAAI,CAAC,oBAAoB,IAAI,KAAK,GACjC,MAAM,aAAa,cAAc,GAAG,QAAQ,EAAE,MAAM,OAAO;GAG5D,MAAM,gBAAgB,eAAe;GAErC,MAAM,gBAAgB,UAAU;GAEhC,MAAM,UAAU,YAAY,eAAe,KAAK,IAAI;GAKpD,MAAM,aAAa,sBAAsB,GAAG,SAAS;GAOrD,MAAM,aACL,SAAS,kBAAkB,QAC3B,MAAM,SAAS,SAAS,aAAa;GACtC,IAAI,CAAC,WAAW,CAAC,YAAY,MAAM,aAAa,eAAe,MAAM;QAChE,MAAM,gBAAgB,aAAa;GAExC,iBAAiB,OAAO,UAAU,MAAM,IAAI;EAC7C,CAAC;CACF;CAEA,OAAO,GAAG,gBAAgB,yBAAyB;CACnD,OAAO,GAAG,WAAW,yBAAyB;CAC9C,OAAO,GAAG,kBAAkB,yBAAyB;CACrD,OAAO,GAAG,WAAW,yBAAyB;CAI9C,0BAA0B,MAAM;AACjC;;;;;;AAOA,IAAM,aACL;;;;;;;;;;;;;;;AAgBD,IAAM,4BAA8C,WAAW;CAC9D,MAAM,YAAY,OAAO;CACzB,MAAM,aAAa,IAAI,gBAAgB;CAEvC,MAAM,mBAAmB,QAAQ,OAAO,SAAS,QAAQ;CAEzD,SAAS,cAAc,OAAsB;EAC5C,IAAI,OAAO,SAAS,UAAU;EAE9B,MAAM,SAAS,MAAM;EACrB,IAAI,kBAAkB,WAAW,OAAO,QAAQ,UAAU,GAAG;EAE7D,MAAM,OAAO,WAAW,IAAI,YAAY;EACxC,MAAM,UAAU,WAAW,IAAI,cAAc;EAE7C,QAAQ,MAAM,KAAd;GACC,KAAK;IACJ,MAAM,eAAe;IACrB,OAAO,KAAK;IACZ;GACD,KAAK;IACJ,MAAM,eAAe;IACrB,OAAO,KAAK;IACZ;GACD,KAAK;IACJ,MAAM,eAAe;IACrB,OAAO,UAAU,CAAC;IAClB;GACD,KAAK,OAAO;IACX,MAAM,eAAe;IACrB,MAAM,UAAU,OAAO,OAAO;IAC9B,MAAM,OAAO,OAAO,SAAS,SAAS,MAAM,IACxC,SAAS,UAAU,IACpB,OAAO,OAAO,SAAS;IAC1B,OAAO,UAAU,IAAI;IACrB;GACD;EACD;CACD;;;;;;CAOA,SAAS,qBAAqB,OAAc;EAE3C,MAAM,WADU,MAA8C,QACrC,aAAa;EACtC,UAAU,aAAa,aAAa,WAAW,QAAQ,QAAQ;CAChE;CAEA,UAAU,iBAAiB,WAAW,eAAe,EACpD,QAAQ,WAAW,OACpB,CAAC;CACD,UAAU,iBAAiB,qBAAqB,sBAAsB,EACrE,QAAQ,WAAW,OACpB,CAAC;CACD,UAAU,iBAAiB,mBAAmB,sBAAsB,EACnE,QAAQ,WAAW,OACpB,CAAC;CAID,IAAI,CAAC,UAAU,aAAa,WAAW,GACtC,UAAU,aAAa,aAAa,QAAQ;CAG7C,OAAO,GAAG,mBAAmB,WAAW,MAAM,CAAC;AAChD;AAEA,IAAqB,aAArB,cAAwC,YAAY;CACnD,YAA2C;CAC3C,YACC,SACA,QACA,UAA+B,CAAC,GAC/B;EACD,MAAM,SAAS,QAAQ,OAAO;EAC9B,MAAM,YAAY;EAElB,KAAK,YAAY;EACjB,KAAK,GAAG,gBAAgB,kBAAkB,SAAS,CAAQ;EAE3D,qBAAqB,IAAI;EACzB,yBAAyB,IAAI;EAE7B,MAAM,QAAQ,KAAK,MAClB,QAAQ,KAAK;EAEd,KAAK,OAAO,WAAY;GACvB,IACC,OAAO,KAAK,QAAQ,WAAW,YAC/B,OAAO,KAAK,QAAQ,WAAW,YAE/B,OAAO,MAAM;GACd,IAAI,SAAiB;GACrB,IAAI,KAAK,SAAS,UAAU,eAAe,KAAK,QAAQ,WAAW,MAClE,SAAU,KAAK,SAAS,QAAQ,aAAwB;GAEzD,IAAI,CAAC,KAAK,QAAQ,MACjB,OAAO,KAAK,UACX,UACC,KAAK,MAAM,QAAQ,MAAM,QACzB,GACA,KAAK,MAAM,QAAQ,MACpB,CACD;GAED,OAAO,KAAK,UAAU,UAAU,KAAK,MAAM,QAAQ,MAAM,MAAM,GAAG,IAAI;EACvE;EAEA,KAAK,OAAO,WAAY;GACvB,IACC,OAAO,KAAK,QAAQ,WAAW,YAC/B,OAAO,KAAK,QAAQ,WAAW,YAE/B,OAAO,MAAM;GACd,IAAI,SAAiB;GACrB,IAAI,KAAK,SAAS,UAAU,eAAe,KAAK,QAAQ,WAAW,MAClE,SAAU,KAAK,SAAS,QAAQ,aAAwB;GAEzD,IAAI,CAAC,KAAK,QAAQ,MACjB,KAAK,UACJ,UACC,KAAK,MAAM,QAAQ,MAAM,QACzB,GACA,KAAK,MAAM,QAAQ,MACpB,CACD;GAED,OAAO,KAAK,UAAU,UAAU,KAAK,MAAM,QAAQ,MAAM,MAAM,GAAG,IAAI;EACvE;CACD;AACD"}
1
+ {"version":3,"file":"KeenSlider.js","names":[],"sources":["../../src/KeenSlider.ts"],"sourcesContent":["import type { ICustomElement } from \"component-register\";\n\nimport _KeenSlider, {\n\ttype KeenSliderHooks as _KeenSliderHooks,\n\ttype KeenSliderInstance as _KeenSliderInstance,\n\ttype KeenSliderOptions as _KeenSliderOptions,\n\ttype KeenSliderPlugin as _KeenSliderPlugin,\n} from \"keen-slider\";\n\nimport { constrain, formatSlideLabel } from \"./utils\";\n\nexport type KeenSliderHooks = _KeenSliderHooks;\n\nexport type KeenSliderInstance<\n\tO = {},\n\tP = {},\n\tH extends _KeenSliderHooks = _KeenSliderHooks,\n> = _KeenSliderInstance<O, P, H>;\n\ntype ExtendedSlidesOption = NonNullable<\n\t_KeenSliderInstance[\"options\"][\"slides\"]\n> & {\n\tperScroll?: number;\n};\n\ntype ExtendedKeenSliderOptions = Omit<\n\t_KeenSliderInstance[\"options\"],\n\t\"slides\"\n> & {\n\tslides?: ExtendedSlidesOption;\n};\n\nexport type KeenSliderOptions<\n\tO = {},\n\tP = {},\n\tH extends _KeenSliderHooks = _KeenSliderHooks,\n> = _KeenSliderOptions<O, P, H> & {\n\tslides: {\n\t\tperScroll?: number;\n\t} & _KeenSliderOptions<O, P, H>[\"slides\"];\n};\n\nexport type KeenSliderPlugin<\n\tO = {},\n\tP = {},\n\tH extends _KeenSliderHooks = _KeenSliderHooks,\n> = _KeenSliderPlugin<O, P, H>;\n\nfunction attachMoreDetails(prevSlide: number) {\n\treturn (slider: KeenSliderInstance) => {\n\t\tif (slider.track.details.rel !== slider.animator.targetIdx) return;\n\n\t\t(slider as KeenSlider).direction =\n\t\t\tslider.track.details.rel > prevSlide ? \"forward\" : \"backward\";\n\n\t\tprevSlide = slider.track.details.rel;\n\t};\n}\n\n/**\n * The `aria-label` this package last wrote on each slide.\n *\n * Module-level because the slider is rebuilt - by every `addPlugin`, by\n * `refresh-on-children-change`, and by a new `config` or `slide-label` - and\n * each rebuild is a fresh plugin with no memory of the last. Without this, a\n * rebuilt slider saw the labels its predecessor had written, took every one\n * for an authored label, and never touched them again: a changed template was\n * ignored, and a slide count that changed left \"1 of 3\" on a carousel of four.\n */\nconst writtenSlideLabels = new WeakMap<Element, string>();\n\n/**\n * APG's carousel pattern: a labelled group whose role description tells\n * assistive tech what it is, with each slide a group of its own.\n *\n * What this replaces was `role=\"listbox\"` with `role=\"option\"` slides, which\n * told a screen-reader user they were inside a form control they could select\n * from. It also made `aria-valuemin/max/now` invalid — a listbox has no value —\n * and left `aria-required-children` permanently unsatisfiable.\n *\n * The slide attributes used to be applied only from `created`, `slideChanged`,\n * `optionsChanged` and `updated`. `created` fires inside `super()`, before the\n * constructor gets here to subscribe, so on a carousel nobody ever interacted\n * with the slides got no ARIA at all — which is exactly why the container\n * advertised owned `option` children it did not have.\n *\n * `slideLabel` is the `slide-label` template. Each slide's `aria-label` was\n * hard-coded English, so a theme on any other locale could only translate it\n * by authoring a label on every slide by hand.\n */\nconst attachAriaAttributes = (\n\tslider: KeenSliderInstance,\n\tslideLabel: string | undefined,\n) => {\n\tconst container = slider.container;\n\n\t// Anything the markup already said is the author's. Recorded before the\n\t// first write, and checked against what an earlier instance wrote, because\n\t// otherwise every label this package wrote looks authored.\n\tconst authoredContainerLabel = container.hasAttribute(\"aria-label\");\n\tconst authoredSlideLabels = new WeakSet<Element>();\n\tfor (const slide of slider.slides) {\n\t\tconst label = slide.getAttribute(\"aria-label\");\n\t\tif (label !== null && label !== writtenSlideLabels.get(slide)) {\n\t\t\tauthoredSlideLabels.add(slide);\n\t\t}\n\t}\n\n\tcontainer.setAttribute(\"role\", \"group\");\n\tcontainer.setAttribute(\"aria-roledescription\", \"carousel\");\n\tif (!authoredContainerLabel) {\n\t\tcontainer.setAttribute(\"aria-label\", \"Carousel\");\n\t}\n\t// `false`, so moving a slide announces that slide rather than re-reading\n\t// the whole carousel. `aria-live` itself belongs to the auto-rotation\n\t// handling, which knows whether announcing is wanted at all.\n\tcontainer.setAttribute(\"aria-atomic\", \"false\");\n\tif (!container.hasAttribute(\"tabindex\")) {\n\t\tcontainer.setAttribute(\"tabindex\", \"0\");\n\t}\n\n\t// These were written by the previous version of this plugin and are not\n\t// valid on `group`. A theme has no reason to have set them itself.\n\tfor (const attribute of [\n\t\t\"aria-multiselectable\",\n\t\t\"aria-valuemin\",\n\t\t\"aria-valuemax\",\n\t\t\"aria-valuenow\",\n\t\t\"aria-orientation\",\n\t\t\"aria-busy\",\n\t]) {\n\t\tcontainer.removeAttribute(attribute);\n\t}\n\n\t/** The fraction of a slide that is in view, 0 when it is fully outside. */\n\tfunction visiblePortion(index: number) {\n\t\treturn slider.track?.details?.slides?.[index]?.portion ?? 0;\n\t}\n\n\tfunction updateTabIndexes(slide: HTMLElement, tabindex: string) {\n\t\tconst focusable = Array.from(\n\t\t\tslide.querySelectorAll(\n\t\t\t\t\"input, a, button:not(:disabled), textarea, select, [tabindex]\",\n\t\t\t),\n\t\t);\n\t\tfor (const element of focusable) {\n\t\t\telement.setAttribute(\"tabindex\", tabindex);\n\t\t}\n\t}\n\n\tfunction updateSlideAriaAttributes(slider: _KeenSliderInstance) {\n\t\tconst total = slider.slides.length;\n\t\tconst disabled = Boolean(slider.options?.disabled);\n\n\t\tslider.slides.forEach((slide, index) => {\n\t\t\tslide.setAttribute(\"role\", \"group\");\n\t\t\tslide.setAttribute(\"aria-roledescription\", \"slide\");\n\t\t\tif (!authoredSlideLabels.has(slide)) {\n\t\t\t\tconst label = formatSlideLabel(slideLabel, index + 1, total);\n\t\t\t\tslide.setAttribute(\"aria-label\", label);\n\t\t\t\twrittenSlideLabels.set(slide, label);\n\t\t\t}\n\t\t\t// Carried over from the listbox model; meaningless on a slide.\n\t\t\tslide.removeAttribute(\"aria-selected\");\n\t\t\t// A slide is a group, not a tab stop. Its contents still are.\n\t\t\tslide.removeAttribute(\"tabindex\");\n\n\t\t\tconst visible = disabled || visiblePortion(index) > 0;\n\n\t\t\t// The styling contract. ARIA is not one: keying CSS off `aria-hidden`\n\t\t\t// is what made removing it a silent breaking change for every theme\n\t\t\t// descended from the skeleton.\n\t\t\tslide.setAttribute(\"data-slide-visible\", `${visible}`);\n\n\t\t\t// `aria-hidden` only where the slide is genuinely out of view, and\n\t\t\t// never over the focused element — hiding focused content strands the\n\t\t\t// user, which is what axe's `aria-hidden-focus` rule is about. The\n\t\t\t// old code hid every inactive slide, so in any multi-slide-per-view\n\t\t\t// carousel it hid slides the user could plainly see.\n\t\t\tconst holdsFocus =\n\t\t\t\tdocument.activeElement !== null &&\n\t\t\t\tslide.contains(document.activeElement);\n\t\t\tif (!visible && !holdsFocus) slide.setAttribute(\"aria-hidden\", \"true\");\n\t\t\telse slide.removeAttribute(\"aria-hidden\");\n\n\t\t\tupdateTabIndexes(slide, visible ? \"0\" : \"-1\");\n\t\t});\n\t}\n\n\tslider.on(\"slideChanged\", updateSlideAriaAttributes);\n\tslider.on(\"created\", updateSlideAriaAttributes);\n\tslider.on(\"optionsChanged\", updateSlideAriaAttributes);\n\tslider.on(\"updated\", updateSlideAriaAttributes);\n\n\t// `created` has already fired by the time this plugin is attached, so the\n\t// subscriptions above would otherwise leave the first render unwritten.\n\tupdateSlideAriaAttributes(slider);\n};\n\n/**\n * Anything that takes typed input. Arrow keys inside one of these belong to\n * the field, not the carousel — without this guard a search box inside a slide\n * cannot be navigated with the keyboard.\n */\nconst TEXT_ENTRY =\n\t'input, textarea, select, [contenteditable=\"\"], [contenteditable=\"true\"]';\n\n/**\n * Arrow-key navigation, and the `aria-live` rule for auto-rotation.\n *\n * The container has carried `tabindex=\"0\"` for as long as the ARIA plugin has\n * existed, with nothing listening — so it took a tab stop and did nothing with\n * it, which is worse than not being focusable. This is the handler that makes\n * the tab stop worth having.\n *\n * `aria-live` was previously hardcoded to `polite` on the container (twice).\n * APG is explicit that a live region on an auto-rotating carousel announces\n * slides the user never asked for, so the announcement is tied to the rotation\n * state instead: silent while rotating, polite once paused and the user is\n * driving.\n */\nconst attachKeyboardNavigation: KeenSliderPlugin = (slider) => {\n\tconst container = slider.container;\n\tconst controller = new AbortController();\n\n\tconst isVertical = () => Boolean(slider.options?.vertical);\n\n\tfunction handleKeydown(event: KeyboardEvent) {\n\t\tif (slider.options?.disabled) return;\n\n\t\tconst target = event.target;\n\t\tif (target instanceof Element && target.closest(TEXT_ENTRY)) return;\n\n\t\tconst back = isVertical() ? \"ArrowUp\" : \"ArrowLeft\";\n\t\tconst forward = isVertical() ? \"ArrowDown\" : \"ArrowRight\";\n\n\t\tswitch (event.key) {\n\t\t\tcase back:\n\t\t\t\tevent.preventDefault();\n\t\t\t\tslider.prev();\n\t\t\t\treturn;\n\t\t\tcase forward:\n\t\t\t\tevent.preventDefault();\n\t\t\t\tslider.next();\n\t\t\t\treturn;\n\t\t\tcase \"Home\":\n\t\t\t\tevent.preventDefault();\n\t\t\t\tslider.moveToIdx(0);\n\t\t\t\treturn;\n\t\t\tcase \"End\": {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tconst details = slider.track?.details;\n\t\t\t\tconst last = Number.isFinite(details?.maxIdx)\n\t\t\t\t\t? (details?.maxIdx ?? 0)\n\t\t\t\t\t: slider.slides.length - 1;\n\t\t\t\tslider.moveToIdx(last);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * `keen-slider-auto-switch` already dispatches this, and the autoplay\n\t * package publishes its pause state the same way, so both drive one code\n\t * path here rather than each teaching the carousel about itself.\n\t */\n\tfunction handleRotationChange(event: Event) {\n\t\tconst detail = (event as CustomEvent<{ isPaused?: boolean }>).detail;\n\t\tconst rotating = detail?.isPaused === false;\n\t\tcontainer.setAttribute(\"aria-live\", rotating ? \"off\" : \"polite\");\n\t}\n\n\tcontainer.addEventListener(\"keydown\", handleKeydown, {\n\t\tsignal: controller.signal,\n\t});\n\tcontainer.addEventListener(\"autoswitch:update\", handleRotationChange, {\n\t\tsignal: controller.signal,\n\t});\n\tcontainer.addEventListener(\"autoplay:update\", handleRotationChange, {\n\t\tsignal: controller.signal,\n\t});\n\n\t// Nothing is auto-rotating until something says so, and a carousel the user\n\t// drives should announce where they have got to.\n\tif (!container.hasAttribute(\"aria-live\")) {\n\t\tcontainer.setAttribute(\"aria-live\", \"polite\");\n\t}\n\n\tslider.on(\"destroyed\", () => controller.abort());\n};\n\nexport type KeenSliderAccessibilityOptions = {\n\t/** Template for each slide's `aria-label`: `{n}` and `{total}`. */\n\tslideLabel?: string;\n};\n\nexport default class KeenSlider extends _KeenSlider {\n\tdirection: \"forward\" | \"backward\" | null = null;\n\tconstructor(\n\t\telement: ICustomElement & HTMLElement,\n\t\tconfig: KeenSliderOptions,\n\t\tplugins: _KeenSliderPlugin[] = [],\n\t\taccessibility: KeenSliderAccessibilityOptions = {},\n\t) {\n\t\tsuper(element, config, plugins);\n\t\tconst prevSlide = 0;\n\n\t\tthis.direction = null;\n\t\tthis.on(\"slideChanged\", attachMoreDetails(prevSlide) as any);\n\n\t\tattachAriaAttributes(this, accessibility.slideLabel);\n\t\tattachKeyboardNavigation(this);\n\n\t\tconst _next = this.next,\n\t\t\t_prev = this.prev;\n\n\t\tthis.next = function () {\n\t\t\tif (\n\t\t\t\ttypeof this.options.slides === \"number\" ||\n\t\t\t\ttypeof this.options.slides === \"function\"\n\t\t\t)\n\t\t\t\treturn _next();\n\t\t\tlet amount: number = 1;\n\t\t\tif (this.options?.slides && \"perScroll\" in this.options.slides === true) {\n\t\t\t\tamount = (this.options?.slides?.perScroll as number) || 1;\n\t\t\t}\n\t\t\tif (!this.options.loop) {\n\t\t\t\treturn this.moveToIdx(\n\t\t\t\t\tconstrain(\n\t\t\t\t\t\tthis.track.details.rel + amount,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\tthis.track.details.maxIdx,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn this.moveToIdx(constrain(this.track.details.abs + amount), true);\n\t\t};\n\n\t\tthis.prev = function () {\n\t\t\tif (\n\t\t\t\ttypeof this.options.slides === \"number\" ||\n\t\t\t\ttypeof this.options.slides === \"function\"\n\t\t\t)\n\t\t\t\treturn _prev();\n\t\t\tlet amount: number = 1;\n\t\t\tif (this.options?.slides && \"perScroll\" in this.options.slides === true) {\n\t\t\t\tamount = (this.options?.slides?.perScroll as number) || 1;\n\t\t\t}\n\t\t\tif (!this.options.loop) {\n\t\t\t\tthis.moveToIdx(\n\t\t\t\t\tconstrain(\n\t\t\t\t\t\tthis.track.details.rel - amount,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\tthis.track.details.maxIdx,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn this.moveToIdx(constrain(this.track.details.abs - amount), true);\n\t\t};\n\t}\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"keen-slider.js","names":[],"sources":["../../../src/components/keen-slider.ts"],"sourcesContent":["import type { CorrectComponentType } from \"@brytdesigns/web-component-core/utils\";\nimport { createGuardedEffect } from \"@brytdesigns/web-component-core/utils\";\n\nimport { createRoot } from \"solid-js\";\nimport {\n\tprovideKeenSliderContext,\n\tuseKeenSliderContext,\n} from \"../hooks/index.js\";\nimport _KeenSlider, {\n\ttype KeenSliderInstance,\n\ttype KeenSliderOptions,\n} from \"../KeenSlider.js\";\n\nexport type Props = {\n\tconfig?: KeenSliderOptions;\n\tcenterSlides: boolean;\n\trefreshOnChildrenChange: boolean;\n};\n\nexport const Name = `keen-slider`;\n\nexport const Component: CorrectComponentType<Props> = (props, { element }) => {\n\tconst context = provideKeenSliderContext(props, element);\n\tconst [slider, methods] = useKeenSliderContext(context);\n\n\tObject.assign(element, {\n\t\t// `observable` is gone in v2; pushing a signal out to an external\n\t\t// subscriber is now an effect inside its own root, so the returned\n\t\t// disposer is the unsubscribe. The public signature is unchanged.\n\t\tsubscribe(callback = (_: KeenSliderInstance) => null) {\n\t\t\tfunction listen(slider: KeenSliderInstance) {\n\t\t\t\tconst cleanup = callback(slider);\n\t\t\t\tslider.on(\n\t\t\t\t\t\"destroyed\",\n\t\t\t\t\ttypeof cleanup === \"function\" ? cleanup : () => null,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn createRoot((dispose) => {\n\t\t\t\tcreateGuardedEffect(slider, listen);\n\t\t\t\treturn dispose;\n\t\t\t});\n\t\t},\n\t});\n\n\tObject.assign(element, { slider });\n\tObject.assign(element, { addPlugin: methods.addPlugin });\n};\n"],"mappings":";;;;;;AAmBA,IAAa,OAAO;AAEpB,IAAa,aAA0C,OAAO,EAAE,cAAc;CAC7E,MAAM,UAAU,yBAAyB,OAAO,OAAO;CACvD,MAAM,CAAC,QAAQ,WAAW,qBAAqB,OAAO;CAEtD,OAAO,OAAO,SAAS,EAItB,UAAU,YAAY,MAA0B,MAAM;EACrD,SAAS,OAAO,QAA4B;GAC3C,MAAM,UAAU,SAAS,MAAM;GAC/B,OAAO,GACN,aACA,OAAO,YAAY,aAAa,gBAAgB,IACjD;EACD;EACA,OAAO,YAAY,YAAY;GAC9B,oBAAoB,QAAQ,MAAM;GAClC,OAAO;EACR,CAAC;CACF,EACD,CAAC;CAED,OAAO,OAAO,SAAS,EAAE,OAAO,CAAC;CACjC,OAAO,OAAO,SAAS,EAAE,WAAW,QAAQ,UAAU,CAAC;AACxD"}
1
+ {"version":3,"file":"keen-slider.js","names":[],"sources":["../../../src/components/keen-slider.ts"],"sourcesContent":["import type { CorrectComponentType } from \"@brytdesigns/web-component-core/utils\";\nimport { createGuardedEffect } from \"@brytdesigns/web-component-core/utils\";\n\nimport { createRoot } from \"solid-js\";\nimport {\n\tprovideKeenSliderContext,\n\tuseKeenSliderContext,\n} from \"../hooks/index.js\";\nimport _KeenSlider, {\n\ttype KeenSliderInstance,\n\ttype KeenSliderOptions,\n} from \"../KeenSlider.js\";\n\nexport type Props = {\n\tconfig?: KeenSliderOptions;\n\tcenterSlides: boolean;\n\trefreshOnChildrenChange: boolean;\n\t/**\n\t * Each slide's `aria-label`. `{n}` is replaced with the slide number and\n\t * `{total}` with the slide count. Translate it rather than reaching in from\n\t * a theme. Slides that already carry an `aria-label` keep their own.\n\t */\n\tslideLabel: string;\n};\n\nexport const Name = `keen-slider`;\n\nexport const Component: CorrectComponentType<Props> = (props, { element }) => {\n\tconst context = provideKeenSliderContext(props, element);\n\tconst [slider, methods] = useKeenSliderContext(context);\n\n\tObject.assign(element, {\n\t\t// `observable` is gone in v2; pushing a signal out to an external\n\t\t// subscriber is now an effect inside its own root, so the returned\n\t\t// disposer is the unsubscribe. The public signature is unchanged.\n\t\tsubscribe(callback = (_: KeenSliderInstance) => null) {\n\t\t\tfunction listen(slider: KeenSliderInstance) {\n\t\t\t\tconst cleanup = callback(slider);\n\t\t\t\tslider.on(\n\t\t\t\t\t\"destroyed\",\n\t\t\t\t\ttypeof cleanup === \"function\" ? cleanup : () => null,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn createRoot((dispose) => {\n\t\t\t\tcreateGuardedEffect(slider, listen);\n\t\t\t\treturn dispose;\n\t\t\t});\n\t\t},\n\t});\n\n\tObject.assign(element, { slider });\n\tObject.assign(element, { addPlugin: methods.addPlugin });\n};\n"],"mappings":";;;;;;AAyBA,IAAa,OAAO;AAEpB,IAAa,aAA0C,OAAO,EAAE,cAAc;CAC7E,MAAM,UAAU,yBAAyB,OAAO,OAAO;CACvD,MAAM,CAAC,QAAQ,WAAW,qBAAqB,OAAO;CAEtD,OAAO,OAAO,SAAS,EAItB,UAAU,YAAY,MAA0B,MAAM;EACrD,SAAS,OAAO,QAA4B;GAC3C,MAAM,UAAU,SAAS,MAAM;GAC/B,OAAO,GACN,aACA,OAAO,YAAY,aAAa,gBAAgB,IACjD;EACD;EACA,OAAO,YAAY,YAAY;GAC9B,oBAAoB,QAAQ,MAAM;GAClC,OAAO;EACR,CAAC;CACF,EACD,CAAC;CAED,OAAO,OAAO,SAAS,EAAE,OAAO,CAAC;CACjC,OAAO,OAAO,SAAS,EAAE,WAAW,QAAQ,UAAU,CAAC;AACxD"}
@@ -4,7 +4,7 @@ import { createMemo, createSignal, merge, omit, onCleanup } from "solid-js";
4
4
  import { consume, createContext, provide } from "component-register";
5
5
  //#region src/hooks/useKeenSlider.ts
6
6
  function initializeKeenSliderContext(props) {
7
- const publicProps = omit(props, "root", "centerSlides", "refreshOnChildrenChange");
7
+ const publicProps = omit(props, "root", "centerSlides", "refreshOnChildrenChange", "slideLabel");
8
8
  const [plugins, setPlugins] = createSignal([]);
9
9
  const [forceSliderRebuild, setForceSliderRebuild] = createSignal(false);
10
10
  const mutObserver = createMemo(() => {
@@ -17,7 +17,7 @@ function initializeKeenSliderContext(props) {
17
17
  });
18
18
  const slider = createMemo(() => {
19
19
  props.root.setAttribute("data-initialized", "true");
20
- const slider = new KeenSlider(props.root, publicProps.config || {}, plugins());
20
+ const slider = new KeenSlider(props.root, publicProps.config || {}, plugins(), { slideLabel: props.slideLabel });
21
21
  const m = mutObserver();
22
22
  if (m) {
23
23
  m.observe(slider.container, { childList: true });
@@ -1 +1 @@
1
- {"version":3,"file":"useKeenSlider.js","names":[],"sources":["../../../src/hooks/useKeenSlider.ts"],"sourcesContent":["import {\n\tcreateGuardedEffect,\n\tcreateRemoteContext,\n\tgetContextFromProvider,\n\tinvokeOnLoaded,\n} from \"@brytdesigns/web-component-core/utils\";\nimport {\n\tconsume,\n\tcreateContext,\n\ttype ICustomElement,\n\tprovide,\n} from \"component-register\";\nimport { createMemo, createSignal, merge, omit, onCleanup } from \"solid-js\";\n\nimport KeenSlider, {\n\ttype KeenSliderInstance,\n\ttype KeenSliderOptions,\n\ttype KeenSliderPlugin,\n} from \"../KeenSlider\";\n\ntype CreateContextOptions = {\n\troot: HTMLElement & ICustomElement;\n\tconfig?: KeenSliderOptions;\n\tcenterSlides?: boolean;\n\trefreshOnChildrenChange?: boolean;\n};\n\ntype WalkableNode = Parameters<typeof provide>[2];\n\ntype KeenSliderContext = ReturnType<typeof initializeKeenSliderContext>;\n\nfunction initializeKeenSliderContext(props: CreateContextOptions) {\n\t// `omit` returns only the rest; picked keys are read straight off `props`.\n\tconst publicProps = omit(\n\t\tprops,\n\t\t\"root\",\n\t\t\"centerSlides\",\n\t\t\"refreshOnChildrenChange\",\n\t);\n\n\tconst [plugins, setPlugins] = createSignal<KeenSliderPlugin[]>([]);\n\tconst [forceSliderRebuild, setForceSliderRebuild] = createSignal(false);\n\n\tconst mutObserver = createMemo(() => {\n\t\tif (props.refreshOnChildrenChange)\n\t\t\treturn new MutationObserver((mutations) => {\n\t\t\t\tmutations.forEach((_) => {\n\t\t\t\t\tsetForceSliderRebuild((v) => !v);\n\t\t\t\t});\n\t\t\t});\n\t\treturn null;\n\t});\n\n\tconst slider = createMemo(() => {\n\t\tprops.root.setAttribute(\"data-initialized\", \"true\");\n\t\tconst slider = new KeenSlider(\n\t\t\tprops.root,\n\t\t\tpublicProps.config || ({} as any),\n\t\t\tplugins(),\n\t\t);\n\t\tconst m = mutObserver();\n\n\t\tif (m) {\n\t\t\tm.observe(slider.container, { childList: true });\n\t\t\tforceSliderRebuild();\n\t\t}\n\n\t\tonCleanup(() => {\n\t\t\tslider.destroy();\n\t\t\tprops.root.setAttribute(\"data-initialized\", \"false\");\n\t\t\tif (m) m.disconnect();\n\t\t});\n\n\t\treturn slider;\n\t});\n\n\tfunction addPlugin(plugin: KeenSliderPlugin) {\n\t\tlet index = -1;\n\n\t\tsetPlugins((plugins) => {\n\t\t\tindex = plugins.length;\n\t\t\treturn [...plugins, plugin];\n\t\t});\n\n\t\treturn () => {\n\t\t\tsetPlugins((plugins) => {\n\t\t\t\treturn [...plugins.slice(0, index), ...plugins.slice(index + 1)];\n\t\t\t});\n\t\t};\n\t}\n\n\tconst centeredPlugin: KeenSliderPlugin = (slider) => {\n\t\tfunction centerSlides(slider: KeenSliderInstance) {\n\t\t\tif (slider.track.details.maxIdx > 0) {\n\t\t\t\t//@ts-expect-error\n\t\t\t\tslider.container.style.justifyContent = null;\n\t\t\t\t//@ts-expect-error\n\t\t\t\tslider.container.style.transform = null;\n\t\t\t} else {\n\t\t\t\tslider.container.style.cssText = `\n justify-content: center;\n transform: translateX(-${(slider.options.slides as any)?.spacing || 16}px)\n `;\n\t\t\t}\n\t\t}\n\n\t\tslider.on(\"created\", centerSlides);\n\t\tslider.on(\"optionsChanged\", centerSlides);\n\t};\n\n\tcreateGuardedEffect(\n\t\t() => props.centerSlides,\n\t\t(centerSlides) => {\n\t\t\tif (!centerSlides) return;\n\t\t\tconst remove = addPlugin(centeredPlugin);\n\t\t\treturn remove;\n\t\t},\n\t);\n\n\tcreateGuardedEffect(plugins, (plugins) => {\n\t\tif (plugins.length === 0) return;\n\t\tconsole.log(\n\t\t\t`Registered a new plugin on element.\\n`,\n\t\t\t`Total slider plugins: ${plugins.length}`,\n\t\t);\n\t});\n\n\treturn [slider, { addPlugin }] as const;\n}\n\nconst KeenSliderContextState = createContext(initializeKeenSliderContext);\n\nexport const provideKeenSliderContext = (\n\tinitialState: Omit<CreateContextOptions, \"root\">,\n\telement: WalkableNode,\n): KeenSliderContext => {\n\tconst props = merge(initialState, { root: element });\n\treturn provide(KeenSliderContextState, props, element);\n};\n\nexport const useKeenSliderContext = (context: KeenSliderContext) => {\n\treturn context;\n};\n\nexport const useKeenSlider = (element: HTMLElement & ICustomElement) => {\n\tconst context: KeenSliderContext = consume(KeenSliderContextState, element);\n\n\tif (!context) {\n\t\tthrow new Error(\n\t\t\t\"KeenSliderContext not found! Please ensure to wrap your custom element with keen-slider element.\",\n\t\t);\n\t}\n\n\treturn useKeenSliderContext(context);\n};\n\nexport const getKeenSliderContext = (element: Element) => {\n\tconst context = getContextFromProvider<KeenSliderContext>(\n\t\tKeenSliderContextState,\n\t\telement,\n\t);\n\treturn useKeenSliderContext(context);\n};\n\ntype AddPluginOptions = {\n\ttarget: void | HTMLElement;\n\tplugin: KeenSliderPlugin;\n\tcontroller?: AbortController;\n};\n\nexport function addPlugin({ target, plugin, controller }: AddPluginOptions) {\n\tif (!target) return;\n\n\tinvokeOnLoaded(\n\t\t() => {\n\t\t\tconst [_, { addPlugin }] = getKeenSliderContext(target);\n\t\t\tconst removePlugin = addPlugin(plugin);\n\t\t\tcontroller?.signal?.addEventListener(\"abort\", removePlugin);\n\t\t},\n\t\t{\n\t\t\tsignal: controller?.signal,\n\t\t},\n\t);\n}\n\nexport const useKeenSliderAt = createRemoteContext<KeenSliderContext>(\n\tKeenSliderContextState,\n\t{\n\t\t// A target may name the slider itself or an ancestor of it. Stated once\n\t\t// here instead of the identical ten-line thunk that was copy-pasted into\n\t\t// all six extension packages.\n\t\tresolve: (target, host) => {\n\t\t\tconst el = target ? document.querySelector(target) : host;\n\t\t\tif (!el) return null;\n\t\t\treturn el.tagName === \"KEEN-SLIDER\"\n\t\t\t\t? el\n\t\t\t\t: el.querySelector(\"keen-slider\");\n\t\t},\n\t},\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"useKeenSlider.js","names":[],"sources":["../../../src/hooks/useKeenSlider.ts"],"sourcesContent":["import {\n\tcreateGuardedEffect,\n\tcreateRemoteContext,\n\tgetContextFromProvider,\n\tinvokeOnLoaded,\n} from \"@brytdesigns/web-component-core/utils\";\nimport {\n\tconsume,\n\tcreateContext,\n\ttype ICustomElement,\n\tprovide,\n} from \"component-register\";\nimport { createMemo, createSignal, merge, omit, onCleanup } from \"solid-js\";\n\nimport KeenSlider, {\n\ttype KeenSliderInstance,\n\ttype KeenSliderOptions,\n\ttype KeenSliderPlugin,\n} from \"../KeenSlider\";\n\ntype CreateContextOptions = {\n\troot: HTMLElement & ICustomElement;\n\tconfig?: KeenSliderOptions;\n\tcenterSlides?: boolean;\n\trefreshOnChildrenChange?: boolean;\n\tslideLabel?: string;\n};\n\ntype WalkableNode = Parameters<typeof provide>[2];\n\ntype KeenSliderContext = ReturnType<typeof initializeKeenSliderContext>;\n\nfunction initializeKeenSliderContext(props: CreateContextOptions) {\n\t// `omit` returns only the rest; picked keys are read straight off `props`.\n\tconst publicProps = omit(\n\t\tprops,\n\t\t\"root\",\n\t\t\"centerSlides\",\n\t\t\"refreshOnChildrenChange\",\n\t\t\"slideLabel\",\n\t);\n\n\tconst [plugins, setPlugins] = createSignal<KeenSliderPlugin[]>([]);\n\tconst [forceSliderRebuild, setForceSliderRebuild] = createSignal(false);\n\n\tconst mutObserver = createMemo(() => {\n\t\tif (props.refreshOnChildrenChange)\n\t\t\treturn new MutationObserver((mutations) => {\n\t\t\t\tmutations.forEach((_) => {\n\t\t\t\t\tsetForceSliderRebuild((v) => !v);\n\t\t\t\t});\n\t\t\t});\n\t\treturn null;\n\t});\n\n\tconst slider = createMemo(() => {\n\t\tprops.root.setAttribute(\"data-initialized\", \"true\");\n\t\t// Read here, so it is tracked: the template is captured when the\n\t\t// slider is built, so a new one rebuilds it, the same as a new\n\t\t// `config` does.\n\t\tconst slider = new KeenSlider(\n\t\t\tprops.root,\n\t\t\tpublicProps.config || ({} as any),\n\t\t\tplugins(),\n\t\t\t{ slideLabel: props.slideLabel },\n\t\t);\n\t\tconst m = mutObserver();\n\n\t\tif (m) {\n\t\t\tm.observe(slider.container, { childList: true });\n\t\t\tforceSliderRebuild();\n\t\t}\n\n\t\tonCleanup(() => {\n\t\t\tslider.destroy();\n\t\t\tprops.root.setAttribute(\"data-initialized\", \"false\");\n\t\t\tif (m) m.disconnect();\n\t\t});\n\n\t\treturn slider;\n\t});\n\n\tfunction addPlugin(plugin: KeenSliderPlugin) {\n\t\tlet index = -1;\n\n\t\tsetPlugins((plugins) => {\n\t\t\tindex = plugins.length;\n\t\t\treturn [...plugins, plugin];\n\t\t});\n\n\t\treturn () => {\n\t\t\tsetPlugins((plugins) => {\n\t\t\t\treturn [...plugins.slice(0, index), ...plugins.slice(index + 1)];\n\t\t\t});\n\t\t};\n\t}\n\n\tconst centeredPlugin: KeenSliderPlugin = (slider) => {\n\t\tfunction centerSlides(slider: KeenSliderInstance) {\n\t\t\tif (slider.track.details.maxIdx > 0) {\n\t\t\t\t//@ts-expect-error\n\t\t\t\tslider.container.style.justifyContent = null;\n\t\t\t\t//@ts-expect-error\n\t\t\t\tslider.container.style.transform = null;\n\t\t\t} else {\n\t\t\t\tslider.container.style.cssText = `\n justify-content: center;\n transform: translateX(-${(slider.options.slides as any)?.spacing || 16}px)\n `;\n\t\t\t}\n\t\t}\n\n\t\tslider.on(\"created\", centerSlides);\n\t\tslider.on(\"optionsChanged\", centerSlides);\n\t};\n\n\tcreateGuardedEffect(\n\t\t() => props.centerSlides,\n\t\t(centerSlides) => {\n\t\t\tif (!centerSlides) return;\n\t\t\tconst remove = addPlugin(centeredPlugin);\n\t\t\treturn remove;\n\t\t},\n\t);\n\n\tcreateGuardedEffect(plugins, (plugins) => {\n\t\tif (plugins.length === 0) return;\n\t\tconsole.log(\n\t\t\t`Registered a new plugin on element.\\n`,\n\t\t\t`Total slider plugins: ${plugins.length}`,\n\t\t);\n\t});\n\n\treturn [slider, { addPlugin }] as const;\n}\n\nconst KeenSliderContextState = createContext(initializeKeenSliderContext);\n\nexport const provideKeenSliderContext = (\n\tinitialState: Omit<CreateContextOptions, \"root\">,\n\telement: WalkableNode,\n): KeenSliderContext => {\n\tconst props = merge(initialState, { root: element });\n\treturn provide(KeenSliderContextState, props, element);\n};\n\nexport const useKeenSliderContext = (context: KeenSliderContext) => {\n\treturn context;\n};\n\nexport const useKeenSlider = (element: HTMLElement & ICustomElement) => {\n\tconst context: KeenSliderContext = consume(KeenSliderContextState, element);\n\n\tif (!context) {\n\t\tthrow new Error(\n\t\t\t\"KeenSliderContext not found! Please ensure to wrap your custom element with keen-slider element.\",\n\t\t);\n\t}\n\n\treturn useKeenSliderContext(context);\n};\n\nexport const getKeenSliderContext = (element: Element) => {\n\tconst context = getContextFromProvider<KeenSliderContext>(\n\t\tKeenSliderContextState,\n\t\telement,\n\t);\n\treturn useKeenSliderContext(context);\n};\n\ntype AddPluginOptions = {\n\ttarget: void | HTMLElement;\n\tplugin: KeenSliderPlugin;\n\tcontroller?: AbortController;\n};\n\nexport function addPlugin({ target, plugin, controller }: AddPluginOptions) {\n\tif (!target) return;\n\n\tinvokeOnLoaded(\n\t\t() => {\n\t\t\tconst [_, { addPlugin }] = getKeenSliderContext(target);\n\t\t\tconst removePlugin = addPlugin(plugin);\n\t\t\tcontroller?.signal?.addEventListener(\"abort\", removePlugin);\n\t\t},\n\t\t{\n\t\t\tsignal: controller?.signal,\n\t\t},\n\t);\n}\n\nexport const useKeenSliderAt = createRemoteContext<KeenSliderContext>(\n\tKeenSliderContextState,\n\t{\n\t\t// A target may name the slider itself or an ancestor of it. Stated once\n\t\t// here instead of the identical ten-line thunk that was copy-pasted into\n\t\t// all six extension packages.\n\t\tresolve: (target, host) => {\n\t\t\tconst el = target ? document.querySelector(target) : host;\n\t\t\tif (!el) return null;\n\t\t\treturn el.tagName === \"KEEN-SLIDER\"\n\t\t\t\t? el\n\t\t\t\t: el.querySelector(\"keen-slider\");\n\t\t},\n\t},\n);\n"],"mappings":";;;;;AAgCA,SAAS,4BAA4B,OAA6B;CAEjE,MAAM,cAAc,KACnB,OACA,QACA,gBACA,2BACA,YACD;CAEA,MAAM,CAAC,SAAS,cAAc,aAAiC,CAAC,CAAC;CACjE,MAAM,CAAC,oBAAoB,yBAAyB,aAAa,KAAK;CAEtE,MAAM,cAAc,iBAAiB;EACpC,IAAI,MAAM,yBACT,OAAO,IAAI,kBAAkB,cAAc;GAC1C,UAAU,SAAS,MAAM;IACxB,uBAAuB,MAAM,CAAC,CAAC;GAChC,CAAC;EACF,CAAC;EACF,OAAO;CACR,CAAC;CAED,MAAM,SAAS,iBAAiB;EAC/B,MAAM,KAAK,aAAa,oBAAoB,MAAM;EAIlD,MAAM,SAAS,IAAI,WAClB,MAAM,MACN,YAAY,UAAW,CAAC,GACxB,QAAQ,GACR,EAAE,YAAY,MAAM,WAAW,CAChC;EACA,MAAM,IAAI,YAAY;EAEtB,IAAI,GAAG;GACN,EAAE,QAAQ,OAAO,WAAW,EAAE,WAAW,KAAK,CAAC;GAC/C,mBAAmB;EACpB;EAEA,gBAAgB;GACf,OAAO,QAAQ;GACf,MAAM,KAAK,aAAa,oBAAoB,OAAO;GACnD,IAAI,GAAG,EAAE,WAAW;EACrB,CAAC;EAED,OAAO;CACR,CAAC;CAED,SAAS,UAAU,QAA0B;EAC5C,IAAI,QAAQ;EAEZ,YAAY,YAAY;GACvB,QAAQ,QAAQ;GAChB,OAAO,CAAC,GAAG,SAAS,MAAM;EAC3B,CAAC;EAED,aAAa;GACZ,YAAY,YAAY;IACvB,OAAO,CAAC,GAAG,QAAQ,MAAM,GAAG,KAAK,GAAG,GAAG,QAAQ,MAAM,QAAQ,CAAC,CAAC;GAChE,CAAC;EACF;CACD;CAEA,MAAM,kBAAoC,WAAW;EACpD,SAAS,aAAa,QAA4B;GACjD,IAAI,OAAO,MAAM,QAAQ,SAAS,GAAG;IAEpC,OAAO,UAAU,MAAM,iBAAiB;IAExC,OAAO,UAAU,MAAM,YAAY;GACpC,OACC,OAAO,UAAU,MAAM,UAAU;;mCAED,OAAO,QAAQ,QAAgB,WAAW,GAAG;;EAG/E;EAEA,OAAO,GAAG,WAAW,YAAY;EACjC,OAAO,GAAG,kBAAkB,YAAY;CACzC;CAEA,0BACO,MAAM,eACX,iBAAiB;EACjB,IAAI,CAAC,cAAc;EAEnB,OADe,UAAU,cAClB;CACR,CACD;CAEA,oBAAoB,UAAU,YAAY;EACzC,IAAI,QAAQ,WAAW,GAAG;EAC1B,QAAQ,IACP,yCACA,yBAAyB,QAAQ,QAClC;CACD,CAAC;CAED,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC;AAC9B;AAEA,IAAM,yBAAyB,cAAc,2BAA2B;AAExE,IAAa,4BACZ,cACA,YACuB;CACvB,MAAM,QAAQ,MAAM,cAAc,EAAE,MAAM,QAAQ,CAAC;CACnD,OAAO,QAAQ,wBAAwB,OAAO,OAAO;AACtD;AAEA,IAAa,wBAAwB,YAA+B;CACnE,OAAO;AACR;AAEA,IAAa,iBAAiB,YAA0C;CACvE,MAAM,UAA6B,QAAQ,wBAAwB,OAAO;CAE1E,IAAI,CAAC,SACJ,MAAM,IAAI,MACT,kGACD;CAGD,OAAO,qBAAqB,OAAO;AACpC;AAEA,IAAa,wBAAwB,YAAqB;CAKzD,OAAO,qBAJS,uBACf,wBACA,OAE2B,CAAO;AACpC;AAQA,SAAgB,UAAU,EAAE,QAAQ,QAAQ,cAAgC;CAC3E,IAAI,CAAC,QAAQ;CAEb,qBACO;EACL,MAAM,CAAC,GAAG,EAAE,eAAe,qBAAqB,MAAM;EACtD,MAAM,eAAe,UAAU,MAAM;EACrC,YAAY,QAAQ,iBAAiB,SAAS,YAAY;CAC3D,GACA,EACC,QAAQ,YAAY,OACrB,CACD;AACD;AAEA,IAAa,kBAAkB,oBAC9B,wBACA,EAIC,UAAU,QAAQ,SAAS;CAC1B,MAAM,KAAK,SAAS,SAAS,cAAc,MAAM,IAAI;CACrD,IAAI,CAAC,IAAI,OAAO;CAChB,OAAO,GAAG,YAAY,gBACnB,KACA,GAAG,cAAc,aAAa;AAClC,EACD,CACD"}
@@ -20,14 +20,24 @@ export type KeenSliderOptions<O = {}, P = {}, H extends _KeenSliderHooks = _Keen
20
20
  } & _KeenSliderOptions<O, P, H>["slides"];
21
21
  };
22
22
  export type KeenSliderPlugin<O = {}, P = {}, H extends _KeenSliderHooks = _KeenSliderHooks> = _KeenSliderPlugin<O, P, H>;
23
+ export type KeenSliderAccessibilityOptions = {
24
+ /** Template for each slide's `aria-label`: `{n}` and `{total}`. */
25
+ slideLabel?: string;
26
+ };
23
27
  declare class KeenSlider extends _KeenSlider {
24
28
  direction: "forward" | "backward" | null;
25
- constructor(element: ICustomElement & HTMLElement, config: KeenSliderOptions, plugins?: _KeenSliderPlugin[]);
29
+ constructor(element: ICustomElement & HTMLElement, config: KeenSliderOptions, plugins?: _KeenSliderPlugin[], accessibility?: KeenSliderAccessibilityOptions);
26
30
  }
27
31
  export type Props = {
28
32
  config?: KeenSliderOptions;
29
33
  centerSlides: boolean;
30
34
  refreshOnChildrenChange: boolean;
35
+ /**
36
+ * Each slide's `aria-label`. `{n}` is replaced with the slide number and
37
+ * `{total}` with the slide count. Translate it rather than reaching in from
38
+ * a theme. Slides that already carry an `aria-label` keep their own.
39
+ */
40
+ slideLabel: string;
31
41
  };
32
42
  declare const Name = "keen-slider";
33
43
  declare const Component: CorrectComponentType<Props>;
@@ -1,3 +1,4 @@
1
+ import { DEFAULT_SLIDE_LABEL } from "./utils.js";
1
2
  import { addPlugin, getKeenSliderContext, useKeenSlider, useKeenSliderAt } from "./hooks/useKeenSlider.js";
2
3
  import "./hooks/index.js";
3
4
  import { Component, Name } from "./components/keen-slider.js";
@@ -7,7 +8,8 @@ import { customShadowlessElement } from "@brytdesigns/web-component-core/utils";
7
8
  customShadowlessElement(Name, {
8
9
  config: {},
9
10
  centerSlides: false,
10
- refreshOnChildrenChange: false
11
+ refreshOnChildrenChange: false,
12
+ slideLabel: DEFAULT_SLIDE_LABEL
11
13
  }, Component);
12
14
  //#endregion
13
15
  export { addPlugin, getKeenSliderContext, useKeenSlider, useKeenSliderAt };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/index.ts"],"sourcesContent":["import { customShadowlessElement } from \"@brytdesigns/web-component-core/utils\";\nimport { KeenSlider } from \"./components/index.js\";\n\nexport type {\n\tKeenSliderInstance,\n\tKeenSliderOptions,\n\tKeenSliderPlugin,\n} from \"./KeenSlider.js\";\n\nexport type KeenSliderProps = KeenSlider.Props;\n\nexport {\n\taddPlugin,\n\tgetKeenSliderContext,\n\tuseKeenSlider,\n\tuseKeenSliderAt,\n} from \"./hooks/index.js\";\n\ncustomShadowlessElement(\n\tKeenSlider.Name,\n\t{\n\t\t// @ts-expect-error\n\t\tconfig: {},\n\t\tcenterSlides: false,\n\t\trefreshOnChildrenChange: false,\n\t},\n\tKeenSlider.Component,\n);\n"],"mappings":";;;;;;AAkBA,wBACC,MACA;CAEC,QAAQ,CAAC;CACT,cAAc;CACd,yBAAyB;AAC1B,GACA,SACD"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/index.ts"],"sourcesContent":["import { customShadowlessElement } from \"@brytdesigns/web-component-core/utils\";\nimport { KeenSlider } from \"./components/index.js\";\nimport { DEFAULT_SLIDE_LABEL } from \"./utils.js\";\n\nexport type {\n\tKeenSliderInstance,\n\tKeenSliderOptions,\n\tKeenSliderPlugin,\n} from \"./KeenSlider.js\";\n\nexport type KeenSliderProps = KeenSlider.Props;\n\nexport {\n\taddPlugin,\n\tgetKeenSliderContext,\n\tuseKeenSlider,\n\tuseKeenSliderAt,\n} from \"./hooks/index.js\";\n\ncustomShadowlessElement(\n\tKeenSlider.Name,\n\t{\n\t\t// @ts-expect-error\n\t\tconfig: {},\n\t\tcenterSlides: false,\n\t\trefreshOnChildrenChange: false,\n\t\tslideLabel: DEFAULT_SLIDE_LABEL,\n\t},\n\tKeenSlider.Component,\n);\n"],"mappings":";;;;;;;AAmBA,wBACC,MACA;CAEC,QAAQ,CAAC;CACT,cAAc;CACd,yBAAyB;CACzB,YAAY;AACb,GACA,SACD"}
@@ -2,7 +2,22 @@
2
2
  function constrain(value, min = -Infinity, max = Infinity) {
3
3
  return Math.min(Math.max(value, min), max);
4
4
  }
5
+ /** What every slide was labelled before `slide-label` existed: "1 of 4". */
6
+ var DEFAULT_SLIDE_LABEL = "{n} of {total}";
7
+ /**
8
+ * Fills a slide-label template. `{n}` is the 1-based slide number and
9
+ * `{total}` the slide count, the same placeholder `dot-label` and
10
+ * `breakpoint-label` use for their own numbers.
11
+ *
12
+ * Every occurrence is replaced, not just the first, so a template may repeat
13
+ * one. Anything that is not a non-empty string falls back to the default: an
14
+ * empty `slide-label=""` would otherwise give every slide an empty accessible
15
+ * name, which is worse than the English one.
16
+ */
17
+ function formatSlideLabel(template, n, total) {
18
+ return (typeof template === "string" && template.trim() !== "" ? template : DEFAULT_SLIDE_LABEL).replaceAll("{n}", `${n}`).replaceAll("{total}", `${total}`);
19
+ }
5
20
  //#endregion
6
- export { constrain };
21
+ export { DEFAULT_SLIDE_LABEL, constrain, formatSlideLabel };
7
22
 
8
23
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../../src/utils.ts"],"sourcesContent":["import type { ICustomElement } from \"component-register\";\n\nexport function constrain(value: number, min = -Infinity, max = Infinity) {\n\treturn Math.min(Math.max(value, min), max);\n}\n\ntype Options = {\n\telement: HTMLElement & ICustomElement;\n};\n\nexport function initializeSliderClasses<Props>(\n\tComponentType = (props: Props, options: Options) => {},\n) {\n\treturn (props: Props, options: Options) => {\n\t\tconst { element } = options;\n\t\telement.classList.add(\"keen-slider\");\n\t\tArray.from(element.children).forEach((child) => {\n\t\t\tchild.classList.add(\"keen-slider__slide\");\n\t\t});\n\t\treturn ComponentType(props, options);\n\t};\n}\n"],"mappings":";AAEA,SAAgB,UAAU,OAAe,MAAM,WAAW,MAAM,UAAU;CACzE,OAAO,KAAK,IAAI,KAAK,IAAI,OAAO,GAAG,GAAG,GAAG;AAC1C"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../src/utils.ts"],"sourcesContent":["import type { ICustomElement } from \"component-register\";\n\nexport function constrain(value: number, min = -Infinity, max = Infinity) {\n\treturn Math.min(Math.max(value, min), max);\n}\n\n/** What every slide was labelled before `slide-label` existed: \"1 of 4\". */\nexport const DEFAULT_SLIDE_LABEL = \"{n} of {total}\";\n\n/**\n * Fills a slide-label template. `{n}` is the 1-based slide number and\n * `{total}` the slide count, the same placeholder `dot-label` and\n * `breakpoint-label` use for their own numbers.\n *\n * Every occurrence is replaced, not just the first, so a template may repeat\n * one. Anything that is not a non-empty string falls back to the default: an\n * empty `slide-label=\"\"` would otherwise give every slide an empty accessible\n * name, which is worse than the English one.\n */\nexport function formatSlideLabel(\n\ttemplate: string | undefined,\n\tn: number,\n\ttotal: number,\n) {\n\tconst usable =\n\t\ttypeof template === \"string\" && template.trim() !== \"\"\n\t\t\t? template\n\t\t\t: DEFAULT_SLIDE_LABEL;\n\treturn usable.replaceAll(\"{n}\", `${n}`).replaceAll(\"{total}\", `${total}`);\n}\n\ntype Options = {\n\telement: HTMLElement & ICustomElement;\n};\n\nexport function initializeSliderClasses<Props>(\n\tComponentType = (props: Props, options: Options) => {},\n) {\n\treturn (props: Props, options: Options) => {\n\t\tconst { element } = options;\n\t\telement.classList.add(\"keen-slider\");\n\t\tArray.from(element.children).forEach((child) => {\n\t\t\tchild.classList.add(\"keen-slider__slide\");\n\t\t});\n\t\treturn ComponentType(props, options);\n\t};\n}\n"],"mappings":";AAEA,SAAgB,UAAU,OAAe,MAAM,WAAW,MAAM,UAAU;CACzE,OAAO,KAAK,IAAI,KAAK,IAAI,OAAO,GAAG,GAAG,GAAG;AAC1C;;AAGA,IAAa,sBAAsB;;;;;;;;;;;AAYnC,SAAgB,iBACf,UACA,GACA,OACC;CAKD,QAHC,OAAO,aAAa,YAAY,SAAS,KAAK,MAAM,KACjD,WACA,oBAAA,CACU,WAAW,OAAO,GAAG,GAAG,CAAC,CAAC,WAAW,WAAW,GAAG,OAAO;AACzE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brytdesigns/web-component-keen-slider",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Keen slider web component",
5
5
  "main": "./dist/main/index.js",
6
6
  "module": "./dist/main/index.js",
@@ -20,7 +20,7 @@
20
20
  "author": "Bryt Designs",
21
21
  "license": "ISC",
22
22
  "dependencies": {
23
- "@brytdesigns/web-component-core": "3.0.0",
23
+ "@brytdesigns/web-component-core": "3.1.0",
24
24
  "component-register": "0.8.7",
25
25
  "keen-slider": "^6.8.6",
26
26
  "solid-js": "2.0.0-rc.14"