directix 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,13 +4,13 @@
4
4
  [![npm downloads](https://img.shields.io/npm/dm/directix.svg)](https://www.npmjs.com/package/directix)
5
5
  [![GitHub license](https://img.shields.io/github/license/saqqdy/directix)](https://github.com/saqqdy/directix/blob/master/LICENSE)
6
6
 
7
- **[中文文档](README_CN.md)**
7
+ **English** | **[中文文档](README_CN.md)**
8
8
 
9
9
  A comprehensive, easy-to-use, and high-performance Vue custom directives library supporting both Vue 2 and Vue 3.
10
10
 
11
11
  ## Features
12
12
 
13
- - 🎯 **Comprehensive** - 40 commonly used directives and 40 composables
13
+ - 🎯 **Comprehensive** - 57 commonly used directives and 57 composables
14
14
  - 🔄 **Vue 2/3 Compatible** - Single codebase supports both Vue 2 and Vue 3
15
15
  - 📦 **Tree-shakable** - Import only what you need
16
16
  - 🔒 **TypeScript** - Full TypeScript support with type definitions
@@ -18,15 +18,16 @@ A comprehensive, easy-to-use, and high-performance Vue custom directives library
18
18
  - 📦 **Multiple Formats** - ESM, CJS, and IIFE (CDN) formats available
19
19
  - ⚡ **Zero Dependencies** - Lightweight with minimal bundle size
20
20
  - 🎨 **Composables** - Every directive has a corresponding composable for Composition API
21
+ - 🔧 **Utility Exports** - Export `configurePermission`, `getPermissionConfig` and other utilities for advanced usage
21
22
 
22
23
  ## Online Demo
23
24
 
24
- Try it online with StackBlitz:
25
+ Try it online with StackBlitz or CodeSandbox:
25
26
 
26
- | Demo | Link |
27
- |------|------|
28
- | Vue 3 | [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/saqqdy/directix/tree/master/examples/vue3) |
29
- | Vue 2 | [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/saqqdy/directix/tree/master/examples/vue2) |
27
+ | Demo | StackBlitz | CodeSandbox |
28
+ |------|------------|-------------|
29
+ | Vue 3 | [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/saqqdy/directix/tree/master/examples/vue3) | [![Open in CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/saqqdy/directix/tree/master/examples/vue3) |
30
+ | Vue 2 | [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/saqqdy/directix/tree/master/examples/vue2) | [![Open in CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/saqqdy/directix/tree/master/examples/vue2) |
30
31
 
31
32
  ## Installation
32
33
 
@@ -213,6 +214,38 @@ See the [Composables](#composables) section below for all available composables.
213
214
  | `v-countdown` | Countdown timer display | ✅ |
214
215
  | `v-print` | Print element content | ❌ |
215
216
  | `v-watermark` | Watermark overlay | ✅ |
217
+ | `v-skeleton` | Skeleton loading placeholder | ✅ |
218
+ | `v-progress` | Progress bar animation | ❌ |
219
+ | `v-counter` | Animated number counter | ✅ |
220
+
221
+ ### Gesture Directives
222
+
223
+ | Directive | Description | SSR |
224
+ |-----------|-------------|-----|
225
+ | `v-pan` | Pan/drag gesture | ❌ |
226
+ | `v-pinch` | Pinch/zoom gesture | ❌ |
227
+ | `v-rotate-gesture` | Rotation gesture | ❌ |
228
+
229
+ ### Visual Effect Directives
230
+
231
+ | Directive | Description | SSR |
232
+ |-----------|-------------|-----|
233
+ | `v-blur` | Background blur overlay | ❌ |
234
+ | `v-fade` | Fade in/out transition | ✅ |
235
+ | `v-parallax` | Parallax scrolling effect | ❌ |
236
+ | `v-lottie` | Lottie animation player | ❌ |
237
+ | `v-typewriter` | Typewriter animation | ✅ |
238
+ | `v-click-wave` | Click wave effect | ❌ |
239
+
240
+ ### Data Directives
241
+
242
+ | Directive | Description | SSR |
243
+ |-----------|-------------|-----|
244
+ | `v-export` | Export data (CSV/JSON/HTML) | ❌ |
245
+ | `v-highlight` | Keyword highlighting | ✅ |
246
+ | `v-emoji` | Emoji input filter | ❌ |
247
+ | `v-context-menu` | Right-click context menu | ❌ |
248
+ | `v-fullscreen` | Fullscreen toggle | ❌ |
216
249
 
217
250
  > ✅ = SSR compatible | ❌ = Not SSR compatible
218
251
 
@@ -289,6 +322,23 @@ Every directive has a corresponding composable function for use with the Composi
289
322
  | `useCountdown` | Countdown timer |
290
323
  | `usePrint` | Print content |
291
324
  | `useWatermark` | Watermark overlay |
325
+ | `useSkeleton` | Skeleton loading state |
326
+ | `useProgress` | Progress bar control |
327
+ | `useCounter` | Animated number counter |
328
+ | `usePan` | Pan gesture detection |
329
+ | `usePinch` | Pinch gesture detection |
330
+ | `useRotateGesture` | Rotation gesture detection |
331
+ | `useBlur` | Blur overlay control |
332
+ | `useFade` | Fade transition control |
333
+ | `useParallax` | Parallax scrolling |
334
+ | `useLottie` | Lottie animation control |
335
+ | `useTypewriter` | Typewriter effect |
336
+ | `useExport` | Data export utilities |
337
+ | `useHighlight` | Keyword highlighting |
338
+ | `useEmoji` | Emoji filtering |
339
+ | `useContextMenu` | Context menu control |
340
+ | `useFullscreen` | Fullscreen mode control |
341
+ | `useClickWave` | Click wave effect |
292
342
 
293
343
  ### Composable Usage Example
294
344
 
@@ -342,12 +392,19 @@ Detect clicks outside an element, useful for closing dropdowns, modals, etc.
342
392
 
343
393
  <script setup>
344
394
  import { ref } from 'vue'
395
+ import { useClickOutside } from 'directix'
345
396
 
346
397
  const show = ref(false)
347
398
 
348
399
  function closeDropdown() {
349
400
  show.value = false
350
401
  }
402
+
403
+ // Composable usage
404
+ const containerRef = ref()
405
+ useClickOutside(containerRef, () => {
406
+ show.value = false
407
+ })
351
408
  </script>
352
409
  ```
353
410
 
@@ -367,6 +424,9 @@ Copy text to clipboard with a simple directive.
367
424
  </template>
368
425
 
369
426
  <script setup>
427
+ import { ref } from 'vue'
428
+ import { useCopy } from 'directix'
429
+
370
430
  const textToCopy = 'Hello, World!'
371
431
 
372
432
  function handleSuccess(text) {
@@ -376,6 +436,10 @@ function handleSuccess(text) {
376
436
  function handleError(error) {
377
437
  console.error('Copy failed:', error)
378
438
  }
439
+
440
+ // Composable usage
441
+ const sourceText = ref('Hello World')
442
+ const { copy, copied } = useCopy({ source: sourceText })
379
443
  </script>
380
444
  ```
381
445
 
@@ -396,9 +460,17 @@ Debounce event handlers to limit execution frequency.
396
460
  </template>
397
461
 
398
462
  <script setup>
463
+ import { useDebounce } from 'directix'
464
+
399
465
  function handleInput(event) {
400
466
  console.log('Debounced input:', event.target.value)
401
467
  }
468
+
469
+ // Composable usage
470
+ const { run: debouncedSearch, cancel } = useDebounce({
471
+ handler: (query) => fetchResults(query),
472
+ wait: 500
473
+ })
402
474
  </script>
403
475
  ```
404
476
 
@@ -421,9 +493,17 @@ Throttle event handlers to limit execution frequency.
421
493
  </template>
422
494
 
423
495
  <script setup>
496
+ import { useThrottle } from 'directix'
497
+
424
498
  function handleClick() {
425
499
  console.log('Throttled click')
426
500
  }
501
+
502
+ // Composable usage
503
+ const { run: throttledScroll, cancel } = useThrottle({
504
+ handler: (position) => updatePosition(position),
505
+ wait: 100
506
+ })
427
507
  </script>
428
508
  ```
429
509
 
@@ -439,6 +519,13 @@ Auto focus an element when mounted.
439
519
  <!-- With options -->
440
520
  <input v-focus="{ focus: true, refocus: true }" />
441
521
  </template>
522
+
523
+ <script setup>
524
+ import { useFocus } from 'directix'
525
+
526
+ // Composable usage
527
+ const { focus, blur, hasFocus } = useFocus()
528
+ </script>
442
529
  ```
443
530
 
444
531
  ### v-permission
@@ -465,7 +552,7 @@ Control element visibility based on user permissions.
465
552
  </template>
466
553
 
467
554
  <script setup>
468
- import { configurePermission } from 'directix'
555
+ import { configurePermission, usePermission } from 'directix'
469
556
 
470
557
  configurePermission({
471
558
  getPermissions: () => ['read', 'write'],
@@ -475,6 +562,10 @@ configurePermission({
475
562
  editor: ['read', 'write', 'edit']
476
563
  }
477
564
  })
565
+
566
+ // Composable usage
567
+ const { hasPermission, hasAnyPermission, hasAllPermissions } = usePermission()
568
+ const canEdit = hasPermission('edit')
478
569
  </script>
479
570
  ```
480
571
 
@@ -490,6 +581,16 @@ Lazy load images when they enter the viewport.
490
581
  <!-- With placeholder and error image -->
491
582
  <img v-lazy="{ src: imageUrl, placeholder: '/placeholder.png', error: '/error.png' }" />
492
583
  </template>
584
+
585
+ <script setup>
586
+ import { useLazy } from 'directix'
587
+
588
+ // Composable usage
589
+ const { load, state, loaded } = useLazy({
590
+ src: 'image.jpg',
591
+ preload: 100
592
+ })
593
+ </script>
493
594
  ```
494
595
 
495
596
  ### v-mask
@@ -507,6 +608,16 @@ Input masking for formatted input.
507
608
  <!-- SSN -->
508
609
  <input v-mask="{ mask: '###-##-####', placeholder: '_' }" placeholder="SSN" />
509
610
  </template>
611
+
612
+ <script setup>
613
+ import { useMask } from 'directix'
614
+
615
+ // Composable usage
616
+ const { maskedValue, unmaskedValue, update } = useMask({
617
+ mask: '(###) ###-####',
618
+ value: '1234567890'
619
+ })
620
+ </script>
510
621
  ```
511
622
 
512
623
  ### v-loading
@@ -523,6 +634,19 @@ Show loading overlay on elements.
523
634
  Content with locked scroll
524
635
  </div>
525
636
  </template>
637
+
638
+ <script setup>
639
+ import { ref } from 'vue'
640
+ import { useLoading } from 'directix'
641
+
642
+ const isLoading = ref(true)
643
+
644
+ // Composable usage
645
+ const { show, hide, setText } = useLoading({
646
+ text: 'Loading...',
647
+ lock: true
648
+ })
649
+ </script>
526
650
  ```
527
651
 
528
652
  ### v-sanitize
@@ -537,6 +661,16 @@ Sanitize HTML content to prevent XSS attacks.
537
661
  <!-- With custom allowed tags -->
538
662
  <div v-sanitize="{ html: userContent, allowedTags: ['b', 'i', 'u'] }"></div>
539
663
  </template>
664
+
665
+ <script setup>
666
+ import { useSanitize } from 'directix'
667
+
668
+ // Composable usage
669
+ const { sanitize, setAllowedTags } = useSanitize({
670
+ allowedTags: ['b', 'i', 'u', 'a']
671
+ })
672
+ const cleanHtml = sanitize(dirtyHtml)
673
+ </script>
540
674
  ```
541
675
 
542
676
  ### v-tooltip
@@ -553,6 +687,16 @@ Display tooltips on hover or click.
553
687
  Click me
554
688
  </button>
555
689
  </template>
690
+
691
+ <script setup>
692
+ import { useTooltip } from 'directix'
693
+
694
+ // Composable usage
695
+ const { show, hide, updateContent, updatePosition } = useTooltip({
696
+ content: 'Tooltip content',
697
+ placement: 'top'
698
+ })
699
+ </script>
556
700
  ```
557
701
 
558
702
  ### v-image-preview
@@ -567,6 +711,15 @@ Preview images with zoom and gesture support.
567
711
  <!-- With options -->
568
712
  <img v-image-preview="{ src: 'thumbnail.jpg', previewSrc: 'full.jpg', enablePinchZoom: true }" />
569
713
  </template>
714
+
715
+ <script setup>
716
+ import { useImagePreview } from 'directix'
717
+
718
+ // Composable usage
719
+ const { open, close, zoom, rotate } = useImagePreview({
720
+ enablePinchZoom: true
721
+ })
722
+ </script>
570
723
  ```
571
724
 
572
725
  ### v-draggable
@@ -581,6 +734,16 @@ Make elements draggable.
581
734
  <!-- With constraints -->
582
735
  <div v-draggable="{ axis: 'x', bounds: 'parent' }">Horizontal drag only</div>
583
736
  </template>
737
+
738
+ <script setup>
739
+ import { useDraggable } from 'directix'
740
+
741
+ // Composable usage
742
+ const { position, isDragging, reset } = useDraggable({
743
+ axis: 'x',
744
+ bounds: 'parent'
745
+ })
746
+ </script>
584
747
  ```
585
748
 
586
749
  ### v-uppercase / v-lowercase / v-capitalcase
@@ -593,6 +756,15 @@ Transform text case.
593
756
  <input v-lowercase placeholder="Auto lowercase" />
594
757
  <input v-capitalcase placeholder="Capitalize first letter" />
595
758
  </template>
759
+
760
+ <script setup>
761
+ import { useUppercase, useLowercase, useCapitalcase } from 'directix'
762
+
763
+ // Composable usage
764
+ const { transform: toUppercase } = useUppercase()
765
+ const { transform: toLowercase } = useLowercase()
766
+ const { transform: toCapitalcase } = useCapitalcase()
767
+ </script>
596
768
  ```
597
769
 
598
770
  ### v-truncate
@@ -607,6 +779,14 @@ Truncate text with ellipsis.
607
779
  <!-- With options -->
608
780
  <p v-truncate="{ length: 100, suffix: '...', position: 'end' }">Long text...</p>
609
781
  </template>
782
+
783
+ <script setup>
784
+ import { useTruncate } from 'directix'
785
+
786
+ // Composable usage
787
+ const { truncate } = useTruncate({ length: 100, suffix: '...' })
788
+ const shortText = truncate(longText)
789
+ </script>
610
790
  ```
611
791
 
612
792
  ### v-touch
@@ -621,6 +801,8 @@ Detect touch gestures.
621
801
  </template>
622
802
 
623
803
  <script setup>
804
+ import { useTouch } from 'directix'
805
+
624
806
  function handleSwipe(direction) {
625
807
  console.log('Swiped:', direction) // 'left', 'right', 'up', 'down'
626
808
  }
@@ -628,6 +810,11 @@ function handleSwipe(direction) {
628
810
  function handlePinch(scale) {
629
811
  console.log('Pinched:', scale)
630
812
  }
813
+
814
+ // Composable usage
815
+ const { onSwipe, onPinch, onRotate } = useTouch({
816
+ onSwipe: handleSwipe
817
+ })
631
818
  </script>
632
819
  ```
633
820
 
@@ -643,6 +830,13 @@ Trim input whitespace.
643
830
  <!-- Trim on input -->
644
831
  <input v-trim="{ position: 'both', event: 'input' }" />
645
832
  </template>
833
+
834
+ <script setup>
835
+ import { useTrim } from 'directix'
836
+
837
+ // Composable usage
838
+ const { trim, trimLeft, trimRight } = useTrim({ position: 'both' })
839
+ </script>
646
840
  ```
647
841
 
648
842
  ### v-money
@@ -653,6 +847,14 @@ Currency format input.
653
847
  <template>
654
848
  <input v-money="{ prefix: '$', precision: 2 }" placeholder="Enter amount" />
655
849
  </template>
850
+
851
+ <script setup>
852
+ import { useMoney } from 'directix'
853
+
854
+ // Composable usage
855
+ const { format, parse } = useMoney({ prefix: '$', precision: 2 })
856
+ const formatted = format(1234.56) // "$1,234.56"
857
+ </script>
656
858
  ```
657
859
 
658
860
  ### v-number
@@ -663,6 +865,13 @@ Number format input.
663
865
  <template>
664
866
  <input v-number="{ precision: 2, min: 0, max: 100 }" placeholder="Enter number" />
665
867
  </template>
868
+
869
+ <script setup>
870
+ import { useNumber } from 'directix'
871
+
872
+ // Composable usage
873
+ const { format, parse } = useNumber({ precision: 2, min: 0, max: 100 })
874
+ </script>
666
875
  ```
667
876
 
668
877
  ### v-click-delay
@@ -679,9 +888,17 @@ Delay click execution to prevent double clicks.
679
888
  </template>
680
889
 
681
890
  <script setup>
891
+ import { useClickDelay } from 'directix'
892
+
682
893
  function handleClick() {
683
894
  console.log('Clicked (delayed)')
684
895
  }
896
+
897
+ // Composable usage
898
+ const { run: delayedClick, cancel } = useClickDelay({
899
+ handler: handleClick,
900
+ delay: 300
901
+ })
685
902
  </script>
686
903
  ```
687
904
 
@@ -702,6 +919,8 @@ Countdown timer display.
702
919
  </template>
703
920
 
704
921
  <script setup>
922
+ import { useCountdown } from 'directix'
923
+
705
924
  function handleTick(remaining) {
706
925
  console.log('Remaining:', remaining)
707
926
  }
@@ -709,6 +928,13 @@ function handleTick(remaining) {
709
928
  function handleComplete() {
710
929
  console.log('Countdown complete!')
711
930
  }
931
+
932
+ // Composable usage
933
+ const { start, pause, reset, remaining } = useCountdown({
934
+ time: 60,
935
+ onTick: handleTick,
936
+ onComplete: handleComplete
937
+ })
712
938
  </script>
713
939
  ```
714
940
 
@@ -724,6 +950,13 @@ Text ellipsis overflow with tooltip.
724
950
  <!-- With custom lines -->
725
951
  <div v-ellipsis="{ lines: 2 }">Multi-line text with ellipsis</div>
726
952
  </template>
953
+
954
+ <script setup>
955
+ import { useEllipsis } from 'directix'
956
+
957
+ // Composable usage
958
+ const { isEllipsisActive, checkEllipsis } = useEllipsis({ lines: 1 })
959
+ </script>
727
960
  ```
728
961
 
729
962
  ### v-hotkey
@@ -742,6 +975,8 @@ Keyboard shortcut binding.
742
975
  </template>
743
976
 
744
977
  <script setup>
978
+ import { useHotkey } from 'directix'
979
+
745
980
  function handleSave() {
746
981
  console.log('Saving...')
747
982
  }
@@ -749,6 +984,11 @@ function handleSave() {
749
984
  function handleCopy() {
750
985
  console.log('Copying...')
751
986
  }
987
+
988
+ // Composable usage
989
+ const { bind, unbind, unbindAll } = useHotkey({
990
+ 'ctrl+s': handleSave
991
+ })
752
992
  </script>
753
993
  ```
754
994
 
@@ -765,6 +1005,19 @@ Print element content.
765
1005
  <!-- Print self -->
766
1006
  <div v-print="{ self: true }">Click to print this content</div>
767
1007
  </template>
1008
+
1009
+ <script setup>
1010
+ import { ref } from 'vue'
1011
+ import { usePrint } from 'directix'
1012
+
1013
+ const printRef = ref()
1014
+
1015
+ // Composable usage
1016
+ const { print, printElement } = usePrint({
1017
+ onBefore: () => console.log('Printing...'),
1018
+ onComplete: () => console.log('Printed!')
1019
+ })
1020
+ </script>
768
1021
  ```
769
1022
 
770
1023
  ### v-pull-refresh
@@ -784,10 +1037,17 @@ Pull to refresh functionality.
784
1037
  </template>
785
1038
 
786
1039
  <script setup>
1040
+ import { usePullRefresh } from 'directix'
1041
+
787
1042
  async function handleRefresh() {
788
1043
  // Fetch new data
789
1044
  await fetchData()
790
1045
  }
1046
+
1047
+ // Composable usage
1048
+ const { isLoading, disable, enable } = usePullRefresh({
1049
+ handler: handleRefresh
1050
+ })
791
1051
  </script>
792
1052
  ```
793
1053
 
@@ -808,9 +1068,17 @@ Swipe gesture detection with mouse support.
808
1068
  </template>
809
1069
 
810
1070
  <script setup>
1071
+ import { useSwipe } from 'directix'
1072
+
811
1073
  function handleSwipe(direction) {
812
1074
  console.log('Swiped:', direction) // 'left', 'right', 'up', 'down'
813
1075
  }
1076
+
1077
+ // Composable usage
1078
+ const { direction, lengthX, lengthY } = useSwipe({
1079
+ onSwipe: handleSwipe,
1080
+ threshold: 50
1081
+ })
814
1082
  </script>
815
1083
  ```
816
1084
 
@@ -829,6 +1097,14 @@ Virtual list for rendering large datasets efficiently.
829
1097
 
830
1098
  <script setup>
831
1099
  const list = Array.from({ length: 10000 }, (_, i) => ({ id: i, name: `Item ${i}` }))
1100
+
1101
+ import { useVirtualList } from 'directix'
1102
+
1103
+ // Composable usage
1104
+ const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
1105
+ largeList,
1106
+ { itemHeight: 50 }
1107
+ )
832
1108
  </script>
833
1109
  ```
834
1110
 
@@ -848,6 +1124,575 @@ Watermark overlay.
848
1124
  Content with watermark
849
1125
  </div>
850
1126
  </template>
1127
+
1128
+ <script setup>
1129
+ import { useWatermark } from 'directix'
1130
+
1131
+ // Composable usage
1132
+ const { show, hide, update } = useWatermark({
1133
+ content: 'Confidential',
1134
+ fontSize: 16,
1135
+ color: '#ccc'
1136
+ })
1137
+ </script>
1138
+ ```
1139
+
1140
+ ### v-blur
1141
+
1142
+ Background blur overlay effect.
1143
+
1144
+ ```vue
1145
+ <template>
1146
+ <!-- Simple blur -->
1147
+ <div v-blur="isBlurred">Content behind blur</div>
1148
+
1149
+ <!-- With radius -->
1150
+ <div v-blur="15">Blur with 15px radius</div>
1151
+
1152
+ <!-- With options -->
1153
+ <div v-blur="{
1154
+ visible: isBlurred,
1155
+ radius: 20,
1156
+ overlayColor: 'rgba(255, 255, 255, 0.3)',
1157
+ lockScroll: true
1158
+ }">
1159
+ Content
1160
+ </div>
1161
+ </template>
1162
+
1163
+ <script setup>
1164
+ import { ref } from 'vue'
1165
+ import { useBlur } from 'directix'
1166
+
1167
+ const isBlurred = ref(false)
1168
+
1169
+ // Composable usage
1170
+ const { show, hide, toggle } = useBlur({
1171
+ radius: 10,
1172
+ overlayColor: 'rgba(0, 0, 0, 0.5)'
1173
+ })
1174
+ </script>
1175
+ ```
1176
+
1177
+ ### v-fade
1178
+
1179
+ Fade in/out transition effect.
1180
+
1181
+ ```vue
1182
+ <template>
1183
+ <!-- Toggle visibility with fade -->
1184
+ <div v-fade="isVisible">Fade content</div>
1185
+
1186
+ <!-- Fade in only -->
1187
+ <div v-fade="'in'">Fade in</div>
1188
+
1189
+ <!-- With options -->
1190
+ <div v-fade="{
1191
+ visible: isVisible,
1192
+ duration: 500,
1193
+ easing: 'ease-in-out',
1194
+ onComplete: () => console.log('Fade complete')
1195
+ }">
1196
+ Content
1197
+ </div>
1198
+ </template>
1199
+
1200
+ <script setup>
1201
+ import { ref } from 'vue'
1202
+ import { useFade } from 'directix'
1203
+
1204
+ const isVisible = ref(true)
1205
+
1206
+ // Composable usage
1207
+ const { fadeIn, fadeOut, toggle } = useFade({
1208
+ duration: 300,
1209
+ easing: 'ease'
1210
+ })
1211
+ </script>
1212
+ ```
1213
+
1214
+ ### v-parallax
1215
+
1216
+ Parallax scrolling effect.
1217
+
1218
+ ```vue
1219
+ <template>
1220
+ <!-- Simple parallax -->
1221
+ <div v-parallax>Parallax content</div>
1222
+
1223
+ <!-- With speed factor -->
1224
+ <div v-parallax="0.3">Slower parallax</div>
1225
+
1226
+ <!-- With options -->
1227
+ <div v-parallax="{
1228
+ speed: 0.5,
1229
+ reverse: true,
1230
+ mobileBreakpoint: 768
1231
+ }">
1232
+ Reverse parallax, disabled on mobile
1233
+ </div>
1234
+ </template>
1235
+
1236
+ <script setup>
1237
+ import { useParallax } from 'directix'
1238
+
1239
+ // Composable usage
1240
+ const { offset, progress, enabled } = useParallax({
1241
+ speed: 0.5,
1242
+ reverse: false
1243
+ })
1244
+ </script>
1245
+ ```
1246
+
1247
+ ### v-lottie
1248
+
1249
+ Lottie animation player.
1250
+
1251
+ ```vue
1252
+ <template>
1253
+ <!-- With URL -->
1254
+ <div v-lottie="'https://assets.example.com/animation.json'"></div>
1255
+
1256
+ <!-- With animation data -->
1257
+ <div v-lottie="animationData"></div>
1258
+
1259
+ <!-- With options -->
1260
+ <div v-lottie="{
1261
+ animationData: animationData,
1262
+ autoplay: true,
1263
+ loop: true,
1264
+ speed: 1.5,
1265
+ onComplete: () => console.log('Done')
1266
+ }"></div>
1267
+ </template>
1268
+
1269
+ <script setup>
1270
+ import animationData from './animation.json'
1271
+ import { useLottie } from 'directix'
1272
+
1273
+ // Composable usage
1274
+ const { play, pause, stop, setSpeed, setDirection } = useLottie({
1275
+ animationData,
1276
+ autoplay: true,
1277
+ loop: true
1278
+ })
1279
+ </script>
1280
+ ```
1281
+
1282
+ ### v-typewriter
1283
+
1284
+ Typewriter animation effect.
1285
+
1286
+ ```vue
1287
+ <template>
1288
+ <!-- Simple usage -->
1289
+ <span v-typewriter="'Hello, World!'"></span>
1290
+
1291
+ <!-- With options -->
1292
+ <span v-typewriter="{
1293
+ text: 'Typing animation',
1294
+ speed: 100,
1295
+ cursor: '_',
1296
+ onComplete: () => console.log('Done!')
1297
+ }"></span>
1298
+
1299
+ <!-- Loop mode -->
1300
+ <span v-typewriter="{
1301
+ text: 'Loop animation',
1302
+ loop: true,
1303
+ deleteDelay: 1000
1304
+ }"></span>
1305
+ </template>
1306
+
1307
+ <script setup>
1308
+ import { useTypewriter } from 'directix'
1309
+
1310
+ // Composable usage
1311
+ const { start, stop, pause, resume } = useTypewriter({
1312
+ text: 'Hello World',
1313
+ speed: 50,
1314
+ loop: false
1315
+ })
1316
+ </script>
1317
+ ```
1318
+
1319
+ ### v-export
1320
+
1321
+ Export data (CSV/JSON/HTML/TXT).
1322
+
1323
+ ```vue
1324
+ <template>
1325
+ <button v-export="exportData">Export CSV</button>
1326
+
1327
+ <button v-export="{ data: tableData, format: 'json', filename: 'my-data' }">
1328
+ Export JSON
1329
+ </button>
1330
+
1331
+ <button v-export="{
1332
+ data: tableData,
1333
+ format: 'csv',
1334
+ columns: ['name', 'email'],
1335
+ headers: { name: 'Name', email: 'Email Address' }
1336
+ }">
1337
+ Export with custom columns
1338
+ </button>
1339
+ </template>
1340
+
1341
+ <script setup>
1342
+ const tableData = [
1343
+ { name: 'John', email: 'john@example.com', age: 30 },
1344
+ { name: 'Jane', email: 'jane@example.com', age: 25 }
1345
+ ]
1346
+
1347
+ import { useExport } from 'directix'
1348
+
1349
+ // Composable usage
1350
+ const { exportCSV, exportJSON, exportHTML } = useExport()
1351
+ </script>
1352
+ ```
1353
+
1354
+ ### v-highlight
1355
+
1356
+ Keyword highlighting.
1357
+
1358
+ ```vue
1359
+ <template>
1360
+ <p v-highlight="'important'">This is an important message.</p>
1361
+
1362
+ <p v-highlight="['Vue', 'React']">Vue and React are popular frameworks.</p>
1363
+
1364
+ <p v-highlight="{
1365
+ keywords: 'highlight',
1366
+ className: 'my-highlight',
1367
+ style: 'background: yellow; color: black;',
1368
+ caseSensitive: true
1369
+ }">
1370
+ This will highlight the word.
1371
+ </p>
1372
+ </template>
1373
+
1374
+ <script setup>
1375
+ import { useHighlight } from 'directix'
1376
+
1377
+ // Composable usage
1378
+ const { highlight, clear } = useHighlight({
1379
+ keywords: ['important', 'key'],
1380
+ className: 'highlight'
1381
+ })
1382
+ </script>
1383
+ ```
1384
+
1385
+ ### v-emoji
1386
+
1387
+ Emoji input filter.
1388
+
1389
+ ```vue
1390
+ <template>
1391
+ <!-- Strip all emojis -->
1392
+ <input v-emoji type="text" />
1393
+
1394
+ <!-- Strip emojis with replacement -->
1395
+ <input v-emoji="{ strip: true, replacement: '*' }" type="text" />
1396
+
1397
+ <!-- Allow specific emojis -->
1398
+ <input v-emoji="{ allowList: ['😊', '👍'] }" type="text" />
1399
+
1400
+ <!-- Block specific emojis -->
1401
+ <input v-emoji="{ blockList: ['🚫', '❌'] }" type="text" />
1402
+ </template>
1403
+
1404
+ <script setup>
1405
+ import { useEmoji } from 'directix'
1406
+
1407
+ // Composable usage
1408
+ const { stripEmojis, containsEmoji } = useEmoji({
1409
+ strip: true,
1410
+ allowList: ['😊', '👍']
1411
+ })
1412
+ </script>
1413
+ ```
1414
+
1415
+ ### v-context-menu
1416
+
1417
+ Right-click context menu.
1418
+
1419
+ ```vue
1420
+ <template>
1421
+ <div v-context-menu="menuItems">Right click here</div>
1422
+ <div v-context-menu="{ items: menuItems, width: 200 }">Custom width</div>
1423
+ </template>
1424
+
1425
+ <script setup>
1426
+ import { useContextMenu } from 'directix'
1427
+
1428
+ const menuItems = [
1429
+ { label: 'Copy', handler: () => console.log('Copy') },
1430
+ { label: 'Paste', handler: () => console.log('Paste') },
1431
+ { divider: true, label: '' },
1432
+ { label: 'Delete', handler: () => console.log('Delete') }
1433
+ ]
1434
+
1435
+ // Composable usage
1436
+ const { show, hide, setItems } = useContextMenu({
1437
+ items: menuItems
1438
+ })
1439
+ </script>
1440
+ ```
1441
+
1442
+ ### v-fullscreen
1443
+
1444
+ Fullscreen toggle.
1445
+
1446
+ ```vue
1447
+ <template>
1448
+ <div v-fullscreen>
1449
+ Content to show in fullscreen
1450
+ <button @click="$el.toggleFullscreen()">Toggle</button>
1451
+ </div>
1452
+
1453
+ <div v-fullscreen="{ fullscreenClass: 'my-fullscreen' }">
1454
+ Custom fullscreen class
1455
+ </div>
1456
+ </template>
1457
+
1458
+ <script setup>
1459
+ import { useFullscreen } from 'directix'
1460
+
1461
+ // Composable usage
1462
+ const { isFullscreen, enter, exit, toggle } = useFullscreen({
1463
+ onEnter: () => console.log('Entered fullscreen'),
1464
+ onExit: () => console.log('Exited fullscreen')
1465
+ })
1466
+ </script>
1467
+ ```
1468
+
1469
+ ### v-skeleton
1470
+
1471
+ Skeleton loading placeholder.
1472
+
1473
+ ```vue
1474
+ <template>
1475
+ <!-- Basic usage -->
1476
+ <div v-skeleton="isLoading">Content here</div>
1477
+
1478
+ <!-- With options -->
1479
+ <div v-skeleton="{ loading: isLoading, animation: 'pulse', width: 200, height: 20 }">
1480
+ Content here
1481
+ </div>
1482
+ </template>
1483
+
1484
+ <script setup>
1485
+ import { ref } from 'vue'
1486
+ import { useSkeleton } from 'directix'
1487
+
1488
+ const isLoading = ref(true)
1489
+
1490
+ // Composable usage
1491
+ const { show, hide, update } = useSkeleton({
1492
+ animation: 'wave',
1493
+ color: '#e8e8e8'
1494
+ })
1495
+ </script>
1496
+ ```
1497
+
1498
+ ### v-progress
1499
+
1500
+ Progress bar animation.
1501
+
1502
+ ```vue
1503
+ <template>
1504
+ <div v-progress="50">Progress at 50%</div>
1505
+
1506
+ <div v-progress="{
1507
+ value: progressValue,
1508
+ color: '#42b883',
1509
+ height: 8,
1510
+ showText: true
1511
+ }">
1512
+ Content
1513
+ </div>
1514
+
1515
+ <div v-progress="{ indeterminate: true }">
1516
+ Loading...
1517
+ </div>
1518
+ </template>
1519
+
1520
+ <script setup>
1521
+ import { ref } from 'vue'
1522
+ import { useProgress } from 'directix'
1523
+
1524
+ const progressValue = ref(50)
1525
+
1526
+ // Composable usage
1527
+ const { setProgress, start, finish, fail } = useProgress({
1528
+ color: '#42b883',
1529
+ height: 4
1530
+ })
1531
+ </script>
1532
+ ```
1533
+
1534
+ ### v-counter
1535
+
1536
+ Animated number counter.
1537
+
1538
+ ```vue
1539
+ <template>
1540
+ <span v-counter="1000">0</span>
1541
+
1542
+ <span v-counter="{
1543
+ value: 10000,
1544
+ duration: 3000,
1545
+ decimals: 2,
1546
+ useGrouping: true
1547
+ }">0</span>
1548
+
1549
+ <span v-counter="{
1550
+ value: targetValue,
1551
+ formatter: (v) => '$' + v.toFixed(2)
1552
+ }">0</span>
1553
+ </template>
1554
+
1555
+ <script setup>
1556
+ import { ref } from 'vue'
1557
+ import { useCounter } from 'directix'
1558
+
1559
+ const targetValue = ref(1000)
1560
+
1561
+ // Composable usage
1562
+ const { start, pause, reset, update } = useCounter({
1563
+ startValue: 0,
1564
+ endValue: 1000,
1565
+ duration: 2000,
1566
+ formatter: (v) => `$${v.toFixed(2)}`
1567
+ })
1568
+ </script>
1569
+ ```
1570
+
1571
+ ### v-click-wave
1572
+
1573
+ Click wave effect.
1574
+
1575
+ ```vue
1576
+ <template>
1577
+ <button v-click-wave>Click me</button>
1578
+ <button v-click-wave="'rgba(255, 255, 255, 0.3)'">Custom color</button>
1579
+ <button v-click-wave="{ color: 'red', duration: 400 }">Custom options</button>
1580
+ </template>
1581
+
1582
+ <script setup>
1583
+ import { ref } from 'vue'
1584
+ import { useClickWave } from 'directix'
1585
+
1586
+ // Composable usage
1587
+ const buttonRef = ref(null)
1588
+ const { bind, trigger } = useClickWave({
1589
+ color: 'currentColor',
1590
+ duration: 500
1591
+ })
1592
+
1593
+ // Bind to element on mount
1594
+ onMounted(() => bind(buttonRef.value))
1595
+ </script>
1596
+ ```
1597
+
1598
+ ### v-pan
1599
+
1600
+ Pan/drag gesture.
1601
+
1602
+ ```vue
1603
+ <template>
1604
+ <div v-pan="handlePan">Swipe me</div>
1605
+
1606
+ <div v-pan="{
1607
+ onPan: handlePan,
1608
+ direction: 'horizontal',
1609
+ threshold: 20
1610
+ }">
1611
+ Horizontal only
1612
+ </div>
1613
+ </template>
1614
+
1615
+ <script setup>
1616
+ import { usePan } from 'directix'
1617
+
1618
+ function handlePan(e) {
1619
+ console.log('Direction:', e.direction)
1620
+ console.log('Distance:', e.distance)
1621
+ }
1622
+
1623
+ // Composable usage
1624
+ const { isPanning, deltaX, deltaY } = usePan({
1625
+ onPan: handlePan,
1626
+ direction: 'horizontal'
1627
+ })
1628
+ </script>
1629
+ ```
1630
+
1631
+ ### v-pinch
1632
+
1633
+ Pinch/zoom gesture.
1634
+
1635
+ ```vue
1636
+ <template>
1637
+ <div v-pinch="handlePinch">Pinch to zoom</div>
1638
+
1639
+ <div v-pinch="{
1640
+ onPinch: handlePinch,
1641
+ enableTransform: true,
1642
+ minScale: 0.5,
1643
+ maxScale: 3
1644
+ }">
1645
+ Pinch to scale
1646
+ </div>
1647
+ </template>
1648
+
1649
+ <script setup>
1650
+ import { usePinch } from 'directix'
1651
+
1652
+ function handlePinch(e) {
1653
+ console.log('Scale:', e.scale)
1654
+ console.log('Center:', e.centerX, e.centerY)
1655
+ }
1656
+
1657
+ // Composable usage
1658
+ const { scale, isPinching } = usePinch({
1659
+ onPinch: handlePinch,
1660
+ minScale: 0.5,
1661
+ maxScale: 3
1662
+ })
1663
+ </script>
1664
+ ```
1665
+
1666
+ ### v-rotate-gesture
1667
+
1668
+ Rotation gesture.
1669
+
1670
+ ```vue
1671
+ <template>
1672
+ <div v-rotate-gesture="handleRotate">Rotate with two fingers</div>
1673
+
1674
+ <div v-rotate-gesture="{
1675
+ onRotate: handleRotate,
1676
+ enableTransform: true
1677
+ }">
1678
+ Rotate with transform
1679
+ </div>
1680
+ </template>
1681
+
1682
+ <script setup>
1683
+ import { useRotateGesture } from 'directix'
1684
+
1685
+ function handleRotate(e) {
1686
+ console.log('Rotation:', e.rotation)
1687
+ console.log('Angle:', e.angle)
1688
+ }
1689
+
1690
+ // Composable usage
1691
+ const { angle, isRotating } = useRotateGesture({
1692
+ onRotate: handleRotate,
1693
+ enableTransform: true
1694
+ })
1695
+ </script>
851
1696
  ```
852
1697
 
853
1698
  ## API Reference