rei-kit 0.18.2 → 0.20.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/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import { a as addDays, c as lastNDays, d as toDateKey, f as todayKey, i as needsIosInstall, l as leadingBlanks, n as isApplePortable, o as eachDayOfYear, r as isInstalled, s as fromDateKey, t as SettingsGroup_default, u as startOfWeek } from "./SettingsGroup-DtEB_Hrd.js";
2
- import { _ as toRedirectPath, a as FormField_default, c as useVisualViewport, d as isThemePreference, f as readStoredTheme, g as safeRedirect, h as tapFeedback, i as BaseInput_default, l as useOnline, m as useTheme, n as BaseCheckbox_default, o as BaseAlert_default, p as setThemeStorageKey, r as BaseSheet_default, s as useToast, t as GoogleButton_default, u as applyTheme } from "./GoogleButton-DV_RIpJE.js";
1
+ import { a as addDays, c as lastNDays, d as toDateKey, f as todayKey, i as needsIosInstall, l as leadingBlanks, n as isApplePortable, o as eachDayOfYear, r as isInstalled, s as fromDateKey, t as SettingsGroup_default, u as startOfWeek } from "./SettingsGroup-JsD19V81.js";
2
+ import { _ as toRedirectPath, a as FormField_default, c as useVisualViewport, d as isThemePreference, f as readStoredTheme, g as safeRedirect, h as tapFeedback, i as BaseInput_default, l as useOnline, m as useTheme, n as BaseCheckbox_default, o as BaseAlert_default, p as setThemeStorageKey, r as BaseSheet_default, s as useToast, t as GoogleButton_default, u as applyTheme } from "./GoogleButton-BKOezkZr.js";
3
3
  import { n as registerErrorMapper, r as toAppError, t as AppError } from "./app-error-DF9cijE0.js";
4
- import { n as BaseButton_default, t as SettingsRow_default } from "./SettingsRow-3lPRJbBZ.js";
5
4
  import { t as _plugin_vue_export_helper_default } from "./_plugin-vue_export-helper-BOaGB7Aw.js";
6
- import { Fragment, Teleport, TransitionGroup, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, mergeModels, mergeProps, normalizeClass, normalizeStyle, onBeforeUnmount, onErrorCaptured, onMounted, onScopeDispose, openBlock, readonly, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, useId, useModel, vModelRadio, vModelSelect, vModelText, watch, watchEffect, withCtx, withDirectives } from "vue";
5
+ import { n as BaseButton_default, t as SettingsRow_default } from "./SettingsRow-3lPRJbBZ.js";
6
+ import { Fragment, Teleport, TransitionGroup, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, mergeModels, mergeProps, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, onErrorCaptured, onMounted, onScopeDispose, openBlock, readonly, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, useId, useModel, vModelRadio, vModelSelect, vModelText, vShow, watch, watchEffect, withCtx, withDirectives, withModifiers } from "vue";
7
7
  import { ArrowDown, ArrowRight, ArrowUp, CheckCircle2, ChevronDown, Info, TriangleAlert, X, XCircle } from "lucide-vue-next";
8
8
  import { RouterLink } from "vue-router";
9
9
  import { createI18n } from "vue-i18n";
@@ -331,6 +331,77 @@ function useMediaQuery(query) {
331
331
  return matches;
332
332
  }
333
333
  //#endregion
334
+ //#region src/components/BaseAvatar.vue?vue&type=script&setup=true&lang.ts
335
+ var _hoisted_1$21 = [
336
+ "role",
337
+ "aria-label",
338
+ "aria-hidden"
339
+ ];
340
+ var _hoisted_2$18 = ["src"];
341
+ var _hoisted_3$11 = {
342
+ key: 1,
343
+ class: "rk-avatar-initials"
344
+ };
345
+ var _hoisted_4$10 = {
346
+ key: 2,
347
+ viewBox: "0 0 20 20",
348
+ class: "rk-avatar-figure",
349
+ fill: "none",
350
+ stroke: "currentColor"
351
+ };
352
+ //#endregion
353
+ //#region src/components/BaseAvatar.vue
354
+ var BaseAvatar_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
355
+ __name: "BaseAvatar",
356
+ props: {
357
+ name: { default: "" },
358
+ src: { default: "" },
359
+ size: { default: "md" },
360
+ fallback: { default: "figure" },
361
+ label: { default: "" }
362
+ },
363
+ setup(__props) {
364
+ const broken = ref(false);
365
+ watch(() => __props.src, () => broken.value = false);
366
+ const showImage = computed(() => Boolean(__props.src) && !broken.value);
367
+ /**
368
+ * Two letters at most, from the first and last word.
369
+ *
370
+ * `Array.from` rather than `slice`, so a name written in a script outside the
371
+ * basic plane is cut between characters rather than through one.
372
+ */
373
+ const initials = computed(() => {
374
+ const words = __props.name.trim().split(/\s+/).filter(Boolean);
375
+ if (words.length === 0) return "";
376
+ return ((Array.from(words[0])[0] ?? "") + (words.length > 1 ? Array.from(words[words.length - 1])[0] ?? "" : "")).toUpperCase();
377
+ });
378
+ const accessibleName = computed(() => __props.label || __props.name);
379
+ return (_ctx, _cache) => {
380
+ return openBlock(), createElementBlock("span", {
381
+ class: normalizeClass(["rk-avatar", `is-${__props.size}`]),
382
+ role: accessibleName.value ? "img" : void 0,
383
+ "aria-label": accessibleName.value || void 0,
384
+ "aria-hidden": accessibleName.value ? void 0 : "true"
385
+ }, [showImage.value ? (openBlock(), createElementBlock("img", {
386
+ key: 0,
387
+ src: __props.src,
388
+ alt: "",
389
+ class: "rk-avatar-img",
390
+ onError: _cache[0] || (_cache[0] = ($event) => broken.value = true)
391
+ }, null, 40, _hoisted_2$18)) : __props.fallback === "initials" && initials.value ? (openBlock(), createElementBlock("span", _hoisted_3$11, toDisplayString(initials.value), 1)) : (openBlock(), createElementBlock("svg", _hoisted_4$10, [..._cache[1] || (_cache[1] = [createElementVNode("circle", {
392
+ cx: "10",
393
+ cy: "6.75",
394
+ r: "3.25",
395
+ "stroke-width": "1.5"
396
+ }, null, -1), createElementVNode("path", {
397
+ d: "M3.75 17c0-3.05 2.8-5.25 6.25-5.25S16.25 13.95 16.25 17",
398
+ "stroke-width": "1.5",
399
+ "stroke-linecap": "round"
400
+ }, null, -1)])]))], 10, _hoisted_1$21);
401
+ };
402
+ }
403
+ }), [["__scopeId", "data-v-47586bec"]]);
404
+ //#endregion
334
405
  //#region src/components/BaseBadge.vue
335
406
  var BaseBadge_default = /* @__PURE__ */ defineComponent({
336
407
  __name: "BaseBadge",
@@ -350,6 +421,123 @@ var BaseBadge_default = /* @__PURE__ */ defineComponent({
350
421
  }
351
422
  });
352
423
  //#endregion
424
+ //#region src/components/BaseMenu.vue?vue&type=script&setup=true&lang.ts
425
+ var _hoisted_1$20 = [
426
+ "aria-label",
427
+ "aria-expanded",
428
+ "aria-controls"
429
+ ];
430
+ var _hoisted_2$17 = ["id"];
431
+ //#endregion
432
+ //#region src/components/BaseMenu.vue
433
+ var BaseMenu_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
434
+ __name: "BaseMenu",
435
+ props: /*@__PURE__*/ mergeModels({
436
+ label: {},
437
+ align: { default: "end" }
438
+ }, {
439
+ "modelValue": {
440
+ type: Boolean,
441
+ default: false
442
+ },
443
+ "modelModifiers": {}
444
+ }),
445
+ emits: ["update:modelValue"],
446
+ setup(__props) {
447
+ const open = useModel(__props, "modelValue");
448
+ const root = ref(null);
449
+ const panel = ref(null);
450
+ const trigger = ref(null);
451
+ const id = useId();
452
+ function items() {
453
+ if (!panel.value) return [];
454
+ return Array.from(panel.value.querySelectorAll("[role=\"menuitem\"]"));
455
+ }
456
+ function focusAt(index) {
457
+ const list = items();
458
+ if (list.length === 0) return;
459
+ list[(index + list.length) % list.length]?.focus();
460
+ }
461
+ function currentIndex() {
462
+ return items().indexOf(document.activeElement);
463
+ }
464
+ function onKeydown(event) {
465
+ if (!open.value) {
466
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
467
+ event.preventDefault();
468
+ open.value = true;
469
+ }
470
+ return;
471
+ }
472
+ switch (event.key) {
473
+ case "Escape":
474
+ event.preventDefault();
475
+ open.value = false;
476
+ break;
477
+ case "ArrowDown":
478
+ event.preventDefault();
479
+ focusAt(currentIndex() + 1);
480
+ break;
481
+ case "ArrowUp":
482
+ event.preventDefault();
483
+ focusAt(currentIndex() - 1);
484
+ break;
485
+ case "Home":
486
+ event.preventDefault();
487
+ focusAt(0);
488
+ break;
489
+ case "End":
490
+ event.preventDefault();
491
+ focusAt(items().length - 1);
492
+ break;
493
+ case "Tab": open.value = false;
494
+ }
495
+ }
496
+ function onDocumentPointer(event) {
497
+ if (root.value && !root.value.contains(event.target)) open.value = false;
498
+ }
499
+ watch(open, async (isOpen) => {
500
+ if (typeof document === "undefined") return;
501
+ if (isOpen) {
502
+ document.addEventListener("pointerdown", onDocumentPointer);
503
+ await nextTick();
504
+ items()[0]?.focus();
505
+ } else {
506
+ document.removeEventListener("pointerdown", onDocumentPointer);
507
+ if (root.value?.contains(document.activeElement)) trigger.value?.focus();
508
+ }
509
+ }, { immediate: true });
510
+ onBeforeUnmount(() => {
511
+ if (typeof document !== "undefined") document.removeEventListener("pointerdown", onDocumentPointer);
512
+ });
513
+ return (_ctx, _cache) => {
514
+ return openBlock(), createElementBlock("div", {
515
+ ref_key: "root",
516
+ ref: root,
517
+ class: "rk-menu",
518
+ onKeydown
519
+ }, [createElementVNode("button", {
520
+ ref_key: "trigger",
521
+ ref: trigger,
522
+ type: "button",
523
+ class: "rk-menu-trigger",
524
+ "aria-label": __props.label,
525
+ "aria-expanded": open.value,
526
+ "aria-haspopup": true,
527
+ "aria-controls": open.value ? unref(id) : void 0,
528
+ onClick: _cache[0] || (_cache[0] = ($event) => open.value = !open.value)
529
+ }, [renderSlot(_ctx.$slots, "trigger", { open: open.value }, void 0, true)], 8, _hoisted_1$20), open.value ? (openBlock(), createElementBlock("div", {
530
+ key: 0,
531
+ id: unref(id),
532
+ ref_key: "panel",
533
+ ref: panel,
534
+ class: normalizeClass(["rk-menu-panel", `is-${__props.align}`]),
535
+ role: "menu"
536
+ }, [renderSlot(_ctx.$slots, "default", {}, void 0, true)], 10, _hoisted_2$17)) : createCommentVNode("", true)], 544);
537
+ };
538
+ }
539
+ }), [["__scopeId", "data-v-8ad05b3d"]]);
540
+ //#endregion
353
541
  //#region src/components/BaseCard.vue
354
542
  var BaseCard_default = /* @__PURE__ */ defineComponent({
355
543
  __name: "BaseCard",
@@ -415,14 +603,215 @@ var BaseCard_default = /* @__PURE__ */ defineComponent({
415
603
  }
416
604
  });
417
605
  //#endregion
606
+ //#region src/components/BaseCombobox.vue?vue&type=script&setup=true&lang.ts
607
+ var _hoisted_1$19 = ["for"];
608
+ var _hoisted_2$16 = { class: "rk-combo-field" };
609
+ var _hoisted_3$10 = [
610
+ "id",
611
+ "placeholder",
612
+ "disabled",
613
+ "aria-expanded",
614
+ "aria-activedescendant",
615
+ "aria-invalid",
616
+ "aria-describedby"
617
+ ];
618
+ var _hoisted_4$9 = ["disabled"];
619
+ var _hoisted_5$5 = ["aria-label"];
620
+ var _hoisted_6$1 = [
621
+ "id",
622
+ "aria-selected",
623
+ "onPointerdown",
624
+ "onPointermove"
625
+ ];
626
+ var _hoisted_7$1 = {
627
+ key: 0,
628
+ class: "rk-combo-empty"
629
+ };
630
+ //#endregion
631
+ //#region src/components/BaseCombobox.vue
632
+ var BaseCombobox_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
633
+ __name: "BaseCombobox",
634
+ props: /*@__PURE__*/ mergeModels({
635
+ label: {},
636
+ options: {},
637
+ placeholder: { default: "" },
638
+ hint: { default: "" },
639
+ error: { default: "" },
640
+ disabled: {
641
+ type: Boolean,
642
+ default: false
643
+ },
644
+ emptyLabel: {}
645
+ }, {
646
+ "modelValue": { default: "" },
647
+ "modelModifiers": {}
648
+ }),
649
+ emits: ["update:modelValue"],
650
+ setup(__props) {
651
+ const model = useModel(__props, "modelValue");
652
+ const id = useId();
653
+ const listId = `${id}-list`;
654
+ const hintId = `${id}-hint`;
655
+ const errorId = `${id}-error`;
656
+ const optionId = (index) => `${id}-opt-${index}`;
657
+ const open = ref(false);
658
+ const query = ref("");
659
+ const highlighted = ref(0);
660
+ const root = ref(null);
661
+ const list = ref(null);
662
+ const selected = computed(() => __props.options.find((option) => option.value === model.value));
663
+ /** What the input shows: the query while typing, the chosen label otherwise. */
664
+ const text = computed({
665
+ get: () => open.value ? query.value : selected.value?.label ?? "",
666
+ set: (value) => {
667
+ query.value = value;
668
+ open.value = true;
669
+ highlighted.value = 0;
670
+ }
671
+ });
672
+ const matches = computed(() => {
673
+ const needle = query.value.trim().toLowerCase();
674
+ if (!open.value || needle === "") return __props.options;
675
+ return __props.options.filter((option) => option.label.toLowerCase().includes(needle));
676
+ });
677
+ const describedBy = computed(() => {
678
+ if (__props.error) return errorId;
679
+ if (__props.hint) return hintId;
680
+ });
681
+ function choose(option) {
682
+ if (!option) return;
683
+ model.value = option.value;
684
+ query.value = "";
685
+ open.value = false;
686
+ }
687
+ function move(delta) {
688
+ const count = matches.value.length;
689
+ if (count === 0) return;
690
+ open.value = true;
691
+ highlighted.value = (highlighted.value + delta + count) % count;
692
+ scrollHighlightIntoView();
693
+ }
694
+ async function scrollHighlightIntoView() {
695
+ await nextTick();
696
+ (list.value?.children[highlighted.value])?.scrollIntoView?.({ block: "nearest" });
697
+ }
698
+ function onKeydown(event) {
699
+ switch (event.key) {
700
+ case "ArrowDown":
701
+ event.preventDefault();
702
+ move(1);
703
+ break;
704
+ case "ArrowUp":
705
+ event.preventDefault();
706
+ move(-1);
707
+ break;
708
+ case "Enter":
709
+ if (!open.value) return;
710
+ event.preventDefault();
711
+ choose(matches.value[highlighted.value]);
712
+ break;
713
+ case "Escape":
714
+ event.preventDefault();
715
+ if (open.value) {
716
+ open.value = false;
717
+ query.value = "";
718
+ } else model.value = "";
719
+ break;
720
+ case "Tab": open.value = false;
721
+ }
722
+ }
723
+ function onDocumentPointer(event) {
724
+ if (root.value && !root.value.contains(event.target)) {
725
+ open.value = false;
726
+ query.value = "";
727
+ }
728
+ }
729
+ watch(open, (isOpen) => {
730
+ if (typeof document === "undefined") return;
731
+ if (isOpen) document.addEventListener("pointerdown", onDocumentPointer);
732
+ else document.removeEventListener("pointerdown", onDocumentPointer);
733
+ }, { immediate: true });
734
+ onBeforeUnmount(() => {
735
+ if (typeof document !== "undefined") document.removeEventListener("pointerdown", onDocumentPointer);
736
+ });
737
+ return (_ctx, _cache) => {
738
+ return openBlock(), createElementBlock("div", {
739
+ ref_key: "root",
740
+ ref: root,
741
+ class: "rk-combo"
742
+ }, [
743
+ createElementVNode("label", {
744
+ for: unref(id),
745
+ class: "rk-combo-label"
746
+ }, toDisplayString(__props.label), 9, _hoisted_1$19),
747
+ createElementVNode("div", _hoisted_2$16, [withDirectives(createElementVNode("input", {
748
+ id: unref(id),
749
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => text.value = $event),
750
+ type: "text",
751
+ role: "combobox",
752
+ class: "rk-combo-input",
753
+ autocomplete: "off",
754
+ placeholder: __props.placeholder,
755
+ disabled: __props.disabled,
756
+ "aria-expanded": open.value,
757
+ "aria-controls": listId,
758
+ "aria-autocomplete": "list",
759
+ "aria-activedescendant": open.value ? optionId(highlighted.value) : void 0,
760
+ "aria-invalid": Boolean(__props.error),
761
+ "aria-describedby": describedBy.value,
762
+ onKeydown,
763
+ onFocus: _cache[1] || (_cache[1] = ($event) => open.value = true)
764
+ }, null, 40, _hoisted_3$10), [[vModelText, text.value]]), createElementVNode("button", {
765
+ type: "button",
766
+ class: "rk-combo-toggle",
767
+ tabindex: "-1",
768
+ "aria-hidden": "true",
769
+ disabled: __props.disabled,
770
+ onClick: _cache[2] || (_cache[2] = ($event) => open.value = !open.value)
771
+ }, " ▾ ", 8, _hoisted_4$9)]),
772
+ withDirectives(createElementVNode("ul", {
773
+ id: listId,
774
+ ref_key: "list",
775
+ ref: list,
776
+ role: "listbox",
777
+ class: "rk-combo-list",
778
+ "aria-label": __props.label
779
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(matches.value, (option, index) => {
780
+ return openBlock(), createElementBlock("li", {
781
+ id: optionId(index),
782
+ key: option.value,
783
+ role: "option",
784
+ class: normalizeClass(["rk-combo-option", {
785
+ "is-highlighted": index === highlighted.value,
786
+ "is-selected": option.value === model.value
787
+ }]),
788
+ "aria-selected": option.value === model.value,
789
+ onPointerdown: withModifiers(($event) => choose(option), ["prevent"]),
790
+ onPointermove: ($event) => highlighted.value = index
791
+ }, toDisplayString(option.label), 43, _hoisted_6$1);
792
+ }), 128)), matches.value.length === 0 ? (openBlock(), createElementBlock("li", _hoisted_7$1, toDisplayString(__props.emptyLabel), 1)) : createCommentVNode("", true)], 8, _hoisted_5$5), [[vShow, open.value]]),
793
+ __props.error ? (openBlock(), createElementBlock("p", {
794
+ key: 0,
795
+ id: errorId,
796
+ class: "rk-combo-error"
797
+ }, toDisplayString(__props.error), 1)) : __props.hint ? (openBlock(), createElementBlock("p", {
798
+ key: 1,
799
+ id: hintId,
800
+ class: "rk-combo-hint"
801
+ }, toDisplayString(__props.hint), 1)) : createCommentVNode("", true)
802
+ ], 512);
803
+ };
804
+ }
805
+ }), [["__scopeId", "data-v-52b21155"]]);
806
+ //#endregion
418
807
  //#region src/components/BaseRadioGroup.vue?vue&type=script&setup=true&lang.ts
419
- var _hoisted_1$14 = ["aria-describedby"];
420
- var _hoisted_2$11 = [
808
+ var _hoisted_1$18 = ["aria-describedby"];
809
+ var _hoisted_2$15 = [
421
810
  "name",
422
811
  "value",
423
812
  "disabled"
424
813
  ];
425
- var _hoisted_3$6 = { class: "text-ink text-sm" };
814
+ var _hoisted_3$9 = { class: "text-ink text-sm" };
426
815
  //#endregion
427
816
  //#region src/components/BaseRadioGroup.vue
428
817
  var BaseRadioGroup_default = /* @__PURE__ */ defineComponent({
@@ -467,7 +856,7 @@ var BaseRadioGroup_default = /* @__PURE__ */ defineComponent({
467
856
  value: option.value,
468
857
  disabled: option.disabled,
469
858
  class: "accent-primary focus-visible:outline-primary size-4 shrink-0 focus-visible:outline-2 focus-visible:outline-offset-2"
470
- }, null, 8, _hoisted_2$11), [[vModelRadio, model.value]]), createElementVNode("span", _hoisted_3$6, toDisplayString(option.label), 1)], 2);
859
+ }, null, 8, _hoisted_2$15), [[vModelRadio, model.value]]), createElementVNode("span", _hoisted_3$9, toDisplayString(option.label), 1)], 2);
471
860
  }), 128)),
472
861
  __props.error ? (openBlock(), createElementBlock("p", {
473
862
  key: 0,
@@ -478,24 +867,24 @@ var BaseRadioGroup_default = /* @__PURE__ */ defineComponent({
478
867
  id: hintId,
479
868
  class: "text-ink-soft text-xs"
480
869
  }, toDisplayString(__props.hint), 1)) : createCommentVNode("", true)
481
- ], 8, _hoisted_1$14);
870
+ ], 8, _hoisted_1$18);
482
871
  };
483
872
  }
484
873
  });
485
874
  //#endregion
486
875
  //#region src/components/BaseSelect.vue?vue&type=script&setup=true&lang.ts
487
- var _hoisted_1$13 = { class: "relative" };
488
- var _hoisted_2$10 = [
876
+ var _hoisted_1$17 = { class: "relative" };
877
+ var _hoisted_2$14 = [
489
878
  "id",
490
879
  "aria-invalid",
491
880
  "aria-describedby"
492
881
  ];
493
- var _hoisted_3$5 = {
882
+ var _hoisted_3$8 = {
494
883
  key: 0,
495
884
  value: void 0,
496
885
  disabled: ""
497
886
  };
498
- var _hoisted_4$5 = ["value", "disabled"];
887
+ var _hoisted_4$8 = ["value", "disabled"];
499
888
  //#endregion
500
889
  //#region src/components/BaseSelect.vue
501
890
  var BaseSelect_default = /* @__PURE__ */ defineComponent({
@@ -531,7 +920,7 @@ var BaseSelect_default = /* @__PURE__ */ defineComponent({
531
920
  "label-hidden": __props.labelHidden,
532
921
  size: __props.size
533
922
  }, {
534
- default: withCtx(({ id, describedBy, invalid }) => [createElementVNode("div", _hoisted_1$13, [withDirectives(createElementVNode("select", {
923
+ default: withCtx(({ id, describedBy, invalid }) => [createElementVNode("div", _hoisted_1$17, [withDirectives(createElementVNode("select", {
535
924
  id,
536
925
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => model.value = $event),
537
926
  "aria-invalid": invalid,
@@ -541,13 +930,13 @@ var BaseSelect_default = /* @__PURE__ */ defineComponent({
541
930
  __props.variant === "unstyled" ? "" : SIZE_CLASS[__props.size],
542
931
  __props.variant !== "unstyled" && invalid ? "border-negative" : ""
543
932
  ]])
544
- }, [__props.placeholder ? (openBlock(), createElementBlock("option", _hoisted_3$5, toDisplayString(__props.placeholder), 1)) : createCommentVNode("", true), (openBlock(true), createElementBlock(Fragment, null, renderList(__props.options, (option) => {
933
+ }, [__props.placeholder ? (openBlock(), createElementBlock("option", _hoisted_3$8, toDisplayString(__props.placeholder), 1)) : createCommentVNode("", true), (openBlock(true), createElementBlock(Fragment, null, renderList(__props.options, (option) => {
545
934
  return openBlock(), createElementBlock("option", {
546
935
  key: option.value,
547
936
  value: option.value,
548
937
  disabled: option.disabled
549
- }, toDisplayString(option.label), 9, _hoisted_4$5);
550
- }), 128))], 10, _hoisted_2$10), [[vModelSelect, model.value]]), createVNode(unref(ChevronDown), {
938
+ }, toDisplayString(option.label), 9, _hoisted_4$8);
939
+ }), 128))], 10, _hoisted_2$14), [[vModelSelect, model.value]]), createVNode(unref(ChevronDown), {
551
940
  class: "text-ink-soft pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2",
552
941
  "aria-hidden": "true"
553
942
  })])]),
@@ -563,6 +952,264 @@ var BaseSelect_default = /* @__PURE__ */ defineComponent({
563
952
  }
564
953
  });
565
954
  //#endregion
955
+ //#region src/components/BaseSlider.vue?vue&type=script&setup=true&lang.ts
956
+ var _hoisted_1$16 = { class: "rk-slider" };
957
+ var _hoisted_2$13 = { class: "rk-slider-head" };
958
+ var _hoisted_3$7 = ["for"];
959
+ var _hoisted_4$7 = ["for"];
960
+ var _hoisted_5$4 = [
961
+ "id",
962
+ "min",
963
+ "max",
964
+ "step",
965
+ "disabled",
966
+ "aria-valuetext",
967
+ "aria-describedby"
968
+ ];
969
+ //#endregion
970
+ //#region src/components/BaseSlider.vue
971
+ var BaseSlider_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
972
+ __name: "BaseSlider",
973
+ props: /*@__PURE__*/ mergeModels({
974
+ label: {},
975
+ min: { default: 0 },
976
+ max: { default: 100 },
977
+ step: { default: 1 },
978
+ hint: { default: "" },
979
+ disabled: {
980
+ type: Boolean,
981
+ default: false
982
+ },
983
+ format: { type: Function },
984
+ showValue: {
985
+ type: Boolean,
986
+ default: true
987
+ }
988
+ }, {
989
+ "modelValue": { default: 0 },
990
+ "modelModifiers": {}
991
+ }),
992
+ emits: ["update:modelValue"],
993
+ setup(__props) {
994
+ const model = useModel(__props, "modelValue");
995
+ const id = useId();
996
+ const hintId = `${id}-hint`;
997
+ const text = computed(() => __props.format ? __props.format(model.value) : String(model.value));
998
+ /** Where the fill stops, as a percentage of the track. */
999
+ const filled = computed(() => {
1000
+ const span = __props.max - __props.min;
1001
+ return span <= 0 ? 0 : (model.value - __props.min) / span * 100;
1002
+ });
1003
+ return (_ctx, _cache) => {
1004
+ return openBlock(), createElementBlock("div", _hoisted_1$16, [
1005
+ createElementVNode("div", _hoisted_2$13, [createElementVNode("label", {
1006
+ for: unref(id),
1007
+ class: "rk-slider-label"
1008
+ }, toDisplayString(__props.label), 9, _hoisted_3$7), __props.showValue ? (openBlock(), createElementBlock("output", {
1009
+ key: 0,
1010
+ for: unref(id),
1011
+ class: "rk-slider-value"
1012
+ }, toDisplayString(text.value), 9, _hoisted_4$7)) : createCommentVNode("", true)]),
1013
+ withDirectives(createElementVNode("input", {
1014
+ id: unref(id),
1015
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => model.value = $event),
1016
+ type: "range",
1017
+ class: "rk-slider-input",
1018
+ min: __props.min,
1019
+ max: __props.max,
1020
+ step: __props.step,
1021
+ disabled: __props.disabled,
1022
+ "aria-valuetext": text.value,
1023
+ "aria-describedby": __props.hint ? hintId : void 0,
1024
+ style: normalizeStyle({ "--rk-slider-filled": `${filled.value}%` })
1025
+ }, null, 12, _hoisted_5$4), [[
1026
+ vModelText,
1027
+ model.value,
1028
+ void 0,
1029
+ { number: true }
1030
+ ]]),
1031
+ __props.hint ? (openBlock(), createElementBlock("p", {
1032
+ key: 0,
1033
+ id: hintId,
1034
+ class: "rk-slider-hint"
1035
+ }, toDisplayString(__props.hint), 1)) : createCommentVNode("", true)
1036
+ ]);
1037
+ };
1038
+ }
1039
+ }), [["__scopeId", "data-v-0a408519"]]);
1040
+ //#endregion
1041
+ //#region src/components/BaseSpinner.vue?vue&type=script&setup=true&lang.ts
1042
+ var _hoisted_1$15 = {
1043
+ class: "rk-spin-wrap",
1044
+ role: "status"
1045
+ };
1046
+ var _hoisted_2$12 = { class: "rk-spin-label" };
1047
+ //#endregion
1048
+ //#region src/components/BaseSpinner.vue
1049
+ var BaseSpinner_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
1050
+ __name: "BaseSpinner",
1051
+ props: {
1052
+ label: {},
1053
+ size: { default: "md" }
1054
+ },
1055
+ setup(__props) {
1056
+ /**
1057
+ * Work in progress, with no idea how much is left.
1058
+ *
1059
+ * `ProgressBar` is the one to reach for when the amount is known; this is for
1060
+ * when it is not. Showing a bar that cannot move is worse than a spinner,
1061
+ * because a bar is a promise about how long.
1062
+ *
1063
+ * `role="status"` and a label, so a screen reader says what is happening. A
1064
+ * spinner with no accessible name is a decoration that happens to be the only
1065
+ * thing on screen — which is why `label` is required and not defaulted to
1066
+ * "Loading" in a language the app may not speak.
1067
+ */
1068
+ const SIZES = {
1069
+ sm: "1rem",
1070
+ md: "1.5rem",
1071
+ lg: "2.5rem"
1072
+ };
1073
+ return (_ctx, _cache) => {
1074
+ return openBlock(), createElementBlock("span", _hoisted_1$15, [createElementVNode("span", {
1075
+ class: "rk-spin",
1076
+ style: normalizeStyle({
1077
+ width: SIZES[__props.size],
1078
+ height: SIZES[__props.size]
1079
+ }),
1080
+ "aria-hidden": "true"
1081
+ }, null, 4), createElementVNode("span", _hoisted_2$12, toDisplayString(__props.label), 1)]);
1082
+ };
1083
+ }
1084
+ }), [["__scopeId", "data-v-842da62c"]]);
1085
+ //#endregion
1086
+ //#region src/components/BaseSwitch.vue?vue&type=script&setup=true&lang.ts
1087
+ var _hoisted_1$14 = { class: "flex items-center justify-between gap-4" };
1088
+ var _hoisted_2$11 = {
1089
+ key: 0,
1090
+ class: "min-w-0"
1091
+ };
1092
+ var _hoisted_3$6 = ["id"];
1093
+ var _hoisted_4$6 = [
1094
+ "aria-checked",
1095
+ "aria-labelledby",
1096
+ "aria-label",
1097
+ "aria-describedby",
1098
+ "disabled"
1099
+ ];
1100
+ //#endregion
1101
+ //#region src/components/BaseSwitch.vue
1102
+ var BaseSwitch_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
1103
+ __name: "BaseSwitch",
1104
+ props: /*@__PURE__*/ mergeModels({
1105
+ label: {},
1106
+ hint: { default: "" },
1107
+ disabled: {
1108
+ type: Boolean,
1109
+ default: false
1110
+ },
1111
+ labelHidden: {
1112
+ type: Boolean,
1113
+ default: false
1114
+ }
1115
+ }, {
1116
+ "modelValue": {
1117
+ type: Boolean,
1118
+ default: false
1119
+ },
1120
+ "modelModifiers": {}
1121
+ }),
1122
+ emits: ["update:modelValue"],
1123
+ setup(__props) {
1124
+ const model = useModel(__props, "modelValue");
1125
+ const id = useId();
1126
+ const hintId = `${id}-hint`;
1127
+ const describedBy = computed(() => __props.hint ? hintId : void 0);
1128
+ function toggle() {
1129
+ if (!__props.disabled) model.value = !model.value;
1130
+ }
1131
+ return (_ctx, _cache) => {
1132
+ return openBlock(), createElementBlock("div", _hoisted_1$14, [!__props.labelHidden ? (openBlock(), createElementBlock("span", _hoisted_2$11, [createElementVNode("span", {
1133
+ id: unref(id),
1134
+ class: "text-ink block text-sm"
1135
+ }, toDisplayString(__props.label), 9, _hoisted_3$6), __props.hint ? (openBlock(), createElementBlock("span", {
1136
+ key: 0,
1137
+ id: hintId,
1138
+ class: "text-ink-soft mt-0.5 block text-xs"
1139
+ }, toDisplayString(__props.hint), 1)) : createCommentVNode("", true)])) : createCommentVNode("", true), createElementVNode("button", {
1140
+ type: "button",
1141
+ role: "switch",
1142
+ class: normalizeClass(["rk-switch", { "is-on": model.value }]),
1143
+ "aria-checked": model.value,
1144
+ "aria-labelledby": __props.labelHidden ? void 0 : unref(id),
1145
+ "aria-label": __props.labelHidden ? __props.label : void 0,
1146
+ "aria-describedby": describedBy.value,
1147
+ disabled: __props.disabled,
1148
+ onClick: toggle
1149
+ }, [..._cache[0] || (_cache[0] = [createElementVNode("span", {
1150
+ class: "rk-switch-knob",
1151
+ "aria-hidden": "true"
1152
+ }, null, -1)])], 10, _hoisted_4$6)]);
1153
+ };
1154
+ }
1155
+ }), [["__scopeId", "data-v-a9f65b91"]]);
1156
+ //#endregion
1157
+ //#region src/components/BaseTable.vue?vue&type=script&setup=true&lang.ts
1158
+ var _hoisted_1$13 = ["aria-label"];
1159
+ var _hoisted_2$10 = { class: "rk-table" };
1160
+ var _hoisted_3$5 = { key: 0 };
1161
+ var _hoisted_4$5 = { key: 1 };
1162
+ var _hoisted_5$3 = ["colspan"];
1163
+ //#endregion
1164
+ //#region src/components/BaseTable.vue
1165
+ var BaseTable_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
1166
+ __name: "BaseTable",
1167
+ props: {
1168
+ columns: {},
1169
+ rows: {},
1170
+ caption: {},
1171
+ captionHidden: {
1172
+ type: Boolean,
1173
+ default: false
1174
+ },
1175
+ rowKey: {}
1176
+ },
1177
+ setup(__props) {
1178
+ const keyFor = (row, index) => __props.rowKey ? String(row[__props.rowKey]) : index;
1179
+ return (_ctx, _cache) => {
1180
+ return openBlock(), createElementBlock("div", {
1181
+ class: "rk-table-scroll",
1182
+ tabindex: "0",
1183
+ role: "region",
1184
+ "aria-label": __props.caption
1185
+ }, [createElementVNode("table", _hoisted_2$10, [
1186
+ createElementVNode("caption", { class: normalizeClass(__props.captionHidden ? "rk-table-caption-hidden" : "rk-table-caption") }, toDisplayString(__props.caption), 3),
1187
+ createElementVNode("thead", null, [createElementVNode("tr", null, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.columns, (column) => {
1188
+ return openBlock(), createElementBlock("th", {
1189
+ key: column.key,
1190
+ scope: "col",
1191
+ class: normalizeClass([column.align === "end" && "is-end", column.nowrap && "is-nowrap"])
1192
+ }, toDisplayString(column.label), 3);
1193
+ }), 128))])]),
1194
+ __props.rows.length > 0 ? (openBlock(), createElementBlock("tbody", _hoisted_3$5, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.rows, (row, index) => {
1195
+ return openBlock(), createElementBlock("tr", { key: keyFor(row, index) }, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.columns, (column) => {
1196
+ return openBlock(), createElementBlock("td", {
1197
+ key: column.key,
1198
+ class: normalizeClass([column.align === "end" && "is-end", column.nowrap && "is-nowrap"])
1199
+ }, [renderSlot(_ctx.$slots, column.key, {
1200
+ row,
1201
+ value: row[column.key]
1202
+ }, () => [createTextVNode(toDisplayString(row[column.key]), 1)], true)], 2);
1203
+ }), 128))]);
1204
+ }), 128))])) : (openBlock(), createElementBlock("tbody", _hoisted_4$5, [createElementVNode("tr", null, [createElementVNode("td", {
1205
+ colspan: __props.columns.length,
1206
+ class: "rk-table-empty"
1207
+ }, [renderSlot(_ctx.$slots, "empty", {}, void 0, true)], 8, _hoisted_5$3)])]))
1208
+ ])], 8, _hoisted_1$13);
1209
+ };
1210
+ }
1211
+ }), [["__scopeId", "data-v-89958b5a"]]);
1212
+ //#endregion
566
1213
  //#region src/components/BaseTextarea.vue?vue&type=script&setup=true&lang.ts
567
1214
  var _hoisted_1$12 = [
568
1215
  "id",
@@ -648,6 +1295,14 @@ var EmptyState_default = /* @__PURE__ */ defineComponent({
648
1295
  description: { default: "" }
649
1296
  },
650
1297
  setup(__props) {
1298
+ /**
1299
+ * A list with nothing in it yet, said kindly.
1300
+ *
1301
+ * A blank area reads as a page that failed to load, and the reader's first
1302
+ * thought is that something is broken rather than that they have not started.
1303
+ * So there is always a sentence, and the slot below it is where the way out
1304
+ * goes — the button that creates the first one.
1305
+ */
651
1306
  return (_ctx, _cache) => {
652
1307
  return openBlock(), createElementBlock("div", _hoisted_1$11, [
653
1308
  _ctx.$slots.icon ? (openBlock(), createElementBlock("div", _hoisted_2$9, [renderSlot(_ctx.$slots, "icon")])) : createCommentVNode("", true),
@@ -731,6 +1386,15 @@ var PageHeader_default = /* @__PURE__ */ defineComponent({
731
1386
  __name: "PageHeader",
732
1387
  props: { title: {} },
733
1388
  setup(__props) {
1389
+ /**
1390
+ * The bar at the top of a screen: a title with room either side of it.
1391
+ *
1392
+ * Three fixed columns rather than a flex row, so the title is centred on the
1393
+ * screen rather than centred on what is left after the buttons. A back arrow on
1394
+ * one side and nothing on the other would otherwise push every title off centre
1395
+ * by exactly half a button, which is visible the moment two screens sit next to
1396
+ * each other.
1397
+ */
734
1398
  return (_ctx, _cache) => {
735
1399
  return openBlock(), createElementBlock("header", _hoisted_1$10, [
736
1400
  createElementVNode("div", _hoisted_2$8, [renderSlot(_ctx.$slots, "left")]),
@@ -1342,8 +2006,8 @@ function createI18nRuntime(options) {
1342
2006
  *
1343
2007
  * The fallback keeps `vitest` and `vite dev` honest, where no define runs.
1344
2008
  */
1345
- var VERSION = "0.18.2";
2009
+ var VERSION = "0.20.0";
1346
2010
  //#endregion
1347
- export { AppError, BaseAlert_default as BaseAlert, BaseBadge_default as BaseBadge, BaseButton_default as BaseButton, BaseCard_default as BaseCard, BaseCheckbox_default as BaseCheckbox, BaseInput_default as BaseInput, BaseRadioGroup_default as BaseRadioGroup, BaseSelect_default as BaseSelect, BaseSheet_default as BaseSheet, BaseTextarea_default as BaseTextarea, EmptyState_default as EmptyState, ErrorBoundary_default as ErrorBoundary, FormField_default as FormField, GoogleButton_default as GoogleButton, LocaleLinks_default as LocaleLinks, PageContainer_default as PageContainer, PageHeader_default as PageHeader, PriceCard_default as PriceCard, ProgressBar_default as ProgressBar, SectionHeading_default as SectionHeading, SegmentedControl_default as SegmentedControl, SettingsGroup_default as SettingsGroup, SettingsRow_default as SettingsRow, SkeletonList_default as SkeletonList, StatCard_default as StatCard, TabBar_default as TabBar, ToastHost_default as ToastHost, ToneDot_default as ToneDot, VERSION, addDays, applyTheme, createI18nRuntime, downloadJson, eachDayOfYear, formatDate, fromDateKey, isApplePortable, isInstalled, isThemePreference, lastNDays, leadingBlanks, needsIosInstall, readStoredTheme, registerErrorMapper, relativeDayLabel, safeRedirect, setFormatLocale, setThemeStorageKey, startOfWeek, tapFeedback, toAppError, toDateKey, toRedirectPath, todayKey, useDebouncedCallback, useDragScroll, useMediaQuery, useOnline, useTheme, useToast, useToday, useVisualViewport };
2011
+ export { AppError, BaseAlert_default as BaseAlert, BaseAvatar_default as BaseAvatar, BaseBadge_default as BaseBadge, BaseButton_default as BaseButton, BaseCard_default as BaseCard, BaseCheckbox_default as BaseCheckbox, BaseCombobox_default as BaseCombobox, BaseInput_default as BaseInput, BaseMenu_default as BaseMenu, BaseRadioGroup_default as BaseRadioGroup, BaseSelect_default as BaseSelect, BaseSheet_default as BaseSheet, BaseSlider_default as BaseSlider, BaseSpinner_default as BaseSpinner, BaseSwitch_default as BaseSwitch, BaseTable_default as BaseTable, BaseTextarea_default as BaseTextarea, EmptyState_default as EmptyState, ErrorBoundary_default as ErrorBoundary, FormField_default as FormField, GoogleButton_default as GoogleButton, LocaleLinks_default as LocaleLinks, PageContainer_default as PageContainer, PageHeader_default as PageHeader, PriceCard_default as PriceCard, ProgressBar_default as ProgressBar, SectionHeading_default as SectionHeading, SegmentedControl_default as SegmentedControl, SettingsGroup_default as SettingsGroup, SettingsRow_default as SettingsRow, SkeletonList_default as SkeletonList, StatCard_default as StatCard, TabBar_default as TabBar, ToastHost_default as ToastHost, ToneDot_default as ToneDot, VERSION, addDays, applyTheme, createI18nRuntime, downloadJson, eachDayOfYear, formatDate, fromDateKey, isApplePortable, isInstalled, isThemePreference, lastNDays, leadingBlanks, needsIosInstall, readStoredTheme, registerErrorMapper, relativeDayLabel, safeRedirect, setFormatLocale, setThemeStorageKey, startOfWeek, tapFeedback, toAppError, toDateKey, toRedirectPath, todayKey, useDebouncedCallback, useDragScroll, useMediaQuery, useOnline, useTheme, useToast, useToday, useVisualViewport };
1348
2012
 
1349
2013
  //# sourceMappingURL=index.js.map