@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.
- package/dist/.loba-build.json +5 -0
- package/dist/composites/Accordion.d.ts +22 -0
- package/dist/composites/AccordionItem.d.ts +21 -0
- package/dist/composites/ButtonGroup.d.ts +21 -0
- package/dist/composites/SectionHeader.d.ts +20 -0
- package/dist/data/EmptyState.d.ts +22 -0
- package/dist/data/List.d.ts +35 -0
- package/dist/data/ListItem.d.ts +19 -0
- package/dist/data/Pagination.d.ts +17 -0
- package/dist/data/Table.d.ts +51 -0
- package/dist/elements/Badge.d.ts +19 -0
- package/dist/elements/Button.d.ts +33 -0
- package/dist/elements/Card.d.ts +21 -0
- package/dist/elements/Image.d.ts +13 -0
- package/dist/elements/Tag.d.ts +13 -0
- package/dist/feedback/Alert.d.ts +27 -0
- package/dist/feedback/Progress.d.ts +29 -0
- package/dist/feedback/Skeleton.d.ts +13 -0
- package/dist/feedback/Toast.d.ts +33 -0
- package/dist/forms/Checkbox.d.ts +27 -0
- package/dist/forms/Choice.d.ts +19 -0
- package/dist/forms/ChoiceGroup.d.ts +32 -0
- package/dist/forms/FormField.d.ts +29 -0
- package/dist/forms/Input.d.ts +29 -0
- package/dist/forms/Radio.d.ts +26 -0
- package/dist/forms/Select.d.ts +28 -0
- package/dist/forms/Switch.d.ts +24 -0
- package/dist/forms/Textarea.d.ts +24 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +1888 -0
- package/dist/navigation/Breadcrumb.d.ts +22 -0
- package/dist/navigation/Breadcrumbs.d.ts +18 -0
- package/dist/navigation/Sidebar.d.ts +22 -0
- package/dist/navigation/SidebarGroup.d.ts +18 -0
- package/dist/navigation/SidebarItem.d.ts +28 -0
- package/dist/navigation/Tab.d.ts +19 -0
- package/dist/navigation/Tabs.d.ts +22 -0
- package/dist/overlays/Dialog.d.ts +26 -0
- package/dist/overlays/Drawer.d.ts +28 -0
- package/dist/overlays/Popover.d.ts +20 -0
- package/dist/overlays/Tooltip.d.ts +21 -0
- package/dist/primitives/Container.d.ts +19 -0
- package/dist/primitives/Divider.d.ts +3 -0
- package/dist/primitives/Grid.d.ts +25 -0
- package/dist/primitives/Section.d.ts +13 -0
- package/dist/primitives/Stack.d.ts +21 -0
- package/dist/typography/Eyebrow.d.ts +13 -0
- package/dist/typography/Heading.d.ts +19 -0
- package/dist/typography/Label.d.ts +24 -0
- package/dist/typography/Text.d.ts +22 -0
- package/doc.md +527 -0
- package/package.json +29 -0
- package/src/composites/Accordion.vue +60 -0
- package/src/composites/AccordionItem.vue +66 -0
- package/src/composites/ButtonGroup.vue +27 -0
- package/src/composites/SectionHeader.vue +23 -0
- package/src/data/EmptyState.vue +40 -0
- package/src/data/List.vue +125 -0
- package/src/data/ListItem.vue +97 -0
- package/src/data/Pagination.vue +254 -0
- package/src/data/Table.vue +382 -0
- package/src/elements/Avatar.vue +0 -0
- package/src/elements/Badge.vue +30 -0
- package/src/elements/Button.vue +56 -0
- package/src/elements/Card.vue +34 -0
- package/src/elements/Icon.vue +0 -0
- package/src/elements/Image.vue +36 -0
- package/src/elements/Tag.vue +10 -0
- package/src/env.d.ts +8 -0
- package/src/feedback/Alert.vue +63 -0
- package/src/feedback/Progress.vue +95 -0
- package/src/feedback/Skeleton.vue +43 -0
- package/src/feedback/Toast.vue +141 -0
- package/src/forms/Checkbox.vue +54 -0
- package/src/forms/Choice.vue +82 -0
- package/src/forms/ChoiceGroup.vue +130 -0
- package/src/forms/FormField.vue +55 -0
- package/src/forms/Input.vue +54 -0
- package/src/forms/Radio.vue +64 -0
- package/src/forms/Select.vue +43 -0
- package/src/forms/Switch.vue +57 -0
- package/src/forms/Textarea.vue +54 -0
- package/src/index.ts +47 -0
- package/src/navigation/Breadcrumb.vue +59 -0
- package/src/navigation/Breadcrumbs.vue +30 -0
- package/src/navigation/Menu.vue +0 -0
- package/src/navigation/Sidebar.vue +35 -0
- package/src/navigation/SidebarGroup.vue +34 -0
- package/src/navigation/SidebarItem.vue +137 -0
- package/src/navigation/Tab.vue +78 -0
- package/src/navigation/Tabs.vue +68 -0
- package/src/overlays/Dialog.vue +193 -0
- package/src/overlays/Drawer.vue +200 -0
- package/src/overlays/Popover.vue +106 -0
- package/src/overlays/Tooltip.vue +69 -0
- package/src/primitives/Container.vue +24 -0
- package/src/primitives/Divider.vue +6 -0
- package/src/primitives/Grid.vue +36 -0
- package/src/primitives/Section.vue +8 -0
- package/src/primitives/Stack.vue +28 -0
- package/src/typography/Eyebrow.vue +9 -0
- package/src/typography/Heading.vue +27 -0
- package/src/typography/Label.vue +34 -0
- package/src/typography/Text.vue +38 -0
- package/tsconfig.json +17 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vite.config.ts +30 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
value?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
readonly?: boolean;
|
|
8
|
+
error?: boolean;
|
|
9
|
+
success?: boolean;
|
|
10
|
+
maxlength?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
14
|
+
value: "",
|
|
15
|
+
disabled: false,
|
|
16
|
+
readonly: false,
|
|
17
|
+
error: false,
|
|
18
|
+
success: false,
|
|
19
|
+
maxlength: undefined,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const emit = defineEmits<{
|
|
23
|
+
input: [value: string];
|
|
24
|
+
change: [value: string];
|
|
25
|
+
}>();
|
|
26
|
+
|
|
27
|
+
const textareaClasses = computed(() => ({
|
|
28
|
+
"input-error": props.error,
|
|
29
|
+
"input-success": props.success,
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
function handleInput(event: Event) {
|
|
33
|
+
const target = event.target as HTMLTextAreaElement;
|
|
34
|
+
emit("input", target.value);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function handleChange(event: Event) {
|
|
38
|
+
const target = event.target as HTMLTextAreaElement;
|
|
39
|
+
emit("change", target.value);
|
|
40
|
+
}
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<template>
|
|
44
|
+
<textarea
|
|
45
|
+
class="textarea"
|
|
46
|
+
:class="textareaClasses"
|
|
47
|
+
:value="props.value"
|
|
48
|
+
:disabled="props.disabled"
|
|
49
|
+
:readonly="props.readonly"
|
|
50
|
+
:maxlength="props.maxlength"
|
|
51
|
+
@input="handleInput"
|
|
52
|
+
@change="handleChange"
|
|
53
|
+
/>
|
|
54
|
+
</template>
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export { default as Button } from "./elements/Button.vue";
|
|
2
|
+
export { default as Card } from "./elements/Card.vue";
|
|
3
|
+
export { default as Input } from "./forms/Input.vue";
|
|
4
|
+
export { default as Section } from "./primitives/Section.vue"
|
|
5
|
+
export { default as Stack } from "./primitives/Stack.vue";
|
|
6
|
+
export { default as Container } from "./primitives/Container.vue"
|
|
7
|
+
export { default as Grid } from "./primitives/Grid.vue"
|
|
8
|
+
export { default as Heading } from "./typography/Heading.vue"
|
|
9
|
+
export { default as Text } from "./typography/Text.vue"
|
|
10
|
+
export { default as Eyebrow } from "./typography/Eyebrow.vue"
|
|
11
|
+
export { default as SectionHeader } from "./composites/SectionHeader.vue"
|
|
12
|
+
export { default as ButtonGroup } from "./composites/ButtonGroup.vue";
|
|
13
|
+
export { default as Divider } from "./primitives/Divider.vue";
|
|
14
|
+
export { default as Label } from "./typography/Label.vue";
|
|
15
|
+
export { default as Image } from "./elements/Image.vue";
|
|
16
|
+
export { default as FormField } from "./forms/FormField.vue";
|
|
17
|
+
export { default as Textarea } from "./forms/Textarea.vue";
|
|
18
|
+
export { default as Select } from "./forms/Select.vue";
|
|
19
|
+
export { default as Accordion } from "./composites/Accordion.vue";
|
|
20
|
+
export { default as AccordionItem } from "./composites/AccordionItem.vue";
|
|
21
|
+
export { default as Tag } from "./elements/Tag.vue";
|
|
22
|
+
export { default as Badge } from "./elements/Badge.vue";
|
|
23
|
+
export { default as EmptyState } from "./data/EmptyState.vue";
|
|
24
|
+
export { default as List } from "./data/List.vue";
|
|
25
|
+
export { default as ListItem } from "./data/ListItem.vue";
|
|
26
|
+
export { default as Pagination } from "./data/Pagination.vue";
|
|
27
|
+
export { default as Table } from "./data/Table.vue";
|
|
28
|
+
export { default as Checkbox } from "./forms/Checkbox.vue";
|
|
29
|
+
export { default as Radio } from "./forms/Radio.vue";
|
|
30
|
+
export { default as Switch } from "./forms/Switch.vue";
|
|
31
|
+
export { default as ChoiceGroup } from "./forms/ChoiceGroup.vue";
|
|
32
|
+
export { default as Choice } from "./forms/Choice.vue";
|
|
33
|
+
export { default as Sidebar } from "./navigation/Sidebar.vue"
|
|
34
|
+
export { default as SidebarGroup } from "./navigation/SidebarGroup.vue"
|
|
35
|
+
export { default as SidebarItem } from "./navigation/SidebarItem.vue"
|
|
36
|
+
export { default as Tabs } from "./navigation/Tabs.vue";
|
|
37
|
+
export { default as Tab } from "./navigation/Tab.vue";
|
|
38
|
+
export { default as Breadcrumb } from "./navigation/Breadcrumb.vue"
|
|
39
|
+
export { default as Breadcrumbs } from "./navigation/Breadcrumbs.vue"
|
|
40
|
+
export { default as Dialog } from "./overlays/Dialog.vue"
|
|
41
|
+
export { default as Drawer } from "./overlays/Drawer.vue"
|
|
42
|
+
export { default as Popover } from "./overlays/Popover.vue"
|
|
43
|
+
export { default as Tooltip } from "./overlays/Tooltip.vue"
|
|
44
|
+
export { default as Alert} from "./feedback/Alert.vue"
|
|
45
|
+
export { default as Progress } from "./feedback/Progress.vue"
|
|
46
|
+
export { default as Skeleton } from "./feedback/Skeleton.vue"
|
|
47
|
+
export { default as Toast } from "./feedback/Toast.vue"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
computed,
|
|
4
|
+
inject,
|
|
5
|
+
} from "vue";
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
href?: string;
|
|
9
|
+
current?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const props = withDefaults(
|
|
13
|
+
defineProps<Props>(),
|
|
14
|
+
{
|
|
15
|
+
href: undefined,
|
|
16
|
+
current: false,
|
|
17
|
+
},
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const separator = inject(
|
|
21
|
+
"loba-breadcrumbs-separator",
|
|
22
|
+
"/",
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const classes = computed(() => ({
|
|
26
|
+
"breadcrumbs-item": true,
|
|
27
|
+
}));
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<template>
|
|
31
|
+
<li :class="classes">
|
|
32
|
+
<a
|
|
33
|
+
v-if="props.href && !props.current"
|
|
34
|
+
class="breadcrumbs-link"
|
|
35
|
+
:href="props.href"
|
|
36
|
+
>
|
|
37
|
+
<slot />
|
|
38
|
+
</a>
|
|
39
|
+
|
|
40
|
+
<span
|
|
41
|
+
v-else
|
|
42
|
+
class="breadcrumbs-current"
|
|
43
|
+
:aria-current="
|
|
44
|
+
props.current
|
|
45
|
+
? 'page'
|
|
46
|
+
: undefined
|
|
47
|
+
"
|
|
48
|
+
>
|
|
49
|
+
<slot />
|
|
50
|
+
</span>
|
|
51
|
+
|
|
52
|
+
<span
|
|
53
|
+
class="breadcrumbs-separator"
|
|
54
|
+
aria-hidden="true"
|
|
55
|
+
>
|
|
56
|
+
{{ separator }}
|
|
57
|
+
</span>
|
|
58
|
+
</li>
|
|
59
|
+
</template>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { provide } from "vue";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
separator?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const props = withDefaults(
|
|
9
|
+
defineProps<Props>(),
|
|
10
|
+
{
|
|
11
|
+
separator: "/",
|
|
12
|
+
},
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
provide(
|
|
16
|
+
"loba-breadcrumbs-separator",
|
|
17
|
+
props.separator,
|
|
18
|
+
);
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<nav
|
|
23
|
+
class="breadcrumbs"
|
|
24
|
+
aria-label="Breadcrumb"
|
|
25
|
+
>
|
|
26
|
+
<ol class="breadcrumbs-list">
|
|
27
|
+
<slot />
|
|
28
|
+
</ol>
|
|
29
|
+
</nav>
|
|
30
|
+
</template>
|
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
|
|
4
|
+
export interface SidebarProps {
|
|
5
|
+
collapsed?: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const props = withDefaults(
|
|
9
|
+
defineProps<SidebarProps>(),
|
|
10
|
+
{
|
|
11
|
+
collapsed: false,
|
|
12
|
+
},
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const sidebarClasses = computed(() => ({
|
|
16
|
+
sidebar: true,
|
|
17
|
+
"sidebar--collapsed": props.collapsed,
|
|
18
|
+
}));
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<aside :class="sidebarClasses">
|
|
23
|
+
<div class="sidebar-header">
|
|
24
|
+
<slot name="header" />
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<nav class="sidebar-navigation">
|
|
28
|
+
<slot />
|
|
29
|
+
</nav>
|
|
30
|
+
|
|
31
|
+
<div class="sidebar-footer">
|
|
32
|
+
<slot name="footer" />
|
|
33
|
+
</div>
|
|
34
|
+
</aside>
|
|
35
|
+
</template>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
|
|
4
|
+
export interface SidebarGroupProps {
|
|
5
|
+
label?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const props = withDefaults(
|
|
9
|
+
defineProps<SidebarGroupProps>(),
|
|
10
|
+
{
|
|
11
|
+
label: "",
|
|
12
|
+
},
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const groupClasses = computed(() => ({
|
|
16
|
+
"sidebar-group": true,
|
|
17
|
+
"sidebar-group--labeled": Boolean(props.label),
|
|
18
|
+
}));
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<div :class="groupClasses">
|
|
23
|
+
<div
|
|
24
|
+
v-if="props.label"
|
|
25
|
+
class="sidebar-group-label"
|
|
26
|
+
>
|
|
27
|
+
{{ props.label }}
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="sidebar-group-items">
|
|
31
|
+
<slot />
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</template>
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
computed,
|
|
4
|
+
ref,
|
|
5
|
+
useSlots,
|
|
6
|
+
} from "vue";
|
|
7
|
+
|
|
8
|
+
export interface SidebarItemProps {
|
|
9
|
+
active?: boolean;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
href?: string;
|
|
12
|
+
expanded?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const props = withDefaults(
|
|
16
|
+
defineProps<SidebarItemProps>(),
|
|
17
|
+
{
|
|
18
|
+
active: false,
|
|
19
|
+
disabled: false,
|
|
20
|
+
href: undefined,
|
|
21
|
+
expanded: false,
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const slots = useSlots();
|
|
26
|
+
|
|
27
|
+
const isExpanded = ref(
|
|
28
|
+
props.expanded,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const hasChildren = computed(
|
|
32
|
+
() => Boolean(slots.children),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const itemClasses = computed(() => ({
|
|
36
|
+
"sidebar-item": true,
|
|
37
|
+
"sidebar-item--active":
|
|
38
|
+
props.active,
|
|
39
|
+
"sidebar-item--disabled":
|
|
40
|
+
props.disabled,
|
|
41
|
+
"sidebar-item--expandable":
|
|
42
|
+
hasChildren.value,
|
|
43
|
+
"sidebar-item--expanded":
|
|
44
|
+
hasChildren.value &&
|
|
45
|
+
isExpanded.value,
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
function handleClick(event: MouseEvent) {
|
|
49
|
+
if (props.disabled) {
|
|
50
|
+
event.preventDefault();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (hasChildren.value) {
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
isExpanded.value =
|
|
57
|
+
!isExpanded.value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<template>
|
|
63
|
+
<div class="sidebar-item-wrapper">
|
|
64
|
+
<component
|
|
65
|
+
:is="hasChildren ? 'button' : 'a'"
|
|
66
|
+
:href="
|
|
67
|
+
hasChildren || !props.href
|
|
68
|
+
? undefined
|
|
69
|
+
: props.href
|
|
70
|
+
"
|
|
71
|
+
:type="
|
|
72
|
+
hasChildren
|
|
73
|
+
? 'button'
|
|
74
|
+
: undefined
|
|
75
|
+
"
|
|
76
|
+
:class="itemClasses"
|
|
77
|
+
:disabled="
|
|
78
|
+
hasChildren
|
|
79
|
+
? props.disabled
|
|
80
|
+
: undefined
|
|
81
|
+
"
|
|
82
|
+
:aria-current="
|
|
83
|
+
props.active
|
|
84
|
+
? 'page'
|
|
85
|
+
: undefined
|
|
86
|
+
"
|
|
87
|
+
:aria-expanded="
|
|
88
|
+
hasChildren
|
|
89
|
+
? isExpanded
|
|
90
|
+
: undefined
|
|
91
|
+
"
|
|
92
|
+
:aria-disabled="
|
|
93
|
+
props.disabled
|
|
94
|
+
? 'true'
|
|
95
|
+
: undefined
|
|
96
|
+
"
|
|
97
|
+
@click="handleClick"
|
|
98
|
+
>
|
|
99
|
+
<span
|
|
100
|
+
v-if="$slots.icon"
|
|
101
|
+
class="sidebar-item-icon"
|
|
102
|
+
aria-hidden="true"
|
|
103
|
+
>
|
|
104
|
+
<slot name="icon" />
|
|
105
|
+
</span>
|
|
106
|
+
|
|
107
|
+
<span class="sidebar-item-label">
|
|
108
|
+
<slot />
|
|
109
|
+
</span>
|
|
110
|
+
|
|
111
|
+
<span
|
|
112
|
+
v-if="hasChildren"
|
|
113
|
+
class="sidebar-item-chevron"
|
|
114
|
+
aria-hidden="true"
|
|
115
|
+
>
|
|
116
|
+
<span
|
|
117
|
+
:class="{
|
|
118
|
+
'sidebar-item-chevron--expanded':
|
|
119
|
+
isExpanded,
|
|
120
|
+
}"
|
|
121
|
+
>
|
|
122
|
+
›
|
|
123
|
+
</span>
|
|
124
|
+
</span>
|
|
125
|
+
</component>
|
|
126
|
+
|
|
127
|
+
<div
|
|
128
|
+
v-if="
|
|
129
|
+
hasChildren &&
|
|
130
|
+
isExpanded
|
|
131
|
+
"
|
|
132
|
+
class="sidebar-item-children"
|
|
133
|
+
>
|
|
134
|
+
<slot name="children" />
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
</template>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
computed,
|
|
4
|
+
inject,
|
|
5
|
+
} from "vue";
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
value: string | number;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const props = withDefaults(
|
|
13
|
+
defineProps<Props>(),
|
|
14
|
+
{
|
|
15
|
+
disabled: false,
|
|
16
|
+
},
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
interface TabsContext {
|
|
20
|
+
activeValue: () =>
|
|
21
|
+
| string
|
|
22
|
+
| number
|
|
23
|
+
| null;
|
|
24
|
+
|
|
25
|
+
selectTab: (
|
|
26
|
+
value: string | number,
|
|
27
|
+
) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const injectedTabs =
|
|
31
|
+
inject<TabsContext>("loba-tabs");
|
|
32
|
+
|
|
33
|
+
if (!injectedTabs) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
"Tab must be used inside Tabs.",
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const tabs = injectedTabs;
|
|
40
|
+
|
|
41
|
+
const active = computed(
|
|
42
|
+
() =>
|
|
43
|
+
tabs.activeValue() ===
|
|
44
|
+
props.value,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const tabClasses = computed(() => ({
|
|
48
|
+
tab: true,
|
|
49
|
+
"tab--active": active.value,
|
|
50
|
+
"tab--disabled": props.disabled,
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
function handleClick() {
|
|
54
|
+
if (props.disabled) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
tabs.selectTab(props.value);
|
|
59
|
+
}
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<template>
|
|
63
|
+
<button
|
|
64
|
+
type="button"
|
|
65
|
+
:class="tabClasses"
|
|
66
|
+
role="tab"
|
|
67
|
+
:aria-selected="active"
|
|
68
|
+
:aria-disabled="
|
|
69
|
+
props.disabled || undefined
|
|
70
|
+
"
|
|
71
|
+
:tabindex="
|
|
72
|
+
active ? 0 : -1
|
|
73
|
+
"
|
|
74
|
+
@click="handleClick"
|
|
75
|
+
>
|
|
76
|
+
<slot />
|
|
77
|
+
</button>
|
|
78
|
+
</template>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
provide,
|
|
4
|
+
ref,
|
|
5
|
+
watch,
|
|
6
|
+
} from "vue";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
modelValue?: string | number | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface TabsContext {
|
|
13
|
+
activeValue: () => string | number | null;
|
|
14
|
+
selectTab: (value: string | number) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const props = withDefaults(
|
|
18
|
+
defineProps<Props>(),
|
|
19
|
+
{
|
|
20
|
+
modelValue: null,
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const emit = defineEmits<{
|
|
25
|
+
"update:modelValue": [
|
|
26
|
+
value: string | number,
|
|
27
|
+
];
|
|
28
|
+
}>();
|
|
29
|
+
|
|
30
|
+
const activeValue = ref<
|
|
31
|
+
string | number | null
|
|
32
|
+
>(props.modelValue);
|
|
33
|
+
|
|
34
|
+
watch(
|
|
35
|
+
() => props.modelValue,
|
|
36
|
+
(value) => {
|
|
37
|
+
activeValue.value = value;
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
function selectTab(value: string | number) {
|
|
42
|
+
activeValue.value = value;
|
|
43
|
+
|
|
44
|
+
emit(
|
|
45
|
+
"update:modelValue",
|
|
46
|
+
value,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
provide<TabsContext>(
|
|
51
|
+
"loba-tabs",
|
|
52
|
+
{
|
|
53
|
+
activeValue: () => activeValue.value,
|
|
54
|
+
selectTab,
|
|
55
|
+
},
|
|
56
|
+
);
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<template>
|
|
60
|
+
<div class="tabs">
|
|
61
|
+
<div
|
|
62
|
+
class="tabs-list"
|
|
63
|
+
role="tablist"
|
|
64
|
+
>
|
|
65
|
+
<slot />
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</template>
|