directix 1.4.0 → 1.4.1

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.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * directix v1.4.0
2
+ * directix v1.4.1
3
3
  * A comprehensive, easy-to-use, and high-performance Vue custom directives library supporting both Vue 2 and Vue 3
4
4
  * (c) 2021-present saqqdy <https://github.com/saqqdy>
5
5
  * Released under the MIT License.
@@ -58,7 +58,7 @@ var __async = (__this, __arguments, generator) => {
58
58
  });
59
59
  };
60
60
  /*!
61
- * directix v1.4.0
61
+ * directix v1.4.1
62
62
  * A comprehensive, easy-to-use, and high-performance Vue custom directives library supporting both Vue 2 and Vue 3
63
63
  * (c) 2021-present saqqdy <https://github.com/saqqdy>
64
64
  * Released under the MIT License.
@@ -2804,15 +2804,15 @@ const vIntersect = defineDirective({
2804
2804
  delete el[STATE_KEY$3];
2805
2805
  }
2806
2806
  });
2807
- let globalObserver = null;
2808
- function getGlobalObserver(preload) {
2809
- if (globalObserver) return globalObserver;
2810
- globalObserver = new IntersectionObserver(
2807
+ let globalObserver$1 = null;
2808
+ function getGlobalObserver$1(preload) {
2809
+ if (globalObserver$1) return globalObserver$1;
2810
+ globalObserver$1 = new IntersectionObserver(
2811
2811
  (entries) => {
2812
2812
  entries.forEach((entry) => {
2813
2813
  if (entry.isIntersecting) {
2814
2814
  load(entry.target);
2815
- globalObserver == null ? void 0 : globalObserver.unobserve(entry.target);
2815
+ globalObserver$1 == null ? void 0 : globalObserver$1.unobserve(entry.target);
2816
2816
  }
2817
2817
  });
2818
2818
  },
@@ -2820,9 +2820,9 @@ function getGlobalObserver(preload) {
2820
2820
  rootMargin: `${preload}px`
2821
2821
  }
2822
2822
  );
2823
- return globalObserver;
2823
+ return globalObserver$1;
2824
2824
  }
2825
- function setSrc(el, src) {
2825
+ function setSrc$1(el, src) {
2826
2826
  if (el.tagName === "IMG") {
2827
2827
  el.src = src;
2828
2828
  } else {
@@ -2847,7 +2847,7 @@ function load(el) {
2847
2847
  const img = new Image();
2848
2848
  img.onload = () => {
2849
2849
  var _a, _b;
2850
- setSrc(el, state.options.src);
2850
+ setSrc$1(el, state.options.src);
2851
2851
  setLazyState(el, "loaded");
2852
2852
  el.classList.remove("v-lazy--loading");
2853
2853
  el.classList.add("v-lazy--loaded");
@@ -2861,7 +2861,7 @@ function load(el) {
2861
2861
  return;
2862
2862
  }
2863
2863
  if (state.options.error) {
2864
- setSrc(el, state.options.error);
2864
+ setSrc$1(el, state.options.error);
2865
2865
  }
2866
2866
  setLazyState(el, "error");
2867
2867
  el.classList.add("v-lazy--error");
@@ -2880,7 +2880,7 @@ function observe(el) {
2880
2880
  state.observer = state.options.observer;
2881
2881
  state.observer.observe(el);
2882
2882
  } else {
2883
- const observer = getGlobalObserver(state.options.preload || 0);
2883
+ const observer = getGlobalObserver$1(state.options.preload || 0);
2884
2884
  state.observer = observer;
2885
2885
  observer.observe(el);
2886
2886
  }
@@ -2890,8 +2890,8 @@ function unobserve(el) {
2890
2890
  if (!state) return;
2891
2891
  if (state.observer) {
2892
2892
  state.observer.unobserve(el);
2893
- } else if (globalObserver) {
2894
- globalObserver.unobserve(el);
2893
+ } else if (globalObserver$1) {
2894
+ globalObserver$1.unobserve(el);
2895
2895
  }
2896
2896
  }
2897
2897
  function normalizeOptions$n(binding) {
@@ -2917,7 +2917,7 @@ const vLazy = defineDirective({
2917
2917
  }
2918
2918
  setLazyState(el, "pending");
2919
2919
  if (options.placeholder) {
2920
- setSrc(el, options.placeholder);
2920
+ setSrc$1(el, options.placeholder);
2921
2921
  }
2922
2922
  el.classList.add("v-lazy");
2923
2923
  const state = {
@@ -2942,7 +2942,7 @@ const vLazy = defineDirective({
2942
2942
  setLazyState(el, "pending");
2943
2943
  el.classList.remove("v-lazy--loaded", "v-lazy--error");
2944
2944
  if (newOptions.placeholder) {
2945
- setSrc(el, newOptions.placeholder);
2945
+ setSrc$1(el, newOptions.placeholder);
2946
2946
  }
2947
2947
  observe(el);
2948
2948
  }
@@ -2967,12 +2967,12 @@ function normalizeOptions$m(binding) {
2967
2967
  disabled: false
2968
2968
  }, binding);
2969
2969
  }
2970
- const DEFAULT_SPINNER = `
2970
+ const DEFAULT_SPINNER$1 = `
2971
2971
  <svg class="v-loading__circular" viewBox="25 25 50 50">
2972
2972
  <circle class="v-loading__path" cx="50" cy="50" r="20" fill="none" stroke-width="2" stroke-miterlimit="10"/>
2973
2973
  </svg>
2974
2974
  `;
2975
- function createLoadingOverlay(options) {
2975
+ function createLoadingOverlay$1(options) {
2976
2976
  const overlay = document.createElement("div");
2977
2977
  overlay.className = options.loadingClass || "v-loading";
2978
2978
  overlay.style.cssText = `
@@ -2988,7 +2988,7 @@ function createLoadingOverlay(options) {
2988
2988
  align-items: center;
2989
2989
  justify-content: center;
2990
2990
  `;
2991
- const spinnerHtml = options.spinner || DEFAULT_SPINNER;
2991
+ const spinnerHtml = options.spinner || DEFAULT_SPINNER$1;
2992
2992
  const spinnerContainer = document.createElement("div");
2993
2993
  spinnerContainer.className = options.spinnerClass || "v-loading__spinner";
2994
2994
  spinnerContainer.innerHTML = spinnerHtml;
@@ -3067,7 +3067,7 @@ const vLoading = defineDirective({
3067
3067
  });
3068
3068
  function showLoading(el, state) {
3069
3069
  if (state.loadingOverlay) return;
3070
- state.loadingOverlay = createLoadingOverlay(state.options);
3070
+ state.loadingOverlay = createLoadingOverlay$1(state.options);
3071
3071
  el.appendChild(state.loadingOverlay);
3072
3072
  if (state.options.lock) {
3073
3073
  el.style.overflow = "hidden";
@@ -3291,15 +3291,15 @@ const vLowercase = defineDirective({
3291
3291
  }
3292
3292
  });
3293
3293
  const STATE_KEY$2 = "__mask";
3294
- const TOKEN_PATTERNS = {
3294
+ const TOKEN_PATTERNS$1 = {
3295
3295
  "#": /\d/,
3296
3296
  A: /[A-Za-z]/,
3297
3297
  N: /[A-Za-z0-9]/,
3298
3298
  X: /./
3299
3299
  };
3300
- function parseMask(mask, placeholder) {
3300
+ function parseMask$1(mask, placeholder) {
3301
3301
  return [...mask].map((char) => {
3302
- const pattern = TOKEN_PATTERNS[char];
3302
+ const pattern = TOKEN_PATTERNS$1[char];
3303
3303
  return pattern ? { pattern, placeholder, isLiteral: false } : { pattern: new RegExp(`\\${char}`), placeholder: char, isLiteral: true };
3304
3304
  });
3305
3305
  }
@@ -3371,7 +3371,7 @@ const vMask = defineDirective({
3371
3371
  const options = normalizeOptions$j(binding.value);
3372
3372
  if (options.disabled || !isBrowser()) return;
3373
3373
  const placeholder = options.placeholder || "_";
3374
- const tokens = parseMask(options.mask, placeholder);
3374
+ const tokens = parseMask$1(options.mask, placeholder);
3375
3375
  const inputHandler = (e) => {
3376
3376
  var _a2, _b, _c;
3377
3377
  const target = e.target;
@@ -3411,7 +3411,7 @@ const vMask = defineDirective({
3411
3411
  const state = el[STATE_KEY$2];
3412
3412
  if (!state) return;
3413
3413
  state.options = normalizeOptions$j(binding.value);
3414
- state.tokens = parseMask(state.options.mask, state.placeholder);
3414
+ state.tokens = parseMask$1(state.options.mask, state.placeholder);
3415
3415
  },
3416
3416
  unmounted(el) {
3417
3417
  const state = el[STATE_KEY$2];
@@ -3764,6 +3764,14 @@ const vNumber = defineDirective({
3764
3764
  }
3765
3765
  });
3766
3766
  const STATE_KEY$1 = "__permission";
3767
+ const WILDCARD$1 = "*";
3768
+ let globalConfig = null;
3769
+ function configurePermission(config) {
3770
+ globalConfig = config;
3771
+ }
3772
+ function getPermissionConfig() {
3773
+ return globalConfig;
3774
+ }
3767
3775
  function normalizeOptions$h(binding) {
3768
3776
  if (!binding) {
3769
3777
  throw new Error("[Directix] v-permission: permission value is required");
@@ -3776,14 +3784,39 @@ function normalizeOptions$h(binding) {
3776
3784
  }
3777
3785
  return binding;
3778
3786
  }
3787
+ function hasPermission$1(required, permissions) {
3788
+ return permissions.includes(WILDCARD$1) || permissions.includes(required);
3789
+ }
3779
3790
  function verifyPermission(options) {
3791
+ var _a;
3780
3792
  if (options.check) {
3781
3793
  return options.check(options.value, options.mode || "some");
3782
3794
  }
3783
- {
3795
+ if (!globalConfig) {
3784
3796
  console.warn("[Directix] v-permission: No permission config provided");
3785
3797
  return true;
3786
3798
  }
3799
+ const permissions = globalConfig.getPermissions();
3800
+ const roles = ((_a = globalConfig.getRoles) == null ? void 0 : _a.call(globalConfig)) || [];
3801
+ const roleMap = globalConfig.roleMap || {};
3802
+ const required = Array.isArray(options.value) ? options.value : [options.value];
3803
+ const mode = options.mode || "some";
3804
+ function checkSingle(value) {
3805
+ if (value in roleMap) {
3806
+ return roles.includes(value);
3807
+ }
3808
+ if (hasPermission$1(value, permissions)) {
3809
+ return true;
3810
+ }
3811
+ for (const role of roles) {
3812
+ const rolePermissions = roleMap[role] || [];
3813
+ if (hasPermission$1(value, rolePermissions)) {
3814
+ return true;
3815
+ }
3816
+ }
3817
+ return false;
3818
+ }
3819
+ return mode === "every" ? required.every(checkSingle) : required.some(checkSingle);
3787
3820
  }
3788
3821
  function handleDenied(el, action, state) {
3789
3822
  var _a, _b;
@@ -4493,23 +4526,23 @@ const vRipple = defineDirective({
4493
4526
  delete el.__ripple;
4494
4527
  }
4495
4528
  });
4496
- const DEFAULT_ALLOWED_TAGS = ["b", "i", "u", "strong", "em", "br", "p", "span", "div"];
4497
- const DEFAULT_ALLOWED_ATTRIBUTES = ["title", "alt", "href", "src"];
4498
- const DANGEROUS_TAGS = ["script", "iframe", "object", "embed", "form", "input", "style", "link", "meta", "base"];
4499
- const DANGEROUS_ATTRIBUTES = ["onclick", "onerror", "onload", "onmouseover", "onfocus", "onblur", "onchange", "onsubmit"];
4529
+ const DEFAULT_ALLOWED_TAGS$1 = ["b", "i", "u", "strong", "em", "br", "p", "span", "div"];
4530
+ const DEFAULT_ALLOWED_ATTRIBUTES$1 = ["title", "alt", "href", "src"];
4531
+ const DANGEROUS_TAGS$1 = ["script", "iframe", "object", "embed", "form", "input", "style", "link", "meta", "base"];
4532
+ const DANGEROUS_ATTRIBUTES$1 = ["onclick", "onerror", "onload", "onmouseover", "onfocus", "onblur", "onchange", "onsubmit"];
4500
4533
  function normalizeOptions$c(binding) {
4501
4534
  if (binding === false) {
4502
4535
  return { disabled: true };
4503
4536
  }
4504
4537
  if (binding === true) {
4505
4538
  return {
4506
- allowedTags: DEFAULT_ALLOWED_TAGS,
4507
- allowedAttributes: DEFAULT_ALLOWED_ATTRIBUTES
4539
+ allowedTags: DEFAULT_ALLOWED_TAGS$1,
4540
+ allowedAttributes: DEFAULT_ALLOWED_ATTRIBUTES$1
4508
4541
  };
4509
4542
  }
4510
4543
  return __spreadValues({
4511
- allowedTags: DEFAULT_ALLOWED_TAGS,
4512
- allowedAttributes: DEFAULT_ALLOWED_ATTRIBUTES,
4544
+ allowedTags: DEFAULT_ALLOWED_TAGS$1,
4545
+ allowedAttributes: DEFAULT_ALLOWED_ATTRIBUTES$1,
4513
4546
  allowDataUrls: false,
4514
4547
  allowStyles: false,
4515
4548
  allowClass: false,
@@ -4525,7 +4558,7 @@ function sanitizeHtml(html, options) {
4525
4558
  }
4526
4559
  const temp = document.createElement("div");
4527
4560
  temp.innerHTML = html;
4528
- for (const tag of DANGEROUS_TAGS) {
4561
+ for (const tag of DANGEROUS_TAGS$1) {
4529
4562
  const elements = temp.getElementsByTagName(tag);
4530
4563
  while (elements.length > 0) {
4531
4564
  (_a = elements[0].parentNode) == null ? void 0 : _a.removeChild(elements[0]);
@@ -4539,7 +4572,7 @@ function sanitizeHtml(html, options) {
4539
4572
  (_a2 = el.parentNode) == null ? void 0 : _a2.replaceChild(text, el);
4540
4573
  return;
4541
4574
  }
4542
- for (const attr of DANGEROUS_ATTRIBUTES) {
4575
+ for (const attr of DANGEROUS_ATTRIBUTES$1) {
4543
4576
  el.removeAttribute(attr);
4544
4577
  }
4545
4578
  const href = el.getAttribute("href");
@@ -4577,8 +4610,8 @@ const vSanitize = defineDirective({
4577
4610
  name: "sanitize",
4578
4611
  ssr: true,
4579
4612
  defaults: {
4580
- allowedTags: DEFAULT_ALLOWED_TAGS,
4581
- allowedAttributes: DEFAULT_ALLOWED_ATTRIBUTES,
4613
+ allowedTags: DEFAULT_ALLOWED_TAGS$1,
4614
+ allowedAttributes: DEFAULT_ALLOWED_ATTRIBUTES$1,
4582
4615
  allowDataUrls: false,
4583
4616
  allowStyles: false,
4584
4617
  allowClass: false,
@@ -7507,6 +7540,105 @@ function useImagePreview(options = {}) {
7507
7540
  bind
7508
7541
  };
7509
7542
  }
7543
+ function useInfiniteScroll(options) {
7544
+ const {
7545
+ onLoad,
7546
+ loading: externalLoading,
7547
+ finished: externalFinished,
7548
+ distance = 0,
7549
+ immediate = true,
7550
+ disabled = false
7551
+ } = options;
7552
+ const internalLoading = vue.ref(false);
7553
+ const internalFinished = vue.ref(false);
7554
+ const loading = externalLoading || internalLoading;
7555
+ const finished = externalFinished || internalFinished;
7556
+ let scrollContainer = null, scrollHandler = null, observer = null, sentinel = null;
7557
+ function load2() {
7558
+ return __async(this, null, function* () {
7559
+ if (vue.unref(loading) || vue.unref(finished) || vue.unref(disabled)) return;
7560
+ internalLoading.value = true;
7561
+ try {
7562
+ yield onLoad();
7563
+ } finally {
7564
+ internalLoading.value = false;
7565
+ }
7566
+ });
7567
+ }
7568
+ function checkScroll() {
7569
+ if (!scrollContainer || vue.unref(loading) || vue.unref(finished) || vue.unref(disabled)) return;
7570
+ const currentDistance = vue.unref(distance);
7571
+ let scrollBottom, containerHeight;
7572
+ if (scrollContainer === window) {
7573
+ scrollBottom = document.documentElement.scrollHeight - window.scrollY;
7574
+ containerHeight = window.innerHeight;
7575
+ } else {
7576
+ const el = scrollContainer;
7577
+ scrollBottom = el.scrollHeight - el.scrollTop;
7578
+ containerHeight = el.clientHeight;
7579
+ }
7580
+ if (scrollBottom <= containerHeight + currentDistance) {
7581
+ load2();
7582
+ }
7583
+ }
7584
+ function bind(element) {
7585
+ if (!isBrowser()) return () => {
7586
+ };
7587
+ stop();
7588
+ if (supportsIntersectionObserver()) {
7589
+ sentinel = document.createElement("div");
7590
+ sentinel.style.cssText = "height: 1px; width: 100%; visibility: hidden;";
7591
+ element.appendChild(sentinel);
7592
+ const parent = getScrollParent(element);
7593
+ const root = parent instanceof Window ? null : parent;
7594
+ observer = new IntersectionObserver(
7595
+ (entries) => {
7596
+ if (entries[0].isIntersecting) {
7597
+ load2();
7598
+ }
7599
+ },
7600
+ {
7601
+ root,
7602
+ rootMargin: `${vue.unref(distance)}px`
7603
+ }
7604
+ );
7605
+ observer.observe(sentinel);
7606
+ } else {
7607
+ scrollContainer = getScrollParent(element);
7608
+ scrollHandler = checkScroll;
7609
+ scrollContainer.addEventListener("scroll", scrollHandler);
7610
+ }
7611
+ if (immediate) {
7612
+ load2();
7613
+ }
7614
+ return stop;
7615
+ }
7616
+ function stop() {
7617
+ if (observer) {
7618
+ observer.disconnect();
7619
+ observer = null;
7620
+ }
7621
+ if (sentinel) {
7622
+ sentinel.remove();
7623
+ sentinel = null;
7624
+ }
7625
+ if (scrollContainer && scrollHandler) {
7626
+ scrollContainer.removeEventListener("scroll", scrollHandler);
7627
+ }
7628
+ scrollContainer = null;
7629
+ scrollHandler = null;
7630
+ }
7631
+ vue.onUnmounted(() => {
7632
+ stop();
7633
+ });
7634
+ return {
7635
+ loading: vue.readonly(loading),
7636
+ finished: vue.readonly(finished),
7637
+ load: load2,
7638
+ bind,
7639
+ stop
7640
+ };
7641
+ }
7510
7642
  function useIntersect(options = {}) {
7511
7643
  const {
7512
7644
  handler,
@@ -7578,6 +7710,269 @@ function useIntersect(options = {}) {
7578
7710
  stop
7579
7711
  };
7580
7712
  }
7713
+ let globalObserver = null;
7714
+ function getGlobalObserver(preload) {
7715
+ if (globalObserver) return globalObserver;
7716
+ globalObserver = new IntersectionObserver(
7717
+ (entries) => {
7718
+ entries.forEach((entry) => {
7719
+ if (entry.isIntersecting) {
7720
+ const el = entry.target;
7721
+ const loadFn = el.__lazyLoad;
7722
+ if (loadFn) {
7723
+ loadFn();
7724
+ globalObserver == null ? void 0 : globalObserver.unobserve(el);
7725
+ }
7726
+ }
7727
+ });
7728
+ },
7729
+ {
7730
+ rootMargin: `${preload}px`
7731
+ }
7732
+ );
7733
+ return globalObserver;
7734
+ }
7735
+ function setSrc(el, src) {
7736
+ if (el.tagName === "IMG") {
7737
+ el.src = src;
7738
+ } else {
7739
+ el.style.backgroundImage = `url("${src}")`;
7740
+ }
7741
+ }
7742
+ function useLazy(options = {}) {
7743
+ const {
7744
+ src,
7745
+ placeholder,
7746
+ error,
7747
+ preload = 0,
7748
+ onLoad,
7749
+ onError,
7750
+ attempt = 1
7751
+ } = options;
7752
+ const state = vue.ref("pending");
7753
+ const isLoading = vue.ref(false);
7754
+ const isLoaded = vue.ref(false);
7755
+ const hasError = vue.ref(false);
7756
+ let currentElement = null, attemptCount = 0, observer = null;
7757
+ function loadImage() {
7758
+ const srcValue = vue.unref(src);
7759
+ if (!srcValue || !currentElement) return;
7760
+ if (state.value === "loading") return;
7761
+ state.value = "loading";
7762
+ isLoading.value = true;
7763
+ const img = new Image();
7764
+ img.onload = () => {
7765
+ setSrc(currentElement, srcValue);
7766
+ state.value = "loaded";
7767
+ isLoading.value = false;
7768
+ isLoaded.value = true;
7769
+ hasError.value = false;
7770
+ currentElement == null ? void 0 : currentElement.classList.remove("v-lazy--loading");
7771
+ currentElement == null ? void 0 : currentElement.classList.add("v-lazy--loaded");
7772
+ onLoad == null ? void 0 : onLoad(currentElement);
7773
+ };
7774
+ img.onerror = () => {
7775
+ currentElement == null ? void 0 : currentElement.classList.remove("v-lazy--loading");
7776
+ attemptCount++;
7777
+ if (attemptCount < attempt) {
7778
+ setTimeout(() => loadImage(), 1e3 * attemptCount);
7779
+ return;
7780
+ }
7781
+ if (error && currentElement) {
7782
+ setSrc(currentElement, error);
7783
+ }
7784
+ state.value = "error";
7785
+ isLoading.value = false;
7786
+ hasError.value = true;
7787
+ currentElement == null ? void 0 : currentElement.classList.add("v-lazy--error");
7788
+ onError == null ? void 0 : onError(currentElement, new Error("Failed to load image"));
7789
+ };
7790
+ currentElement.classList.add("v-lazy--loading");
7791
+ img.src = srcValue;
7792
+ }
7793
+ function bind(element) {
7794
+ if (!isBrowser()) return () => {
7795
+ };
7796
+ unbind();
7797
+ currentElement = element;
7798
+ attemptCount = 0;
7799
+ state.value = "pending";
7800
+ element.classList.add("v-lazy");
7801
+ if (placeholder) {
7802
+ setSrc(element, placeholder);
7803
+ }
7804
+ element.__lazyLoad = loadImage;
7805
+ if (!supportsIntersectionObserver()) {
7806
+ loadImage();
7807
+ return unbind;
7808
+ }
7809
+ observer = getGlobalObserver(preload);
7810
+ observer.observe(element);
7811
+ return unbind;
7812
+ }
7813
+ function unbind() {
7814
+ if (currentElement) {
7815
+ if (observer) {
7816
+ observer.unobserve(currentElement);
7817
+ }
7818
+ delete currentElement.__lazyLoad;
7819
+ currentElement.classList.remove("v-lazy", "v-lazy--loading", "v-lazy--loaded", "v-lazy--error");
7820
+ }
7821
+ currentElement = null;
7822
+ }
7823
+ function reset() {
7824
+ state.value = "pending";
7825
+ isLoading.value = false;
7826
+ isLoaded.value = false;
7827
+ hasError.value = false;
7828
+ attemptCount = 0;
7829
+ }
7830
+ vue.onUnmounted(() => {
7831
+ unbind();
7832
+ });
7833
+ return {
7834
+ state: vue.readonly(state),
7835
+ isLoading: vue.readonly(isLoading),
7836
+ isLoaded: vue.readonly(isLoaded),
7837
+ hasError: vue.readonly(hasError),
7838
+ bind,
7839
+ load: loadImage,
7840
+ reset
7841
+ };
7842
+ }
7843
+ const DEFAULT_SPINNER = `
7844
+ <svg class="v-loading__circular" viewBox="25 25 50 50">
7845
+ <circle class="v-loading__path" cx="50" cy="50" r="20" fill="none" stroke-width="2" stroke-miterlimit="10"/>
7846
+ </svg>
7847
+ `;
7848
+ function createLoadingOverlay(options) {
7849
+ const overlay = document.createElement("div");
7850
+ overlay.className = options.loadingClass || "v-loading";
7851
+ overlay.style.cssText = `
7852
+ position: absolute;
7853
+ top: 0;
7854
+ left: 0;
7855
+ right: 0;
7856
+ bottom: 0;
7857
+ z-index: 1000;
7858
+ background: ${options.background || "rgba(255, 255, 255, 0.9)"};
7859
+ display: flex;
7860
+ flex-direction: column;
7861
+ align-items: center;
7862
+ justify-content: center;
7863
+ `;
7864
+ const spinnerHtml = options.spinner || DEFAULT_SPINNER;
7865
+ const spinnerContainer = document.createElement("div");
7866
+ spinnerContainer.className = options.spinnerClass || "v-loading__spinner";
7867
+ spinnerContainer.innerHTML = spinnerHtml;
7868
+ overlay.appendChild(spinnerContainer);
7869
+ if (options.text) {
7870
+ const textEl = document.createElement("div");
7871
+ textEl.className = options.textClass || "v-loading__text";
7872
+ textEl.textContent = options.text;
7873
+ overlay.appendChild(textEl);
7874
+ }
7875
+ return overlay;
7876
+ }
7877
+ function useLoading(options = {}) {
7878
+ const {
7879
+ initial = false,
7880
+ text,
7881
+ loadingClass = "v-loading",
7882
+ spinnerClass = "v-loading__spinner",
7883
+ textClass = "v-loading__text",
7884
+ spinner,
7885
+ background = "rgba(255, 255, 255, 0.9)",
7886
+ lock = false
7887
+ } = options;
7888
+ const loading = vue.ref(vue.unref(initial));
7889
+ let currentElement = null, loadingOverlay = null, originalPosition = "", originalOverflow = "";
7890
+ function showLoading2() {
7891
+ if (!currentElement || loadingOverlay) return;
7892
+ loadingOverlay = createLoadingOverlay({
7893
+ text: vue.unref(text),
7894
+ loadingClass,
7895
+ spinnerClass,
7896
+ textClass,
7897
+ spinner,
7898
+ background
7899
+ });
7900
+ currentElement.appendChild(loadingOverlay);
7901
+ if (lock) {
7902
+ currentElement.style.overflow = "hidden";
7903
+ }
7904
+ currentElement.classList.add("v-loading--active");
7905
+ }
7906
+ function hideLoading2() {
7907
+ if (!currentElement || !loadingOverlay) return;
7908
+ loadingOverlay.remove();
7909
+ loadingOverlay = null;
7910
+ if (lock) {
7911
+ currentElement.style.overflow = originalOverflow;
7912
+ }
7913
+ currentElement.classList.remove("v-loading--active");
7914
+ }
7915
+ function start() {
7916
+ loading.value = true;
7917
+ }
7918
+ function stop() {
7919
+ loading.value = false;
7920
+ }
7921
+ function toggle() {
7922
+ loading.value = !loading.value;
7923
+ }
7924
+ function bind(element) {
7925
+ if (!isBrowser()) return () => {
7926
+ };
7927
+ unbind();
7928
+ currentElement = element;
7929
+ originalPosition = element.style.position;
7930
+ originalOverflow = element.style.overflow;
7931
+ const computedStyle = getComputedStyle(element);
7932
+ if (computedStyle.position === "static") {
7933
+ element.style.position = "relative";
7934
+ }
7935
+ if (loading.value) {
7936
+ showLoading2();
7937
+ }
7938
+ return unbind;
7939
+ }
7940
+ function unbind() {
7941
+ hideLoading2();
7942
+ if (currentElement) {
7943
+ currentElement.style.position = originalPosition;
7944
+ currentElement.style.overflow = originalOverflow;
7945
+ }
7946
+ currentElement = null;
7947
+ }
7948
+ vue.watch(loading, (newValue) => {
7949
+ if (newValue) {
7950
+ showLoading2();
7951
+ } else {
7952
+ hideLoading2();
7953
+ }
7954
+ });
7955
+ if (typeof text === "object" && "value" in text) {
7956
+ vue.watch(text, (newText) => {
7957
+ if (loadingOverlay) {
7958
+ const textEl = loadingOverlay.querySelector(`.${textClass}`);
7959
+ if (textEl) {
7960
+ textEl.textContent = newText || "";
7961
+ }
7962
+ }
7963
+ });
7964
+ }
7965
+ vue.onUnmounted(() => {
7966
+ unbind();
7967
+ });
7968
+ return {
7969
+ loading,
7970
+ start,
7971
+ stop,
7972
+ toggle,
7973
+ bind
7974
+ };
7975
+ }
7581
7976
  function getEventPosition(e) {
7582
7977
  if ("touches" in e && e.touches.length > 0) {
7583
7978
  return {
@@ -7721,40 +8116,183 @@ function useLowercase(options) {
7721
8116
  function createLowercaser(first = false) {
7722
8117
  return (text) => lowercaseText(text, first);
7723
8118
  }
7724
- function formatMoney(value, options = {}) {
8119
+ const TOKEN_PATTERNS = {
8120
+ "#": /\d/,
8121
+ A: /[A-Za-z]/,
8122
+ N: /[A-Za-z0-9]/,
8123
+ X: /./
8124
+ };
8125
+ function parseMask(mask, placeholder) {
8126
+ return [...mask].map((char) => {
8127
+ const pattern = TOKEN_PATTERNS[char];
8128
+ return pattern ? { pattern, placeholder, isLiteral: false } : { pattern: new RegExp(`\\${char}`), placeholder: char, isLiteral: true };
8129
+ });
8130
+ }
8131
+ function useMask(options) {
7725
8132
  const {
7726
- precision = 2,
7727
- separator = ",",
7728
- decimal = ".",
7729
- symbol = "$",
7730
- symbolPosition = "before"
8133
+ mask,
8134
+ placeholder = "_",
8135
+ showPlaceholder = true,
8136
+ showMaskOnBlur = false,
8137
+ clearIncomplete = false,
8138
+ disabled: _disabled = false,
8139
+ onChange,
8140
+ onComplete
7731
8141
  } = options;
7732
- const fixed = value.toFixed(precision);
7733
- const [intPart, decPart] = fixed.split(".");
7734
- const formattedInt = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
7735
- let result = formattedInt;
7736
- if (precision > 0 && decPart) {
7737
- result += decimal + decPart;
8142
+ let currentElement = null, inputHandler = null, focusHandler = null, blurHandler = null;
8143
+ function getTokens() {
8144
+ return parseMask(vue.unref(mask), placeholder);
8145
+ }
8146
+ function formatValue2(value, tokens, show) {
8147
+ let result = "", valueIndex = 0;
8148
+ for (const token of tokens) {
8149
+ if (valueIndex >= value.length) {
8150
+ result += token.isLiteral ? token.placeholder : show ? placeholder : "";
8151
+ continue;
8152
+ }
8153
+ const inputChar = value[valueIndex];
8154
+ if (token.isLiteral) {
8155
+ if (inputChar === token.placeholder) valueIndex++;
8156
+ result += token.placeholder;
8157
+ } else if (token.pattern.test(inputChar)) {
8158
+ result += inputChar;
8159
+ valueIndex++;
8160
+ } else if (inputChar === placeholder) {
8161
+ result += show ? placeholder : "";
8162
+ valueIndex++;
8163
+ } else {
8164
+ valueIndex++;
8165
+ }
8166
+ }
8167
+ return result;
7738
8168
  }
7739
- return symbolPosition === "before" ? symbol + result : result + symbol;
7740
- }
7741
- function parseMoney(formatted, options = {}) {
7742
- const { decimal = ".", symbol = "$" } = options;
7743
- let cleaned = formatted.replace(new RegExp(`[${symbol}\\s]`, "g"), "");
7744
- if (decimal !== ".") {
7745
- cleaned = cleaned.replace(/,/g, "");
7746
- cleaned = cleaned.replace(new RegExp(`\\${decimal}`, "g"), ".");
7747
- } else {
7748
- cleaned = cleaned.replace(/,/g, "");
8169
+ function getRawValue2(value, tokens) {
8170
+ let raw = "";
8171
+ for (let i = 0; i < value.length && i < tokens.length; i++) {
8172
+ if (!tokens[i].isLiteral && value[i] !== placeholder) {
8173
+ raw += value[i];
8174
+ }
8175
+ }
8176
+ return raw;
7749
8177
  }
7750
- return parseFloat(cleaned) || 0;
7751
- }
7752
- function useMoney(options) {
7753
- const {
7754
- value,
7755
- symbol = "$",
7756
- symbolPosition = "before",
7757
- precision = 2,
8178
+ function checkComplete(value, tokens) {
8179
+ for (let i = 0; i < tokens.length; i++) {
8180
+ if (!tokens[i].isLiteral && (i >= value.length || value[i] === placeholder)) {
8181
+ return false;
8182
+ }
8183
+ }
8184
+ return true;
8185
+ }
8186
+ function getCursorPos2(tokens, rawCursorPos) {
8187
+ let pos = rawCursorPos;
8188
+ while (pos < tokens.length && tokens[pos].isLiteral) {
8189
+ pos++;
8190
+ }
8191
+ return Math.min(pos, tokens.length);
8192
+ }
8193
+ function handleInput(e) {
8194
+ const target = e.target;
8195
+ const tokens = getTokens();
8196
+ const rawValue = target.value;
8197
+ const cursorPos = target.selectionStart || 0;
8198
+ const formatted = formatValue2(rawValue, tokens, showPlaceholder);
8199
+ if (formatted !== rawValue) {
8200
+ target.value = formatted;
8201
+ target.setSelectionRange(getCursorPos2(tokens, cursorPos), getCursorPos2(tokens, cursorPos));
8202
+ target.dispatchEvent(new Event("input", { bubbles: true }));
8203
+ return;
8204
+ }
8205
+ onChange == null ? void 0 : onChange(formatted, getRawValue2(formatted, tokens));
8206
+ if (checkComplete(formatted, tokens)) {
8207
+ onComplete == null ? void 0 : onComplete(formatted);
8208
+ }
8209
+ }
8210
+ function handleFocus() {
8211
+ if (!currentElement) return;
8212
+ const tokens = getTokens();
8213
+ if (!currentElement.value && showPlaceholder) {
8214
+ currentElement.value = formatValue2("", tokens, true);
8215
+ }
8216
+ }
8217
+ function handleBlur() {
8218
+ if (!currentElement) return;
8219
+ const tokens = getTokens();
8220
+ if (!showMaskOnBlur && !checkComplete(currentElement.value, tokens) && clearIncomplete) {
8221
+ currentElement.value = "";
8222
+ }
8223
+ }
8224
+ function bind(element) {
8225
+ if (!isBrowser()) return () => {
8226
+ };
8227
+ unbind();
8228
+ currentElement = element;
8229
+ inputHandler = handleInput;
8230
+ focusHandler = handleFocus;
8231
+ blurHandler = handleBlur;
8232
+ element.addEventListener("input", inputHandler);
8233
+ element.addEventListener("focus", focusHandler);
8234
+ element.addEventListener("blur", blurHandler);
8235
+ if (element.value) {
8236
+ const tokens = getTokens();
8237
+ element.value = formatValue2(element.value, tokens, showPlaceholder);
8238
+ }
8239
+ return unbind;
8240
+ }
8241
+ function unbind() {
8242
+ if (currentElement) {
8243
+ if (inputHandler) currentElement.removeEventListener("input", inputHandler);
8244
+ if (focusHandler) currentElement.removeEventListener("focus", focusHandler);
8245
+ if (blurHandler) currentElement.removeEventListener("blur", blurHandler);
8246
+ }
8247
+ currentElement = null;
8248
+ inputHandler = null;
8249
+ focusHandler = null;
8250
+ blurHandler = null;
8251
+ }
8252
+ vue.onUnmounted(() => {
8253
+ unbind();
8254
+ });
8255
+ return {
8256
+ getFormattedValue: (value) => formatValue2(value, getTokens(), showPlaceholder),
8257
+ getRawValue: (value) => getRawValue2(value, getTokens()),
8258
+ isComplete: (value) => checkComplete(value, getTokens()),
8259
+ bind
8260
+ };
8261
+ }
8262
+ function formatMoney(value, options = {}) {
8263
+ const {
8264
+ precision = 2,
8265
+ separator = ",",
8266
+ decimal = ".",
8267
+ symbol = "$",
8268
+ symbolPosition = "before"
8269
+ } = options;
8270
+ const fixed = value.toFixed(precision);
8271
+ const [intPart, decPart] = fixed.split(".");
8272
+ const formattedInt = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
8273
+ let result = formattedInt;
8274
+ if (precision > 0 && decPart) {
8275
+ result += decimal + decPart;
8276
+ }
8277
+ return symbolPosition === "before" ? symbol + result : result + symbol;
8278
+ }
8279
+ function parseMoney(formatted, options = {}) {
8280
+ const { decimal = ".", symbol = "$" } = options;
8281
+ let cleaned = formatted.replace(new RegExp(`[${symbol}\\s]`, "g"), "");
8282
+ if (decimal !== ".") {
8283
+ cleaned = cleaned.replace(/,/g, "");
8284
+ cleaned = cleaned.replace(new RegExp(`\\${decimal}`, "g"), ".");
8285
+ } else {
8286
+ cleaned = cleaned.replace(/,/g, "");
8287
+ }
8288
+ return parseFloat(cleaned) || 0;
8289
+ }
8290
+ function useMoney(options) {
8291
+ const {
8292
+ value,
8293
+ symbol = "$",
8294
+ symbolPosition = "before",
8295
+ precision = 2,
7758
8296
  separator = ",",
7759
8297
  decimal = "."
7760
8298
  } = options;
@@ -7792,6 +8330,68 @@ function useMoney(options) {
7792
8330
  function createMoneyFormatter(options = {}) {
7793
8331
  return (value) => formatMoney(value, options);
7794
8332
  }
8333
+ function useMutation(options) {
8334
+ const {
8335
+ handler,
8336
+ attributes = false,
8337
+ attributeFilter,
8338
+ childList = true,
8339
+ subtree = false,
8340
+ characterData = false,
8341
+ attributeOldValue = false,
8342
+ characterDataOldValue = false,
8343
+ disabled = false
8344
+ } = options;
8345
+ let currentElement = null, observer = null;
8346
+ function createObserver2() {
8347
+ if (!isBrowser() || !supportsMutationObserver()) {
8348
+ console.warn("[Directix] useMutation: MutationObserver not supported");
8349
+ return null;
8350
+ }
8351
+ return new MutationObserver((mutations, obs) => {
8352
+ if (vue.unref(disabled)) return;
8353
+ handler(mutations, obs);
8354
+ });
8355
+ }
8356
+ function start() {
8357
+ if (!currentElement || !observer || vue.unref(disabled)) return;
8358
+ observer.observe(currentElement, {
8359
+ attributes,
8360
+ attributeFilter,
8361
+ childList,
8362
+ subtree,
8363
+ characterData,
8364
+ attributeOldValue,
8365
+ characterDataOldValue
8366
+ });
8367
+ }
8368
+ function stop() {
8369
+ if (observer) {
8370
+ observer.disconnect();
8371
+ }
8372
+ }
8373
+ function bind(element) {
8374
+ if (!isBrowser()) return () => {
8375
+ };
8376
+ stop();
8377
+ currentElement = element;
8378
+ observer = createObserver2();
8379
+ if (observer && !vue.unref(disabled)) {
8380
+ start();
8381
+ }
8382
+ return stop;
8383
+ }
8384
+ vue.onUnmounted(() => {
8385
+ stop();
8386
+ observer = null;
8387
+ currentElement = null;
8388
+ });
8389
+ return {
8390
+ bind,
8391
+ stop,
8392
+ start
8393
+ };
8394
+ }
7795
8395
  function formatNumber(value, options = {}) {
7796
8396
  const {
7797
8397
  precision = 0,
@@ -8315,6 +8915,206 @@ function useResize(options = {}) {
8315
8915
  stop
8316
8916
  };
8317
8917
  }
8918
+ function createRippleElement(x, y, el, color, duration, initialScale, finalScale) {
8919
+ const rect = el.getBoundingClientRect();
8920
+ const size = Math.max(rect.width, rect.height) * 2;
8921
+ const ripple = document.createElement("span");
8922
+ ripple.className = "v-ripple__wave";
8923
+ ripple.style.cssText = `
8924
+ position: absolute;
8925
+ border-radius: 50%;
8926
+ pointer-events: none;
8927
+ background-color: ${color};
8928
+ width: ${size}px;
8929
+ height: ${size}px;
8930
+ left: ${x - size / 2}px;
8931
+ top: ${y - size / 2}px;
8932
+ transform: scale(${initialScale});
8933
+ opacity: 0.3;
8934
+ z-index: 0;
8935
+ `;
8936
+ if (typeof ripple.animate === "function") {
8937
+ ripple.animate(
8938
+ [
8939
+ { transform: `scale(${initialScale})`, opacity: 0.3 },
8940
+ { transform: `scale(${finalScale})`, opacity: 0 }
8941
+ ],
8942
+ {
8943
+ duration,
8944
+ easing: "ease-out",
8945
+ fill: "forwards"
8946
+ }
8947
+ ).onfinish = () => {
8948
+ ripple.remove();
8949
+ };
8950
+ } else {
8951
+ ripple.style.transition = `transform ${duration}ms ease-out, opacity ${duration}ms ease-out`;
8952
+ String(ripple.offsetHeight);
8953
+ ripple.style.transform = `scale(${finalScale})`;
8954
+ ripple.style.opacity = "0";
8955
+ setTimeout(() => ripple.remove(), duration);
8956
+ }
8957
+ return ripple;
8958
+ }
8959
+ function useRipple(options = {}) {
8960
+ const {
8961
+ color = "currentColor",
8962
+ duration = 600,
8963
+ disabled = false,
8964
+ initialScale = 0,
8965
+ finalScale = 2
8966
+ } = options;
8967
+ let currentElement = null, clickHandler = null;
8968
+ function handleClick(event) {
8969
+ if (vue.unref(disabled)) return;
8970
+ const currentColor = vue.unref(color);
8971
+ const currentDuration = vue.unref(duration);
8972
+ const ripple = createRippleElement(
8973
+ event.clientX - currentElement.getBoundingClientRect().left,
8974
+ event.clientY - currentElement.getBoundingClientRect().top,
8975
+ currentElement,
8976
+ currentColor,
8977
+ currentDuration,
8978
+ initialScale,
8979
+ finalScale
8980
+ );
8981
+ currentElement.appendChild(ripple);
8982
+ }
8983
+ function trigger(event) {
8984
+ var _a, _b;
8985
+ if (!currentElement || vue.unref(disabled)) return;
8986
+ const rect = currentElement.getBoundingClientRect();
8987
+ const x = (_a = event == null ? void 0 : event.x) != null ? _a : rect.width / 2;
8988
+ const y = (_b = event == null ? void 0 : event.y) != null ? _b : rect.height / 2;
8989
+ const ripple = createRippleElement(
8990
+ x,
8991
+ y,
8992
+ currentElement,
8993
+ vue.unref(color),
8994
+ vue.unref(duration),
8995
+ initialScale,
8996
+ finalScale
8997
+ );
8998
+ currentElement.appendChild(ripple);
8999
+ }
9000
+ function bind(element) {
9001
+ if (!isBrowser()) return () => {
9002
+ };
9003
+ unbind();
9004
+ currentElement = element;
9005
+ const computedStyle = getComputedStyle(element);
9006
+ if (computedStyle.position === "static") {
9007
+ element.style.position = "relative";
9008
+ }
9009
+ if (computedStyle.overflow === "visible") {
9010
+ element.style.overflow = "hidden";
9011
+ }
9012
+ element.classList.add("v-ripple");
9013
+ clickHandler = handleClick;
9014
+ element.addEventListener("click", clickHandler);
9015
+ return unbind;
9016
+ }
9017
+ function unbind() {
9018
+ if (currentElement && clickHandler) {
9019
+ currentElement.removeEventListener("click", clickHandler);
9020
+ currentElement.classList.remove("v-ripple");
9021
+ }
9022
+ currentElement = null;
9023
+ clickHandler = null;
9024
+ }
9025
+ vue.onUnmounted(() => {
9026
+ unbind();
9027
+ });
9028
+ return {
9029
+ bind,
9030
+ trigger
9031
+ };
9032
+ }
9033
+ const DEFAULT_ALLOWED_TAGS = ["b", "i", "u", "strong", "em", "br", "p", "span", "div"];
9034
+ const DEFAULT_ALLOWED_ATTRIBUTES = ["title", "alt", "href", "src"];
9035
+ const DANGEROUS_TAGS = ["script", "iframe", "object", "embed", "form", "input", "style", "link", "meta", "base"];
9036
+ const DANGEROUS_ATTRIBUTES = ["onclick", "onerror", "onload", "onmouseover", "onfocus", "onblur", "onchange", "onsubmit"];
9037
+ function useSanitize(options = {}) {
9038
+ const {
9039
+ allowedTags = DEFAULT_ALLOWED_TAGS,
9040
+ allowedAttributes = DEFAULT_ALLOWED_ATTRIBUTES,
9041
+ allowDataUrls = false,
9042
+ allowStyles = false,
9043
+ allowClass = false,
9044
+ allowId = false,
9045
+ handler
9046
+ } = options;
9047
+ function sanitizeHtml2(html) {
9048
+ var _a;
9049
+ if (handler) {
9050
+ return handler(html);
9051
+ }
9052
+ if (!isBrowser()) return html;
9053
+ const temp = document.createElement("div");
9054
+ temp.innerHTML = html;
9055
+ for (const tag of DANGEROUS_TAGS) {
9056
+ const elements = temp.getElementsByTagName(tag);
9057
+ while (elements.length > 0) {
9058
+ (_a = elements[0].parentNode) == null ? void 0 : _a.removeChild(elements[0]);
9059
+ }
9060
+ }
9061
+ const processElement = (el) => {
9062
+ var _a2;
9063
+ const tagName = el.tagName.toLowerCase();
9064
+ if (!allowedTags.includes(tagName)) {
9065
+ const text = document.createTextNode(el.textContent || "");
9066
+ (_a2 = el.parentNode) == null ? void 0 : _a2.replaceChild(text, el);
9067
+ return;
9068
+ }
9069
+ for (const attr of DANGEROUS_ATTRIBUTES) {
9070
+ el.removeAttribute(attr);
9071
+ }
9072
+ const href = el.getAttribute("href");
9073
+ if (href && href.toLowerCase().startsWith("javascript:")) {
9074
+ el.removeAttribute("href");
9075
+ }
9076
+ if (!allowDataUrls) {
9077
+ const src = el.getAttribute("src");
9078
+ if (src && src.toLowerCase().startsWith("data:")) {
9079
+ el.removeAttribute("src");
9080
+ }
9081
+ }
9082
+ const attrs = Array.from(el.attributes);
9083
+ for (const attr of attrs) {
9084
+ const isAllowed = allowedAttributes.includes(attr.name.toLowerCase());
9085
+ const isClass = attr.name === "class" && allowClass;
9086
+ const isId = attr.name === "id" && allowId;
9087
+ const isStyle = attr.name === "style" && allowStyles;
9088
+ if (!isAllowed && !isClass && !isId && !isStyle) {
9089
+ el.removeAttribute(attr.name);
9090
+ }
9091
+ }
9092
+ for (const child of Array.from(el.children)) {
9093
+ processElement(child);
9094
+ }
9095
+ };
9096
+ for (const child of Array.from(temp.children)) {
9097
+ processElement(child);
9098
+ }
9099
+ return temp.innerHTML;
9100
+ }
9101
+ function bind(element) {
9102
+ if (!isBrowser()) return () => {
9103
+ };
9104
+ const content = element.innerHTML;
9105
+ if (content) {
9106
+ element.innerHTML = sanitizeHtml2(content);
9107
+ }
9108
+ return () => {
9109
+ };
9110
+ }
9111
+ vue.onUnmounted(() => {
9112
+ });
9113
+ return {
9114
+ sanitize: sanitizeHtml2,
9115
+ bind
9116
+ };
9117
+ }
8318
9118
  function getScrollInfoFromContainer(container, lastScrollLeft, lastScrollTop) {
8319
9119
  let scrollLeft = 0, scrollTop = 0, scrollLeftMax = 0, scrollTopMax = 0;
8320
9120
  if (container === window) {
@@ -8463,6 +9263,97 @@ function useScroll(options = {}) {
8463
9263
  scrollTo
8464
9264
  };
8465
9265
  }
9266
+ function useSticky(options = {}) {
9267
+ const {
9268
+ offsetTop = 0,
9269
+ onStick,
9270
+ disabled = false
9271
+ } = options;
9272
+ const isSticky = vue.ref(false);
9273
+ let currentElement = null, scrollContainer = null, scrollHandler = null, placeholder = null, originalStyles = null;
9274
+ function checkSticky2() {
9275
+ var _a;
9276
+ if (!currentElement || vue.unref(disabled)) return;
9277
+ const container = scrollContainer === window ? document.documentElement : scrollContainer;
9278
+ const containerRect = container.getBoundingClientRect();
9279
+ const elementRect = currentElement.getBoundingClientRect();
9280
+ const currentOffsetTop = vue.unref(offsetTop);
9281
+ const shouldBeSticky = elementRect.top <= currentOffsetTop && containerRect.top <= currentOffsetTop;
9282
+ if (shouldBeSticky !== isSticky.value) {
9283
+ isSticky.value = shouldBeSticky;
9284
+ if (shouldBeSticky) {
9285
+ if (!placeholder) {
9286
+ placeholder = document.createElement("div");
9287
+ placeholder.style.width = `${elementRect.width}px`;
9288
+ placeholder.style.height = `${elementRect.height}px`;
9289
+ (_a = currentElement.parentNode) == null ? void 0 : _a.insertBefore(placeholder, currentElement);
9290
+ }
9291
+ currentElement.style.position = "fixed";
9292
+ currentElement.style.top = `${currentOffsetTop}px`;
9293
+ currentElement.style.width = `${elementRect.width}px`;
9294
+ currentElement.classList.add("v-sticky--active");
9295
+ } else {
9296
+ if (placeholder) {
9297
+ placeholder.remove();
9298
+ placeholder = null;
9299
+ }
9300
+ if (originalStyles) {
9301
+ currentElement.style.position = originalStyles.position;
9302
+ currentElement.style.top = originalStyles.top;
9303
+ currentElement.style.width = originalStyles.width;
9304
+ }
9305
+ currentElement.classList.remove("v-sticky--active");
9306
+ }
9307
+ onStick == null ? void 0 : onStick(shouldBeSticky);
9308
+ }
9309
+ }
9310
+ function bind(element) {
9311
+ if (!isBrowser()) return () => {
9312
+ };
9313
+ stop();
9314
+ currentElement = element;
9315
+ originalStyles = {
9316
+ position: element.style.position,
9317
+ top: element.style.top,
9318
+ width: element.style.width
9319
+ };
9320
+ element.classList.add("v-sticky");
9321
+ scrollContainer = getScrollParent(element);
9322
+ scrollHandler = checkSticky2;
9323
+ scrollContainer.addEventListener("scroll", scrollHandler, { passive: true });
9324
+ checkSticky2();
9325
+ return stop;
9326
+ }
9327
+ function stop() {
9328
+ if (scrollContainer && scrollHandler) {
9329
+ scrollContainer.removeEventListener("scroll", scrollHandler);
9330
+ }
9331
+ if (currentElement) {
9332
+ if (originalStyles) {
9333
+ currentElement.style.position = originalStyles.position;
9334
+ currentElement.style.top = originalStyles.top;
9335
+ currentElement.style.width = originalStyles.width;
9336
+ }
9337
+ currentElement.classList.remove("v-sticky", "v-sticky--active");
9338
+ }
9339
+ if (placeholder) {
9340
+ placeholder.remove();
9341
+ placeholder = null;
9342
+ }
9343
+ scrollContainer = null;
9344
+ scrollHandler = null;
9345
+ currentElement = null;
9346
+ isSticky.value = false;
9347
+ }
9348
+ vue.onUnmounted(() => {
9349
+ stop();
9350
+ });
9351
+ return {
9352
+ isSticky: vue.readonly(isSticky),
9353
+ bind,
9354
+ stop
9355
+ };
9356
+ }
8466
9357
  const DEFAULT_DIRECTIONS = ["left", "right", "up", "down"];
8467
9358
  function getSwipeDirection(deltaX, deltaY, allowedDirections) {
8468
9359
  const absX = Math.abs(deltaX);
@@ -8663,6 +9554,167 @@ function useThrottle(options) {
8663
9554
  function throttleFn(fn, wait = 300, options) {
8664
9555
  return throttle(fn, wait, options);
8665
9556
  }
9557
+ function useTooltip(options = {}) {
9558
+ const {
9559
+ content,
9560
+ placement = "top",
9561
+ trigger = "hover",
9562
+ delay = 0,
9563
+ hideDelay = 0,
9564
+ arrow = true,
9565
+ class: customClass,
9566
+ onShow,
9567
+ onHide,
9568
+ disabled = false
9569
+ } = options;
9570
+ const isVisible = vue.ref(false);
9571
+ let currentElement = null, tooltipEl = null, showTimeout = null, hideTimeout = null;
9572
+ const eventHandlers = {};
9573
+ function createTooltip2() {
9574
+ const tooltip = document.createElement("div");
9575
+ tooltip.className = `v-tooltip v-tooltip--${placement}${customClass ? ` ${customClass}` : ""}${arrow ? " v-tooltip--arrow" : ""}`;
9576
+ tooltip.style.cssText = `
9577
+ position: absolute;
9578
+ z-index: 9999;
9579
+ opacity: 0;
9580
+ transition: opacity 0.2s;
9581
+ pointer-events: none;
9582
+ `;
9583
+ return tooltip;
9584
+ }
9585
+ function positionTooltip2() {
9586
+ if (!tooltipEl || !currentElement) return;
9587
+ const elementRect = currentElement.getBoundingClientRect();
9588
+ const tooltipRect = tooltipEl.getBoundingClientRect();
9589
+ let top = 0, left = 0;
9590
+ switch (placement) {
9591
+ case "top":
9592
+ top = elementRect.top - tooltipRect.height - 8;
9593
+ left = elementRect.left + (elementRect.width - tooltipRect.width) / 2;
9594
+ break;
9595
+ case "bottom":
9596
+ top = elementRect.bottom + 8;
9597
+ left = elementRect.left + (elementRect.width - tooltipRect.width) / 2;
9598
+ break;
9599
+ case "left":
9600
+ top = elementRect.top + (elementRect.height - tooltipRect.height) / 2;
9601
+ left = elementRect.left - tooltipRect.width - 8;
9602
+ break;
9603
+ case "right":
9604
+ top = elementRect.top + (elementRect.height - tooltipRect.height) / 2;
9605
+ left = elementRect.right + 8;
9606
+ break;
9607
+ }
9608
+ if (left < 0) left = 8;
9609
+ if (left + tooltipRect.width > window.innerWidth) left = window.innerWidth - tooltipRect.width - 8;
9610
+ if (top < 0) top = 8;
9611
+ if (top + tooltipRect.height > window.innerHeight) top = window.innerHeight - tooltipRect.height - 8;
9612
+ tooltipEl.style.top = `${top + window.scrollY}px`;
9613
+ tooltipEl.style.left = `${left + window.scrollX}px`;
9614
+ }
9615
+ function show() {
9616
+ if (vue.unref(disabled) || !content) return;
9617
+ if (hideTimeout) {
9618
+ clearTimeout(hideTimeout);
9619
+ hideTimeout = null;
9620
+ }
9621
+ if (delay > 0) {
9622
+ showTimeout = setTimeout(doShow, delay);
9623
+ } else {
9624
+ doShow();
9625
+ }
9626
+ }
9627
+ function doShow() {
9628
+ var _a;
9629
+ if (!currentElement) return;
9630
+ if (!tooltipEl) {
9631
+ tooltipEl = createTooltip2();
9632
+ document.body.appendChild(tooltipEl);
9633
+ }
9634
+ tooltipEl.textContent = (_a = vue.unref(content)) != null ? _a : null;
9635
+ positionTooltip2();
9636
+ tooltipEl.style.opacity = "1";
9637
+ isVisible.value = true;
9638
+ onShow == null ? void 0 : onShow();
9639
+ }
9640
+ function hide() {
9641
+ if (showTimeout) {
9642
+ clearTimeout(showTimeout);
9643
+ showTimeout = null;
9644
+ }
9645
+ if (hideDelay > 0) {
9646
+ hideTimeout = setTimeout(doHide, hideDelay);
9647
+ } else {
9648
+ doHide();
9649
+ }
9650
+ }
9651
+ function doHide() {
9652
+ if (tooltipEl) {
9653
+ tooltipEl.style.opacity = "0";
9654
+ }
9655
+ isVisible.value = false;
9656
+ onHide == null ? void 0 : onHide();
9657
+ }
9658
+ function toggle() {
9659
+ if (isVisible.value) {
9660
+ hide();
9661
+ } else {
9662
+ show();
9663
+ }
9664
+ }
9665
+ function bind(element) {
9666
+ if (!isBrowser()) return () => {
9667
+ };
9668
+ unbind();
9669
+ currentElement = element;
9670
+ if (trigger === "hover") {
9671
+ eventHandlers.mouseenter = show;
9672
+ eventHandlers.mouseleave = hide;
9673
+ element.addEventListener("mouseenter", eventHandlers.mouseenter);
9674
+ element.addEventListener("mouseleave", eventHandlers.mouseleave);
9675
+ } else if (trigger === "click") {
9676
+ eventHandlers.click = toggle;
9677
+ element.addEventListener("click", eventHandlers.click);
9678
+ } else if (trigger === "focus") {
9679
+ eventHandlers.focus = show;
9680
+ eventHandlers.blur = hide;
9681
+ element.addEventListener("focus", eventHandlers.focus);
9682
+ element.addEventListener("blur", eventHandlers.blur);
9683
+ }
9684
+ return unbind;
9685
+ }
9686
+ function unbind() {
9687
+ if (currentElement) {
9688
+ Object.entries(eventHandlers).forEach(([event, handler]) => {
9689
+ currentElement.removeEventListener(event, handler);
9690
+ });
9691
+ }
9692
+ if (tooltipEl) {
9693
+ tooltipEl.remove();
9694
+ tooltipEl = null;
9695
+ }
9696
+ if (showTimeout) {
9697
+ clearTimeout(showTimeout);
9698
+ showTimeout = null;
9699
+ }
9700
+ if (hideTimeout) {
9701
+ clearTimeout(hideTimeout);
9702
+ hideTimeout = null;
9703
+ }
9704
+ currentElement = null;
9705
+ isVisible.value = false;
9706
+ }
9707
+ vue.onUnmounted(() => {
9708
+ unbind();
9709
+ });
9710
+ return {
9711
+ isVisible: vue.readonly(isVisible),
9712
+ show,
9713
+ hide,
9714
+ toggle,
9715
+ bind
9716
+ };
9717
+ }
8666
9718
  function useTouch(options = {}) {
8667
9719
  const {
8668
9720
  onSwipe,
@@ -8842,6 +9894,7 @@ function escapeRegex(str) {
8842
9894
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8843
9895
  }
8844
9896
  function trimText(text, position = "both", chars) {
9897
+ if (!text) return "";
8845
9898
  const charPattern = chars ? `[\\s${escapeRegex(chars)}]` : "\\s";
8846
9899
  switch (position) {
8847
9900
  case "start":
@@ -8884,6 +9937,74 @@ function useTrim(options) {
8884
9937
  function createTrimmer(position = "both", chars) {
8885
9938
  return (text) => trimText(text, position, chars);
8886
9939
  }
9940
+ function truncateEnd(text, length, omission) {
9941
+ if (text.length <= length) return text;
9942
+ return text.slice(0, length - omission.length) + omission;
9943
+ }
9944
+ function truncateStart(text, length, omission) {
9945
+ if (text.length <= length) return text;
9946
+ return omission + text.slice(-(length - omission.length));
9947
+ }
9948
+ function truncateMiddle(text, length, omission) {
9949
+ if (text.length <= length) return text;
9950
+ const availableLength = length - omission.length;
9951
+ const startLength = Math.ceil(availableLength / 2);
9952
+ const endLength = Math.floor(availableLength / 2);
9953
+ return text.slice(0, startLength) + omission + text.slice(-endLength);
9954
+ }
9955
+ function useTruncate(options) {
9956
+ const {
9957
+ text,
9958
+ length = 100,
9959
+ position = "end",
9960
+ omission = "..."
9961
+ } = options;
9962
+ const truncated = vue.ref("");
9963
+ const isTruncated = vue.ref(false);
9964
+ const originalLength = vue.ref(0);
9965
+ function truncate(textValue, lengthValue, positionValue) {
9966
+ const maxLen = lengthValue != null ? lengthValue : vue.unref(length);
9967
+ const pos = positionValue != null ? positionValue : vue.unref(position);
9968
+ if (textValue.length <= maxLen) {
9969
+ return textValue;
9970
+ }
9971
+ switch (pos) {
9972
+ case "start":
9973
+ return truncateStart(textValue, maxLen, omission);
9974
+ case "middle":
9975
+ return truncateMiddle(textValue, maxLen, omission);
9976
+ case "end":
9977
+ default:
9978
+ return truncateEnd(textValue, maxLen, omission);
9979
+ }
9980
+ }
9981
+ function update() {
9982
+ const textValue = vue.unref(text);
9983
+ const lengthValue = vue.unref(length);
9984
+ originalLength.value = textValue.length;
9985
+ isTruncated.value = textValue.length > lengthValue;
9986
+ truncated.value = truncate(textValue);
9987
+ }
9988
+ vue.watch(
9989
+ () => vue.unref(text),
9990
+ update,
9991
+ { immediate: true }
9992
+ );
9993
+ vue.watch(
9994
+ () => vue.unref(length),
9995
+ update
9996
+ );
9997
+ vue.watch(
9998
+ () => vue.unref(position),
9999
+ update
10000
+ );
10001
+ return {
10002
+ truncated: vue.readonly(truncated),
10003
+ isTruncated: vue.readonly(isTruncated),
10004
+ originalLength: vue.readonly(originalLength),
10005
+ truncate
10006
+ };
10007
+ }
8887
10008
  function uppercaseText(text, firstOnly = false) {
8888
10009
  if (!text) return text;
8889
10010
  if (firstOnly) {
@@ -9382,6 +10503,7 @@ exports.addCleanupVue3 = addCleanup;
9382
10503
  exports.calculateTime = calculateTime;
9383
10504
  exports.capitalizeText = capitalizeText;
9384
10505
  exports.capitalizeWord = capitalizeWord;
10506
+ exports.configurePermission = configurePermission;
9385
10507
  exports.createCapitalizer = createCapitalizer;
9386
10508
  exports.createDelayedClick = createDelayedClick;
9387
10509
  exports.createLowercaser = createLowercaser;
@@ -9403,6 +10525,7 @@ exports.formatNumber = formatNumber;
9403
10525
  exports.formatTime = formatTime;
9404
10526
  exports.generateId = generateId;
9405
10527
  exports.get = get;
10528
+ exports.getPermissionConfig = getPermissionConfig;
9406
10529
  exports.getVueVersion = getVueVersion;
9407
10530
  exports.isArray = isArray;
9408
10531
  exports.isBoolean = isBoolean;
@@ -9447,20 +10570,30 @@ exports.useFocus = useFocus;
9447
10570
  exports.useHotkey = useHotkey;
9448
10571
  exports.useHover = useHover;
9449
10572
  exports.useImagePreview = useImagePreview;
10573
+ exports.useInfiniteScroll = useInfiniteScroll;
9450
10574
  exports.useIntersect = useIntersect;
10575
+ exports.useLazy = useLazy;
10576
+ exports.useLoading = useLoading;
9451
10577
  exports.useLongPress = useLongPress;
9452
10578
  exports.useLowercase = useLowercase;
10579
+ exports.useMask = useMask;
9453
10580
  exports.useMoney = useMoney;
10581
+ exports.useMutation = useMutation;
9454
10582
  exports.useNumber = useNumber;
9455
10583
  exports.usePermission = usePermission;
9456
10584
  exports.usePrint = usePrint;
9457
10585
  exports.usePullRefresh = usePullRefresh;
9458
10586
  exports.useResize = useResize;
10587
+ exports.useRipple = useRipple;
10588
+ exports.useSanitize = useSanitize;
9459
10589
  exports.useScroll = useScroll;
10590
+ exports.useSticky = useSticky;
9460
10591
  exports.useSwipe = useSwipe;
9461
10592
  exports.useThrottle = useThrottle;
10593
+ exports.useTooltip = useTooltip;
9462
10594
  exports.useTouch = useTouch;
9463
10595
  exports.useTrim = useTrim;
10596
+ exports.useTruncate = useTruncate;
9464
10597
  exports.useUppercase = useUppercase;
9465
10598
  exports.useVirtualList = useVirtualList;
9466
10599
  exports.useVisible = useVisible;