@1337exp/taoui 1.3.2 → 1.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Tao UI
2
2
 
3
- **English** · [Русский](README.RU.md)
3
+ **English** · [Русский](README.ru.md)
4
4
 
5
5
  A Vue 3 component library for Nuxt/Vue apps, driven by design tokens:
6
6
  change a handful of CSS variables and the whole kit follows.
@@ -46,7 +46,7 @@ change a handful of CSS variables and the whole kit follows.
46
46
  - **TaoQuantity** — cart quantity: `− | field | +`, integers, stock; minus at the minimum can remove the row
47
47
  - **TaoTextarea** — autosizing multiline. `maxlength` caps input, `count` shows “entered / max”
48
48
  - **TaoPinCode** — N cells. By default a click clears the cell and everything to the right (`clear-on="focus"`); `clear-on="input"` replaces the digit only when typing. The last cell blurs and emits `complete`
49
- - **TaoSlider** — mouse, touch, and arrows. Optional precise input on right-click
49
+ - **TaoSlider** — mouse, touch, and arrows. Optional precise input on right-click. `size="small"` is a thin track for seek / volume
50
50
  - **TaoFileDrop** — drop zone (drag & drop + click). File names listed under the zone, `v-model` is `File[]`. Clearing the whole batch is the parent’s job (`clear-request`)
51
51
 
52
52
  ### Data display
@@ -68,7 +68,7 @@ change a handful of CSS variables and the whole kit follows.
68
68
 
69
69
  ### Navigation
70
70
 
71
- - **TaoLink** — uses `<NuxtLink>` in Nuxt and a plain `<a>` elsewhere, with no extra setup
71
+ - **TaoLink** — `<NuxtLink>` in Nuxt, `<RouterLink>` with vue-router, otherwise a plain `<a>` — no extra setup
72
72
  - **TaoBreadcrumb** — crumbs: the last item is the current page
73
73
  - **TaoDropdownMenu** — dropdown that flips at the viewport edge
74
74
  - **TaoScrollTop** — floating “back to top” with a ripple. Appears on reverse scroll after `boundary`
@@ -46,7 +46,7 @@
46
46
  - **TaoQuantity** — количество в корзине: `− | поле | +`, целые штуки, сток, минус на минимуме может убрать строку
47
47
  - **TaoTextarea** — многострочное поле с авто-высотой. `maxlength` ограничивает ввод, `count` показывает «введено / максимум»
48
48
  - **TaoPinCode** — пин-код из N полей. По умолчанию клик стирает ячейку и все справа (`clear-on="focus"`); `clear-on="input"` заменяет цифру только при вводе. Последняя ячейка снимает фокус и эмитит `complete`
49
- - **TaoSlider** — слайдер: мышь, тач и стрелки. Опциональный точный ввод по правому клику
49
+ - **TaoSlider** — слайдер: мышь, тач и стрелки. Опциональный точный ввод по правому клику. `size="small"` — тонкий трек для seek / громкости
50
50
  - **TaoFileDrop** — зона загрузки (drag & drop + клик). Список имён под зоной, `v-model` — `File[]`. Очистку всей пачки решает родитель (`clear-request`)
51
51
 
52
52
  ### Отображение данных
@@ -68,7 +68,7 @@
68
68
 
69
69
  ### Навигация и взаимодействие
70
70
 
71
- - **TaoLink** — ссылка, использующая `<NuxtLink>` в Nuxt-проекте и обычный `<a>` вне его — без дополнительной настройки
71
+ - **TaoLink** — `<NuxtLink>` в Nuxt, `<RouterLink>` при vue-router, иначе обычный `<a>` — без дополнительной настройки
72
72
  - **TaoBreadcrumb** — крошки: последний пункт — текущая страница
73
73
  - **TaoDropdownMenu** — выпадающее меню с авто-позиционированием у края экрана
74
74
  - **TaoScrollTop** — плавающая кнопка «наверх» с ripple. Появляется при обратном скролле после `boundary`
package/dist/index.cjs CHANGED
@@ -968,6 +968,21 @@
968
968
  }
969
969
  });
970
970
  const TaoTabs = /* @__PURE__ */ _export_sfc(_sfc_main$K, [["__scopeId", "data-v-bf16735e"]]);
971
+ function listenFocusLoss(onLoss) {
972
+ function onVisibilityChange() {
973
+ if (document.hidden) {
974
+ onLoss();
975
+ }
976
+ }
977
+ document.addEventListener("visibilitychange", onVisibilityChange);
978
+ window.addEventListener("blur", onLoss);
979
+ window.addEventListener("pagehide", onLoss);
980
+ return () => {
981
+ document.removeEventListener("visibilitychange", onVisibilityChange);
982
+ window.removeEventListener("blur", onLoss);
983
+ window.removeEventListener("pagehide", onLoss);
984
+ };
985
+ }
971
986
  const _sfc_main$J = /* @__PURE__ */ vue.defineComponent({
972
987
  ...{ name: "TaoTooltip" },
973
988
  __name: "TaoTooltip",
@@ -977,6 +992,14 @@
977
992
  },
978
993
  setup(__props) {
979
994
  const showTooltip = vue.ref(false);
995
+ function hide() {
996
+ showTooltip.value = false;
997
+ }
998
+ let stopFocusLoss;
999
+ vue.onMounted(() => {
1000
+ stopFocusLoss = listenFocusLoss(hide);
1001
+ });
1002
+ vue.onBeforeUnmount(() => stopFocusLoss == null ? void 0 : stopFocusLoss());
980
1003
  return (_ctx, _cache) => {
981
1004
  return vue.openBlock(), vue.createElementBlock("div", {
982
1005
  class: "tao-tooltip-wrapper",
@@ -1000,7 +1023,7 @@
1000
1023
  };
1001
1024
  }
1002
1025
  });
1003
- const TaoTooltip = /* @__PURE__ */ _export_sfc(_sfc_main$J, [["__scopeId", "data-v-6f3b3c11"]]);
1026
+ const TaoTooltip = /* @__PURE__ */ _export_sfc(_sfc_main$J, [["__scopeId", "data-v-fa4a9dac"]]);
1004
1027
  const _hoisted_1$B = { class: "tao-popover" };
1005
1028
  const _sfc_main$I = /* @__PURE__ */ vue.defineComponent({
1006
1029
  ...{ name: "TaoPopover" },
@@ -2262,7 +2285,8 @@
2262
2285
  showValue: { type: Boolean, default: false },
2263
2286
  placeholder: { default: "" },
2264
2287
  modelValue: {},
2265
- disabled: { type: Boolean, default: false }
2288
+ disabled: { type: Boolean, default: false },
2289
+ size: { default: "medium" }
2266
2290
  },
2267
2291
  emits: ["update:modelValue"],
2268
2292
  setup(__props, { emit: __emit }) {
@@ -2282,6 +2306,7 @@
2282
2306
  const current = vue.computed(() => unpackModelValue());
2283
2307
  const trackRef = vue.ref(null);
2284
2308
  const isDragging = vue.ref(false);
2309
+ let dragPointer = -1;
2285
2310
  const shadowInputRef = vue.ref(null);
2286
2311
  const shadowModel = vue.ref("0");
2287
2312
  const showShadowModel = vue.ref(false);
@@ -2303,42 +2328,63 @@
2303
2328
  return props.min;
2304
2329
  }
2305
2330
  const rect = trackRef.value.getBoundingClientRect();
2306
- const clientX = "touches" in event ? event.touches[0].clientX : event.clientX;
2307
- const x = Math.min(Math.max(clientX - rect.left, 0), rect.width);
2331
+ const x = Math.min(Math.max(event.clientX - rect.left, 0), rect.width);
2308
2332
  const ratio = rect.width ? x / rect.width : 0;
2309
2333
  const v = props.min + ratio * (props.max - props.min);
2310
2334
  return clampValue(Math.round((v - props.min) / props.step) * props.step + props.min);
2311
2335
  }
2312
- function handleMove(event) {
2313
- if (props.disabled) {
2336
+ function releaseDragCapture() {
2337
+ const el = trackRef.value;
2338
+ const id = dragPointer;
2339
+ if (!el || id === -1) {
2314
2340
  return;
2315
2341
  }
2316
- commit(calculateValue(event));
2317
- }
2318
- function onMouseUp(event) {
2319
- const evBtn = "touches" in event ? void 0 : event.button;
2320
- if (typeof evBtn !== "undefined" && evBtn !== 0 || showShadowModel.value) {
2342
+ if (typeof el.hasPointerCapture === "function" && !el.hasPointerCapture(id)) {
2321
2343
  return;
2322
2344
  }
2323
- isDragging.value = false;
2345
+ try {
2346
+ el.releasePointerCapture(id);
2347
+ } catch {
2348
+ }
2324
2349
  }
2325
- function onMouseDown(event) {
2326
- if (props.disabled) {
2350
+ function endDrag() {
2351
+ if (!isDragging.value) {
2327
2352
  return;
2328
2353
  }
2329
- const evBtn = "touches" in event ? void 0 : event.button;
2330
- if (typeof evBtn !== "undefined" && evBtn !== 0 || showShadowModel.value) {
2354
+ isDragging.value = false;
2355
+ releaseDragCapture();
2356
+ dragPointer = -1;
2357
+ }
2358
+ function onPointerDown(event) {
2359
+ if (props.disabled || showShadowModel.value || event.button !== 0 || !trackRef.value) {
2331
2360
  return;
2332
2361
  }
2362
+ dragPointer = event.pointerId;
2333
2363
  isDragging.value = true;
2334
- handleMove(event);
2364
+ commit(calculateValue(event));
2335
2365
  event.preventDefault();
2366
+ try {
2367
+ trackRef.value.setPointerCapture(event.pointerId);
2368
+ } catch {
2369
+ }
2370
+ }
2371
+ function onPointerMove(event) {
2372
+ if (!isDragging.value || event.pointerId !== dragPointer) {
2373
+ return;
2374
+ }
2375
+ commit(calculateValue(event));
2336
2376
  }
2337
- function onMouseMove(event) {
2377
+ function onPointerEnd(event) {
2338
2378
  if (!isDragging.value) {
2339
2379
  return;
2340
2380
  }
2341
- handleMove(event);
2381
+ if (dragPointer !== -1 && event.pointerId !== dragPointer) {
2382
+ return;
2383
+ }
2384
+ endDrag();
2385
+ }
2386
+ function onLostPointerCapture() {
2387
+ endDrag();
2342
2388
  }
2343
2389
  function onKeydown(event) {
2344
2390
  if (props.disabled || showShadowModel.value) {
@@ -2392,26 +2438,23 @@
2392
2438
  return (_a = shadowInputRef.value) == null ? void 0 : _a.focus();
2393
2439
  });
2394
2440
  }
2441
+ let stopFocusLoss;
2395
2442
  vue.onMounted(() => {
2396
- window.addEventListener("mouseleave", onMouseUp);
2397
- window.addEventListener("mouseup", onMouseUp);
2398
- window.addEventListener("mousemove", onMouseMove);
2399
- window.addEventListener("touchend", onMouseUp);
2400
- window.addEventListener("touchmove", onMouseMove);
2443
+ stopFocusLoss = listenFocusLoss(endDrag);
2401
2444
  });
2402
2445
  vue.onBeforeUnmount(() => {
2403
- window.removeEventListener("mouseleave", onMouseUp);
2404
- window.removeEventListener("mouseup", onMouseUp);
2405
- window.removeEventListener("mousemove", onMouseMove);
2406
- window.removeEventListener("touchend", onMouseUp);
2407
- window.removeEventListener("touchmove", onMouseMove);
2446
+ endDrag();
2447
+ stopFocusLoss == null ? void 0 : stopFocusLoss();
2408
2448
  });
2409
2449
  return (_ctx, _cache) => {
2410
2450
  return vue.openBlock(), vue.createElementBlock("div", {
2411
2451
  id: controlId.value,
2412
2452
  ref_key: "trackRef",
2413
2453
  ref: trackRef,
2414
- class: vue.normalizeClass(["tao-slider", { "tao-slider--show-value": __props.showValue, "tao-slider--disabled": __props.disabled }]),
2454
+ class: vue.normalizeClass(["tao-slider", [
2455
+ `tao-slider--${__props.size}`,
2456
+ { "tao-slider--show-value": __props.showValue, "tao-slider--disabled": __props.disabled }
2457
+ ]]),
2415
2458
  role: "slider",
2416
2459
  tabindex: __props.disabled ? -1 : 0,
2417
2460
  "aria-valuemin": __props.min,
@@ -2420,8 +2463,11 @@
2420
2463
  "aria-disabled": __props.disabled || void 0,
2421
2464
  "aria-describedby": describedBy.value,
2422
2465
  onContextmenu: vue.withModifiers(onContextMenu, ["prevent"]),
2423
- onMousedown: onMouseDown,
2424
- onTouchstart: onMouseDown,
2466
+ onPointerdown: onPointerDown,
2467
+ onPointermove: onPointerMove,
2468
+ onPointerup: onPointerEnd,
2469
+ onPointercancel: onPointerEnd,
2470
+ onLostpointercapture: onLostPointerCapture,
2425
2471
  onKeydown
2426
2472
  }, [
2427
2473
  _cache[2] || (_cache[2] = vue.createElementVNode("div", { class: "tao-slider__track" }, null, -1)),
@@ -2444,7 +2490,7 @@
2444
2490
  class: "tao-slider__shadow-input",
2445
2491
  onBlur: onBlurShadowModel,
2446
2492
  onKeydown: onShadowKeydown,
2447
- onMousedown: _cache[1] || (_cache[1] = vue.withModifiers(() => {
2493
+ onPointerdown: _cache[1] || (_cache[1] = vue.withModifiers(() => {
2448
2494
  }, ["stop"]))
2449
2495
  }, null, 40, _hoisted_2$m)), [
2450
2496
  [vue.vModelText, shadowModel.value]
@@ -2457,7 +2503,7 @@
2457
2503
  };
2458
2504
  }
2459
2505
  });
2460
- const TaoSlider = /* @__PURE__ */ _export_sfc(_sfc_main$q, [["__scopeId", "data-v-241d1042"]]);
2506
+ const TaoSlider = /* @__PURE__ */ _export_sfc(_sfc_main$q, [["__scopeId", "data-v-24a38782"]]);
2461
2507
  const _hoisted_1$o = { class: "tao-file-drop" };
2462
2508
  const _hoisted_2$l = { class: "tao-file-drop__row" };
2463
2509
  const _hoisted_3$i = ["accept", "multiple"];
@@ -2559,6 +2605,13 @@
2559
2605
  function fileKey(file, index) {
2560
2606
  return `${file.name}-${file.size}-${file.lastModified}-${index}`;
2561
2607
  }
2608
+ let stopFocusLoss;
2609
+ vue.onMounted(() => {
2610
+ stopFocusLoss = listenFocusLoss(() => {
2611
+ isDragging.value = false;
2612
+ });
2613
+ });
2614
+ vue.onBeforeUnmount(() => stopFocusLoss == null ? void 0 : stopFocusLoss());
2562
2615
  return (_ctx, _cache) => {
2563
2616
  return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$o, [
2564
2617
  vue.createElementVNode("div", _hoisted_2$l, [
@@ -2612,7 +2665,7 @@
2612
2665
  };
2613
2666
  }
2614
2667
  });
2615
- const TaoFileDrop = /* @__PURE__ */ _export_sfc(_sfc_main$p, [["__scopeId", "data-v-fbd848b0"]]);
2668
+ const TaoFileDrop = /* @__PURE__ */ _export_sfc(_sfc_main$p, [["__scopeId", "data-v-f97c4e91"]]);
2616
2669
  const _hoisted_1$n = ["aria-expanded"];
2617
2670
  const _hoisted_2$k = {
2618
2671
  key: 1,
@@ -2977,15 +3030,15 @@
2977
3030
  exactPath: { type: Boolean, default: false }
2978
3031
  },
2979
3032
  setup(__props) {
3033
+ var _a;
2980
3034
  const props = __props;
2981
- const linkComponent = vue.computed(() => {
2982
- const resolved = vue.resolveComponent("NuxtLink");
2983
- return typeof resolved === "string" ? "a" : resolved;
2984
- });
3035
+ const globals = ((_a = vue.getCurrentInstance()) == null ? void 0 : _a.appContext.components) ?? {};
3036
+ const linkKind = globals.NuxtLink ? "nuxt" : globals.RouterLink ? "router" : "a";
3037
+ const linkComponent = linkKind === "nuxt" ? globals.NuxtLink : linkKind === "router" ? globals.RouterLink : "a";
2985
3038
  const linkProps = vue.computed(() => {
2986
3039
  const target = props.asNewTab ? "_blank" : void 0;
2987
3040
  const rel = props.private && "noopener noreferrer" || props.asNewTab && "noopener" || void 0;
2988
- if (linkComponent.value === "a") {
3041
+ if (linkKind === "a") {
2989
3042
  return {
2990
3043
  href: typeof props.to === "string" ? props.to : void 0,
2991
3044
  target,
@@ -2996,11 +3049,11 @@
2996
3049
  to: props.to,
2997
3050
  target,
2998
3051
  rel,
2999
- exactPath: props.exactPath
3052
+ ...linkKind === "nuxt" ? { exactPath: props.exactPath } : {}
3000
3053
  };
3001
3054
  });
3002
3055
  return (_ctx, _cache) => {
3003
- return vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(linkComponent.value), vue.mergeProps(linkProps.value, { draggable: "false" }), {
3056
+ return vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(vue.unref(linkComponent)), vue.mergeProps(linkProps.value, { draggable: "false" }), {
3004
3057
  default: vue.withCtx(() => [
3005
3058
  vue.renderSlot(_ctx.$slots, "default")
3006
3059
  ]),
@@ -3701,7 +3754,14 @@
3701
3754
  window.removeEventListener("resize", onViewportChange);
3702
3755
  window.removeEventListener("scroll", onViewportChange, true);
3703
3756
  });
3757
+ let stopFocusLoss;
3758
+ vue.onMounted(() => {
3759
+ stopFocusLoss = listenFocusLoss(() => {
3760
+ picking.value = false;
3761
+ });
3762
+ });
3704
3763
  vue.onBeforeUnmount(() => {
3764
+ stopFocusLoss == null ? void 0 : stopFocusLoss();
3705
3765
  document.removeEventListener("pointerdown", onDocumentPointer);
3706
3766
  window.removeEventListener("resize", onViewportChange);
3707
3767
  window.removeEventListener("scroll", onViewportChange, true);
@@ -3805,7 +3865,7 @@
3805
3865
  };
3806
3866
  }
3807
3867
  });
3808
- const TaoCombobox = /* @__PURE__ */ _export_sfc(_sfc_main$j, [["__scopeId", "data-v-adeb76a4"]]);
3868
+ const TaoCombobox = /* @__PURE__ */ _export_sfc(_sfc_main$j, [["__scopeId", "data-v-60be67d0"]]);
3809
3869
  const ISO_DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
3810
3870
  function formatTaoDateIso(parts) {
3811
3871
  return `${String(parts.y).padStart(4, "0")}-${String(parts.m).padStart(2, "0")}-${String(parts.d).padStart(2, "0")}`;
@@ -4581,7 +4641,14 @@
4581
4641
  window.removeEventListener("resize", onViewportChange);
4582
4642
  window.removeEventListener("scroll", onViewportChange, true);
4583
4643
  });
4644
+ let stopFocusLoss;
4645
+ vue.onMounted(() => {
4646
+ stopFocusLoss = listenFocusLoss(() => {
4647
+ hoverIso.value = null;
4648
+ });
4649
+ });
4584
4650
  vue.onBeforeUnmount(() => {
4651
+ stopFocusLoss == null ? void 0 : stopFocusLoss();
4585
4652
  document.removeEventListener("pointerdown", onDocumentPointer);
4586
4653
  window.removeEventListener("resize", onViewportChange);
4587
4654
  window.removeEventListener("scroll", onViewportChange, true);
@@ -4752,7 +4819,7 @@
4752
4819
  };
4753
4820
  }
4754
4821
  });
4755
- const TaoDateRange = /* @__PURE__ */ _export_sfc(_sfc_main$h, [["__scopeId", "data-v-4e222262"]]);
4822
+ const TaoDateRange = /* @__PURE__ */ _export_sfc(_sfc_main$h, [["__scopeId", "data-v-3d067081"]]);
4756
4823
  const _hoisted_1$g = ["for"];
4757
4824
  const _hoisted_2$e = ["id", "checked", "disabled", "aria-checked"];
4758
4825
  const _hoisted_3$c = {
@@ -5985,10 +6052,13 @@
5985
6052
  }
5986
6053
  function onWindowBlur() {
5987
6054
  endDrag();
6055
+ hoverPaused.value = false;
6056
+ syncAutoplay();
5988
6057
  }
5989
6058
  function onVisibilityChange() {
5990
6059
  if (document.hidden) {
5991
6060
  endDrag();
6061
+ hoverPaused.value = false;
5992
6062
  }
5993
6063
  syncAutoplay();
5994
6064
  }
@@ -6160,7 +6230,7 @@
6160
6230
  };
6161
6231
  }
6162
6232
  });
6163
- const TaoCarousel = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["__scopeId", "data-v-81afb8b0"]]);
6233
+ const TaoCarousel = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["__scopeId", "data-v-4f6de725"]]);
6164
6234
  const _hoisted_1$3 = ["aria-label"];
6165
6235
  const _hoisted_2$2 = ["aria-label"];
6166
6236
  const _hoisted_3$2 = {
@@ -6468,7 +6538,12 @@
6468
6538
  close();
6469
6539
  }
6470
6540
  startTimer();
6541
+ let stopFocusLoss;
6542
+ vue.onMounted(() => {
6543
+ stopFocusLoss = listenFocusLoss(resume);
6544
+ });
6471
6545
  vue.onBeforeUnmount(() => {
6546
+ stopFocusLoss == null ? void 0 : stopFocusLoss();
6472
6547
  clearTimer();
6473
6548
  });
6474
6549
  return (_ctx, _cache) => {
@@ -6593,7 +6668,7 @@
6593
6668
  };
6594
6669
  }
6595
6670
  });
6596
- const TaoToastItem = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-c8a329f5"]]);
6671
+ const TaoToastItem = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-7c4692c0"]]);
6597
6672
  const _hoisted_1$1 = { class: "tao-toast-slot__inner" };
6598
6673
  const _sfc_main$1 = /* @__PURE__ */ vue.defineComponent({
6599
6674
  ...{ name: "TaoToastViewport" },