@paraxe/vue 1.0.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.
Files changed (107) hide show
  1. package/dist/.loba-build.json +5 -0
  2. package/dist/composites/Accordion.d.ts +22 -0
  3. package/dist/composites/AccordionItem.d.ts +21 -0
  4. package/dist/composites/ButtonGroup.d.ts +21 -0
  5. package/dist/composites/SectionHeader.d.ts +20 -0
  6. package/dist/data/EmptyState.d.ts +22 -0
  7. package/dist/data/List.d.ts +35 -0
  8. package/dist/data/ListItem.d.ts +19 -0
  9. package/dist/data/Pagination.d.ts +17 -0
  10. package/dist/data/Table.d.ts +51 -0
  11. package/dist/elements/Badge.d.ts +19 -0
  12. package/dist/elements/Button.d.ts +33 -0
  13. package/dist/elements/Card.d.ts +21 -0
  14. package/dist/elements/Image.d.ts +13 -0
  15. package/dist/elements/Tag.d.ts +13 -0
  16. package/dist/feedback/Alert.d.ts +27 -0
  17. package/dist/feedback/Progress.d.ts +29 -0
  18. package/dist/feedback/Skeleton.d.ts +13 -0
  19. package/dist/feedback/Toast.d.ts +33 -0
  20. package/dist/forms/Checkbox.d.ts +27 -0
  21. package/dist/forms/Choice.d.ts +19 -0
  22. package/dist/forms/ChoiceGroup.d.ts +32 -0
  23. package/dist/forms/FormField.d.ts +29 -0
  24. package/dist/forms/Input.d.ts +29 -0
  25. package/dist/forms/Radio.d.ts +26 -0
  26. package/dist/forms/Select.d.ts +28 -0
  27. package/dist/forms/Switch.d.ts +24 -0
  28. package/dist/forms/Textarea.d.ts +24 -0
  29. package/dist/index.d.ts +47 -0
  30. package/dist/index.js +1888 -0
  31. package/dist/navigation/Breadcrumb.d.ts +22 -0
  32. package/dist/navigation/Breadcrumbs.d.ts +18 -0
  33. package/dist/navigation/Sidebar.d.ts +22 -0
  34. package/dist/navigation/SidebarGroup.d.ts +18 -0
  35. package/dist/navigation/SidebarItem.d.ts +28 -0
  36. package/dist/navigation/Tab.d.ts +19 -0
  37. package/dist/navigation/Tabs.d.ts +22 -0
  38. package/dist/overlays/Dialog.d.ts +26 -0
  39. package/dist/overlays/Drawer.d.ts +28 -0
  40. package/dist/overlays/Popover.d.ts +20 -0
  41. package/dist/overlays/Tooltip.d.ts +21 -0
  42. package/dist/primitives/Container.d.ts +19 -0
  43. package/dist/primitives/Divider.d.ts +3 -0
  44. package/dist/primitives/Grid.d.ts +25 -0
  45. package/dist/primitives/Section.d.ts +13 -0
  46. package/dist/primitives/Stack.d.ts +21 -0
  47. package/dist/typography/Eyebrow.d.ts +13 -0
  48. package/dist/typography/Heading.d.ts +19 -0
  49. package/dist/typography/Label.d.ts +24 -0
  50. package/dist/typography/Text.d.ts +22 -0
  51. package/doc.md +527 -0
  52. package/package.json +29 -0
  53. package/src/composites/Accordion.vue +60 -0
  54. package/src/composites/AccordionItem.vue +66 -0
  55. package/src/composites/ButtonGroup.vue +27 -0
  56. package/src/composites/SectionHeader.vue +23 -0
  57. package/src/data/EmptyState.vue +40 -0
  58. package/src/data/List.vue +125 -0
  59. package/src/data/ListItem.vue +97 -0
  60. package/src/data/Pagination.vue +254 -0
  61. package/src/data/Table.vue +382 -0
  62. package/src/elements/Avatar.vue +0 -0
  63. package/src/elements/Badge.vue +30 -0
  64. package/src/elements/Button.vue +56 -0
  65. package/src/elements/Card.vue +34 -0
  66. package/src/elements/Icon.vue +0 -0
  67. package/src/elements/Image.vue +36 -0
  68. package/src/elements/Tag.vue +10 -0
  69. package/src/env.d.ts +8 -0
  70. package/src/feedback/Alert.vue +63 -0
  71. package/src/feedback/Progress.vue +95 -0
  72. package/src/feedback/Skeleton.vue +43 -0
  73. package/src/feedback/Toast.vue +141 -0
  74. package/src/forms/Checkbox.vue +54 -0
  75. package/src/forms/Choice.vue +82 -0
  76. package/src/forms/ChoiceGroup.vue +130 -0
  77. package/src/forms/FormField.vue +55 -0
  78. package/src/forms/Input.vue +54 -0
  79. package/src/forms/Radio.vue +64 -0
  80. package/src/forms/Select.vue +43 -0
  81. package/src/forms/Switch.vue +57 -0
  82. package/src/forms/Textarea.vue +54 -0
  83. package/src/index.ts +47 -0
  84. package/src/navigation/Breadcrumb.vue +59 -0
  85. package/src/navigation/Breadcrumbs.vue +30 -0
  86. package/src/navigation/Menu.vue +0 -0
  87. package/src/navigation/Sidebar.vue +35 -0
  88. package/src/navigation/SidebarGroup.vue +34 -0
  89. package/src/navigation/SidebarItem.vue +137 -0
  90. package/src/navigation/Tab.vue +78 -0
  91. package/src/navigation/Tabs.vue +68 -0
  92. package/src/overlays/Dialog.vue +193 -0
  93. package/src/overlays/Drawer.vue +200 -0
  94. package/src/overlays/Popover.vue +106 -0
  95. package/src/overlays/Tooltip.vue +69 -0
  96. package/src/primitives/Container.vue +24 -0
  97. package/src/primitives/Divider.vue +6 -0
  98. package/src/primitives/Grid.vue +36 -0
  99. package/src/primitives/Section.vue +8 -0
  100. package/src/primitives/Stack.vue +28 -0
  101. package/src/typography/Eyebrow.vue +9 -0
  102. package/src/typography/Heading.vue +27 -0
  103. package/src/typography/Label.vue +34 -0
  104. package/src/typography/Text.vue +38 -0
  105. package/tsconfig.json +17 -0
  106. package/tsconfig.tsbuildinfo +1 -0
  107. package/vite.config.ts +30 -0
@@ -0,0 +1,193 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ nextTick,
4
+ onBeforeUnmount,
5
+ onMounted,
6
+ ref,
7
+ watch,
8
+ } from "vue";
9
+
10
+ interface Props {
11
+ modelValue?: boolean;
12
+ closeOnBackdrop?: boolean;
13
+ closeOnEscape?: boolean;
14
+ }
15
+
16
+ const props = withDefaults(
17
+ defineProps<Props>(),
18
+ {
19
+ modelValue: false,
20
+ closeOnBackdrop: true,
21
+ closeOnEscape: true,
22
+ },
23
+ );
24
+
25
+ const emit = defineEmits<{
26
+ "update:modelValue": [
27
+ value: boolean,
28
+ ];
29
+ }>();
30
+
31
+ const dialogElement =
32
+ ref<HTMLElement | null>(null);
33
+
34
+ let previouslyFocused:
35
+ | HTMLElement
36
+ | null = null;
37
+
38
+ const focusableSelector = [
39
+ "button:not([disabled])",
40
+ "[href]",
41
+ "input:not([disabled])",
42
+ "select:not([disabled])",
43
+ "textarea:not([disabled])",
44
+ "[tabindex]:not([tabindex='-1'])",
45
+ ].join(", ");
46
+
47
+ function close() {
48
+ emit("update:modelValue", false);
49
+ }
50
+
51
+ function handleBackdropClick() {
52
+ if (props.closeOnBackdrop) {
53
+ close();
54
+ }
55
+ }
56
+
57
+ function handleKeydown(
58
+ event: KeyboardEvent,
59
+ ) {
60
+ if (
61
+ !props.modelValue ||
62
+ !dialogElement.value
63
+ ) {
64
+ return;
65
+ }
66
+
67
+ if (
68
+ event.key === "Escape" &&
69
+ props.closeOnEscape
70
+ ) {
71
+ event.preventDefault();
72
+ close();
73
+ return;
74
+ }
75
+
76
+ if (event.key !== "Tab") {
77
+ return;
78
+ }
79
+
80
+ const focusable =
81
+ Array.from(
82
+ dialogElement.value.querySelectorAll<HTMLElement>(
83
+ focusableSelector,
84
+ ),
85
+ );
86
+
87
+ if (focusable.length === 0) {
88
+ event.preventDefault();
89
+ return;
90
+ }
91
+
92
+ const first = focusable[0];
93
+ const last =
94
+ focusable[focusable.length - 1];
95
+
96
+ if (
97
+ event.shiftKey &&
98
+ document.activeElement === first
99
+ ) {
100
+ event.preventDefault();
101
+ last.focus();
102
+ return;
103
+ }
104
+
105
+ if (
106
+ !event.shiftKey &&
107
+ document.activeElement === last
108
+ ) {
109
+ event.preventDefault();
110
+ first.focus();
111
+ }
112
+ }
113
+
114
+ async function openDialog() {
115
+ previouslyFocused =
116
+ document.activeElement instanceof
117
+ HTMLElement
118
+ ? document.activeElement
119
+ : null;
120
+
121
+ await nextTick();
122
+
123
+ const focusable =
124
+ dialogElement.value?.querySelector<HTMLElement>(
125
+ focusableSelector,
126
+ );
127
+
128
+ focusable?.focus();
129
+ }
130
+
131
+ function restoreFocus() {
132
+ previouslyFocused?.focus();
133
+ previouslyFocused = null;
134
+ }
135
+
136
+ watch(
137
+ () => props.modelValue,
138
+ async (open) => {
139
+ if (open) {
140
+ await openDialog();
141
+ } else {
142
+ restoreFocus();
143
+ }
144
+ },
145
+ );
146
+
147
+ onMounted(() => {
148
+ if (props.modelValue) {
149
+ openDialog();
150
+ }
151
+
152
+ document.addEventListener(
153
+ "keydown",
154
+ handleKeydown,
155
+ );
156
+ });
157
+
158
+ onBeforeUnmount(() => {
159
+ document.removeEventListener(
160
+ "keydown",
161
+ handleKeydown,
162
+ );
163
+
164
+ restoreFocus();
165
+ });
166
+ </script>
167
+
168
+ <template>
169
+ <Teleport to="body">
170
+ <div
171
+ v-if="modelValue"
172
+ class="dialog dialog--open"
173
+ role="presentation"
174
+ >
175
+ <button
176
+ class="dialog-backdrop"
177
+ type="button"
178
+ aria-label="Close dialog"
179
+ tabindex="-1"
180
+ @click="handleBackdropClick"
181
+ />
182
+
183
+ <div
184
+ ref="dialogElement"
185
+ class="dialog-content"
186
+ role="dialog"
187
+ aria-modal="true"
188
+ >
189
+ <slot />
190
+ </div>
191
+ </div>
192
+ </Teleport>
193
+ </template>
@@ -0,0 +1,200 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ nextTick,
4
+ onBeforeUnmount,
5
+ onMounted,
6
+ ref,
7
+ watch,
8
+ } from "vue";
9
+
10
+ interface Props {
11
+ modelValue?: boolean;
12
+ side?: "left" | "right";
13
+ closeOnBackdrop?: boolean;
14
+ closeOnEscape?: boolean;
15
+ }
16
+
17
+ const props = withDefaults(
18
+ defineProps<Props>(),
19
+ {
20
+ modelValue: false,
21
+ side: "right",
22
+ closeOnBackdrop: true,
23
+ closeOnEscape: true,
24
+ },
25
+ );
26
+
27
+ const emit = defineEmits<{
28
+ "update:modelValue": [
29
+ value: boolean,
30
+ ];
31
+ }>();
32
+
33
+ const panel = ref<HTMLElement | null>(null);
34
+
35
+ let previouslyFocused:
36
+ | HTMLElement
37
+ | null = null;
38
+
39
+ const focusableSelector = [
40
+ "button:not([disabled])",
41
+ "[href]",
42
+ "input:not([disabled])",
43
+ "select:not([disabled])",
44
+ "textarea:not([disabled])",
45
+ "[tabindex]:not([tabindex='-1'])",
46
+ ].join(", ");
47
+
48
+ function close() {
49
+ emit("update:modelValue", false);
50
+ }
51
+
52
+ function handleBackdropClick() {
53
+ if (props.closeOnBackdrop) {
54
+ close();
55
+ }
56
+ }
57
+
58
+ function handleKeydown(
59
+ event: KeyboardEvent,
60
+ ) {
61
+ if (
62
+ !props.modelValue ||
63
+ !panel.value
64
+ ) {
65
+ return;
66
+ }
67
+
68
+ if (
69
+ event.key === "Escape" &&
70
+ props.closeOnEscape
71
+ ) {
72
+ event.preventDefault();
73
+ close();
74
+ return;
75
+ }
76
+
77
+ if (event.key !== "Tab") {
78
+ return;
79
+ }
80
+
81
+ const focusable = Array.from(
82
+ panel.value.querySelectorAll<HTMLElement>(
83
+ focusableSelector,
84
+ ),
85
+ );
86
+
87
+ if (focusable.length === 0) {
88
+ event.preventDefault();
89
+ return;
90
+ }
91
+
92
+ const first = focusable[0];
93
+ const last =
94
+ focusable[focusable.length - 1];
95
+
96
+ if (
97
+ event.shiftKey &&
98
+ document.activeElement === first
99
+ ) {
100
+ event.preventDefault();
101
+ last.focus();
102
+ return;
103
+ }
104
+
105
+ if (
106
+ !event.shiftKey &&
107
+ document.activeElement === last
108
+ ) {
109
+ event.preventDefault();
110
+ first.focus();
111
+ }
112
+ }
113
+
114
+ async function openDrawer() {
115
+ previouslyFocused =
116
+ document.activeElement instanceof
117
+ HTMLElement
118
+ ? document.activeElement
119
+ : null;
120
+
121
+ await nextTick();
122
+
123
+ const focusable =
124
+ panel.value?.querySelector<HTMLElement>(
125
+ focusableSelector,
126
+ );
127
+
128
+ focusable?.focus();
129
+ }
130
+
131
+ function restoreFocus() {
132
+ previouslyFocused?.focus();
133
+ previouslyFocused = null;
134
+ }
135
+
136
+ watch(
137
+ () => props.modelValue,
138
+ async (open) => {
139
+ if (open) {
140
+ await openDrawer();
141
+ } else {
142
+ restoreFocus();
143
+ }
144
+ },
145
+ );
146
+
147
+ onMounted(() => {
148
+ document.addEventListener(
149
+ "keydown",
150
+ handleKeydown,
151
+ );
152
+
153
+ if (props.modelValue) {
154
+ openDrawer();
155
+ }
156
+ });
157
+
158
+ onBeforeUnmount(() => {
159
+ document.removeEventListener(
160
+ "keydown",
161
+ handleKeydown,
162
+ );
163
+
164
+ restoreFocus();
165
+ });
166
+ </script>
167
+
168
+ <template>
169
+ <Teleport to="body">
170
+ <div
171
+ v-if="modelValue"
172
+ class="drawer"
173
+ :class="{
174
+ 'drawer--open': modelValue,
175
+ 'drawer--left': side === 'left',
176
+ 'drawer--right': side === 'right',
177
+ }"
178
+ role="presentation"
179
+ >
180
+ <button
181
+ class="drawer-backdrop"
182
+ type="button"
183
+ aria-label="Close drawer"
184
+ tabindex="-1"
185
+ @click="handleBackdropClick"
186
+ />
187
+
188
+ <aside
189
+ ref="panel"
190
+ class="drawer-panel"
191
+ role="dialog"
192
+ aria-modal="true"
193
+ >
194
+ <div class="drawer-content">
195
+ <slot />
196
+ </div>
197
+ </aside>
198
+ </div>
199
+ </Teleport>
200
+ </template>
@@ -0,0 +1,106 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ onBeforeUnmount,
4
+ onMounted,
5
+ ref,
6
+ } from "vue";
7
+
8
+ interface Props {
9
+ placement?: "top" | "bottom" | "left" | "right";
10
+ }
11
+
12
+ const props = withDefaults(
13
+ defineProps<Props>(),
14
+ {
15
+ placement: "bottom",
16
+ },
17
+ );
18
+
19
+ const open = ref(false);
20
+
21
+ const popover = ref<HTMLElement | null>(null);
22
+
23
+ function toggle() {
24
+ open.value = !open.value;
25
+ }
26
+
27
+ function close() {
28
+ open.value = false;
29
+ }
30
+
31
+ function handleDocumentClick(
32
+ event: MouseEvent,
33
+ ) {
34
+ if (!popover.value) {
35
+ return;
36
+ }
37
+
38
+ const target = event.target;
39
+
40
+ if (
41
+ target instanceof Node &&
42
+ !popover.value.contains(target)
43
+ ) {
44
+ close();
45
+ }
46
+ }
47
+
48
+ function handleKeydown(
49
+ event: KeyboardEvent,
50
+ ) {
51
+ if (event.key === "Escape") {
52
+ close();
53
+ }
54
+ }
55
+
56
+ onMounted(() => {
57
+ document.addEventListener(
58
+ "click",
59
+ handleDocumentClick,
60
+ );
61
+
62
+ document.addEventListener(
63
+ "keydown",
64
+ handleKeydown,
65
+ );
66
+ });
67
+
68
+ onBeforeUnmount(() => {
69
+ document.removeEventListener(
70
+ "click",
71
+ handleDocumentClick,
72
+ );
73
+
74
+ document.removeEventListener(
75
+ "keydown",
76
+ handleKeydown,
77
+ );
78
+ });
79
+ </script>
80
+
81
+ <template>
82
+ <div
83
+ ref="popover"
84
+ class="popover"
85
+ :class="{
86
+ 'popover--open': open,
87
+ [`popover--${props.placement}`]:
88
+ true,
89
+ }"
90
+ >
91
+ <div
92
+ class="popover-trigger"
93
+ @click="toggle"
94
+ >
95
+ <slot name="trigger" />
96
+ </div>
97
+
98
+ <div
99
+ v-if="open"
100
+ class="popover-content"
101
+ role="dialog"
102
+ >
103
+ <slot />
104
+ </div>
105
+ </div>
106
+ </template>
@@ -0,0 +1,69 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ onBeforeUnmount,
4
+ ref,
5
+ } from "vue";
6
+
7
+ interface Props {
8
+ placement?: "top" | "bottom" | "left" | "right";
9
+ text: string;
10
+ delay?: number;
11
+ }
12
+
13
+ const props = withDefaults(
14
+ defineProps<Props>(),
15
+ {
16
+ placement: "top",
17
+ delay: 300,
18
+ },
19
+ );
20
+
21
+ const visible = ref(false);
22
+
23
+ let timer: ReturnType<
24
+ typeof setTimeout
25
+ > | undefined;
26
+
27
+ function show() {
28
+ clearTimeout(timer);
29
+
30
+ timer = setTimeout(() => {
31
+ visible.value = true;
32
+ }, props.delay);
33
+ }
34
+
35
+ function hide() {
36
+ clearTimeout(timer);
37
+ visible.value = false;
38
+ }
39
+
40
+ onBeforeUnmount(() => {
41
+ clearTimeout(timer);
42
+ });
43
+ </script>
44
+
45
+ <template>
46
+ <span
47
+ class="tooltip"
48
+ :class="{
49
+ 'tooltip--visible': visible,
50
+ [`tooltip--${props.placement}`]:
51
+ true,
52
+ }"
53
+ @mouseenter="show"
54
+ @mouseleave="hide"
55
+ @focusin="show"
56
+ @focusout="hide"
57
+ >
58
+ <span class="tooltip-trigger">
59
+ <slot />
60
+ </span>
61
+
62
+ <span
63
+ class="tooltip-content"
64
+ role="tooltip"
65
+ >
66
+ {{ props.text }}
67
+ </span>
68
+ </span>
69
+ </template>
@@ -0,0 +1,24 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+
4
+ type ContainerSize = "default" | "narrow";
5
+
6
+ interface Props {
7
+ size?: ContainerSize;
8
+ }
9
+
10
+ const props = withDefaults(defineProps<Props>(), {
11
+ size: "default",
12
+ });
13
+
14
+ const containerClasses = computed(() => ({
15
+ container: props.size === "default",
16
+ "container-narrow": props.size === "narrow",
17
+ }));
18
+ </script>
19
+
20
+ <template>
21
+ <div :class="containerClasses">
22
+ <slot />
23
+ </div>
24
+ </template>
@@ -0,0 +1,6 @@
1
+ <script setup lang="ts">
2
+ </script>
3
+
4
+ <template>
5
+ <div class="divider" aria-hidden="true" />
6
+ </template>
@@ -0,0 +1,36 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+
4
+ type GridColumns = 1 | 2 | 3 | 4 | 5 | 6;
5
+ type GridRows = 1 | 2 | 3 | 4 | 5 | 6;
6
+ type GridSize = "small" | "medium" | "large";
7
+
8
+ interface Props {
9
+ columns?: GridColumns;
10
+ rows?: GridRows;
11
+ size?: GridSize;
12
+ }
13
+
14
+ const props = withDefaults(defineProps<Props>(), {
15
+ columns: 1,
16
+ rows: 1,
17
+ size: "medium",
18
+ });
19
+
20
+ const gridClasses = computed(() => [
21
+ props.size === "small"
22
+ ? "grid-sm"
23
+ : props.size === "large"
24
+ ? "grid-lg"
25
+ : "grid",
26
+
27
+ `grid-cols-${props.columns}`,
28
+ `grid-rows-${props.rows}`,
29
+ ]);
30
+ </script>
31
+
32
+ <template>
33
+ <div :class="gridClasses">
34
+ <slot />
35
+ </div>
36
+ </template>
@@ -0,0 +1,8 @@
1
+ <script setup lang="ts">
2
+ </script>
3
+
4
+ <template>
5
+ <section class="section">
6
+ <slot />
7
+ </section>
8
+ </template>
@@ -0,0 +1,28 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+
4
+ type StackSize = "small" | "medium" | "large";
5
+
6
+ interface Props {
7
+ size?: StackSize;
8
+ fullWidth?: boolean;
9
+ }
10
+
11
+ const props = withDefaults(defineProps<Props>(), {
12
+ size: "medium",
13
+ fullWidth: false,
14
+ });
15
+
16
+ const stackClasses = computed(() => ({
17
+ stack: props.size === "medium",
18
+ "stack-sm": props.size === "small",
19
+ "stack-lg": props.size === "large",
20
+ "stack-full": props.fullWidth,
21
+ }));
22
+ </script>
23
+
24
+ <template>
25
+ <div :class="stackClasses">
26
+ <slot />
27
+ </div>
28
+ </template>
@@ -0,0 +1,9 @@
1
+ <script setup lang="ts">
2
+
3
+ </script>
4
+
5
+ <template>
6
+ <span class="eyebrow">
7
+ <slot />
8
+ </span>
9
+ </template>
@@ -0,0 +1,27 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+
4
+ type HeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
5
+
6
+ interface Props {
7
+ level?: HeadingLevel;
8
+ }
9
+
10
+ const props = withDefaults(defineProps<Props>(), {
11
+ level: "h2",
12
+ });
13
+
14
+ const headingClasses = computed(() => ({
15
+ "heading-xl": props.level === "h1",
16
+ "heading-lg": props.level === "h2",
17
+ "heading-md": props.level === "h3",
18
+ "heading-sm": props.level === "h4",
19
+ "heading-xs": props.level === "h5" || props.level === "h6",
20
+ }));
21
+ </script>
22
+
23
+ <template>
24
+ <component :is="level" :class="headingClasses">
25
+ <slot />
26
+ </component>
27
+ </template>