@baklavue/ui 1.0.0-preview.2
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/.releaserc.json +14 -0
- package/CHANGELOG.md +40 -0
- package/README.md +15 -0
- package/index.ts +1 -0
- package/package.json +45 -0
- package/src/accordion/Accordion.vue +206 -0
- package/src/accordion/accordion.types.ts +109 -0
- package/src/accordion/index.ts +3 -0
- package/src/alert/Alert.vue +199 -0
- package/src/alert/alert.types.ts +58 -0
- package/src/alert/index.ts +2 -0
- package/src/badge/Badge.vue +20 -0
- package/src/badge/badge.types.ts +7 -0
- package/src/badge/index.ts +2 -0
- package/src/button/Button.vue +45 -0
- package/src/button/button.types.ts +30 -0
- package/src/button/index.ts +3 -0
- package/src/checkbox/Checkbox.vue +148 -0
- package/src/checkbox/checkbox.types.ts +108 -0
- package/src/checkbox/index.ts +2 -0
- package/src/datepicker/Datepicker.vue +172 -0
- package/src/datepicker/datepicker.types.ts +39 -0
- package/src/datepicker/index.ts +2 -0
- package/src/dialog/Dialog.vue +178 -0
- package/src/dialog/dialog.types.ts +17 -0
- package/src/dialog/index.ts +2 -0
- package/src/drawer/Drawer.vue +162 -0
- package/src/drawer/drawer.types.ts +17 -0
- package/src/drawer/index.ts +2 -0
- package/src/dropdown/Dropdown.vue +231 -0
- package/src/dropdown/dropdown.types.ts +110 -0
- package/src/dropdown/index.ts +2 -0
- package/src/icon/Icon.vue +102 -0
- package/src/icon/icon.types.ts +25 -0
- package/src/icon/index.ts +2 -0
- package/src/index.ts +37 -0
- package/src/input/Input.vue +148 -0
- package/src/input/index.ts +3 -0
- package/src/input/input.types.ts +156 -0
- package/src/link/Link.vue +133 -0
- package/src/link/index.ts +2 -0
- package/src/link/link.types.ts +42 -0
- package/src/notification/Notification.vue +57 -0
- package/src/notification/index.ts +2 -0
- package/src/notification/notification.types.ts +25 -0
- package/src/pagination/Pagination.vue +137 -0
- package/src/pagination/index.ts +2 -0
- package/src/pagination/pagination.types.ts +61 -0
- package/src/radio/Radio.vue +205 -0
- package/src/radio/index.ts +2 -0
- package/src/radio/radio.types.ts +95 -0
- package/src/select/Select.vue +147 -0
- package/src/select/index.ts +2 -0
- package/src/select/select.types.ts +53 -0
- package/src/spinner/Spinner.vue +49 -0
- package/src/spinner/index.ts +2 -0
- package/src/spinner/spinner.types.ts +11 -0
- package/src/split-button/SplitButton.vue +73 -0
- package/src/split-button/index.ts +2 -0
- package/src/split-button/split-button.types.ts +19 -0
- package/src/stepper/Stepper.vue +100 -0
- package/src/stepper/index.ts +2 -0
- package/src/stepper/stepper.types.ts +29 -0
- package/src/switch/Switch.vue +80 -0
- package/src/switch/index.ts +2 -0
- package/src/switch/switch.types.ts +13 -0
- package/src/tab/Tab.vue +99 -0
- package/src/tab/index.ts +2 -0
- package/src/tab/tab.types.ts +17 -0
- package/src/table/Table.vue +264 -0
- package/src/table/index.ts +7 -0
- package/src/table/table.types.ts +62 -0
- package/src/tag/Tag.vue +83 -0
- package/src/tag/index.ts +2 -0
- package/src/tag/tag.types.ts +24 -0
- package/src/textarea/Textarea.vue +84 -0
- package/src/textarea/index.ts +2 -0
- package/src/textarea/textarea.types.ts +37 -0
- package/src/tooltip/Tooltip.vue +81 -0
- package/src/tooltip/index.ts +3 -0
- package/src/tooltip/tooltip.types.ts +29 -0
- package/src/utils/loadBaklavaResources.ts +24 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Link Component
|
|
4
|
+
*
|
|
5
|
+
* A Vue UI kit component for Baklava's `bl-link` web component for navigation links.
|
|
6
|
+
* Supports inline (text within content) and standalone (button-like) variants with
|
|
7
|
+
* full TypeScript support and HTML anchor attributes (href, target, rel, etc.).
|
|
8
|
+
*
|
|
9
|
+
* @component
|
|
10
|
+
* @example
|
|
11
|
+
* ```vue
|
|
12
|
+
* <!-- Basic usage -->
|
|
13
|
+
* <template>
|
|
14
|
+
* <BvLink href="/about">About</BvLink>
|
|
15
|
+
* </template>
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```vue
|
|
20
|
+
* <!-- Inline variant (default) -->
|
|
21
|
+
* <template>
|
|
22
|
+
* <p>Visit our <BvLink href="/docs">documentation</BvLink> for more info.</p>
|
|
23
|
+
* </template>
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```vue
|
|
28
|
+
* <!-- Standalone variant with size -->
|
|
29
|
+
* <template>
|
|
30
|
+
* <BvLink href="/signup" variant="standalone" size="large">
|
|
31
|
+
* Sign up
|
|
32
|
+
* </BvLink>
|
|
33
|
+
* </template>
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```vue
|
|
38
|
+
* <!-- With icon slot -->
|
|
39
|
+
* <template>
|
|
40
|
+
* <BvLink href="/settings" variant="standalone">
|
|
41
|
+
* <template #icon><BvIcon name="settings" /></template>
|
|
42
|
+
* Settings
|
|
43
|
+
* </BvLink>
|
|
44
|
+
* </template>
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```vue
|
|
49
|
+
* <!-- External link with target -->
|
|
50
|
+
* <template>
|
|
51
|
+
* <BvLink href="https://example.com" target="_blank" rel="noopener noreferrer">
|
|
52
|
+
* External site
|
|
53
|
+
* </BvLink>
|
|
54
|
+
* </template>
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```vue
|
|
59
|
+
* <!-- Disabled state -->
|
|
60
|
+
* <template>
|
|
61
|
+
* <BvLink href="/disabled" :disabled="true">Disabled link</BvLink>
|
|
62
|
+
* </template>
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
import { computed, onMounted } from "vue";
|
|
66
|
+
import { loadBaklavaResources } from "../utils/loadBaklavaResources";
|
|
67
|
+
import type { LinkProps } from "./link.types";
|
|
68
|
+
|
|
69
|
+
const props = withDefaults(defineProps<LinkProps>(), {
|
|
70
|
+
href: undefined,
|
|
71
|
+
target: undefined,
|
|
72
|
+
disabled: undefined,
|
|
73
|
+
variant: undefined,
|
|
74
|
+
size: undefined,
|
|
75
|
+
kind: undefined,
|
|
76
|
+
ariaLabel: undefined,
|
|
77
|
+
rel: undefined,
|
|
78
|
+
hreflang: undefined,
|
|
79
|
+
type: undefined,
|
|
80
|
+
referrerPolicy: undefined,
|
|
81
|
+
download: undefined,
|
|
82
|
+
ping: undefined,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const emit = defineEmits<{
|
|
86
|
+
/** Emitted when the link is clicked. */
|
|
87
|
+
click: [event: CustomEvent];
|
|
88
|
+
}>();
|
|
89
|
+
|
|
90
|
+
/** Props to pass to bl-link. When disabled, omit href so navigation is prevented. */
|
|
91
|
+
const linkBindings = computed(() => {
|
|
92
|
+
const { disabled, href, ...rest } = props;
|
|
93
|
+
return {
|
|
94
|
+
...rest,
|
|
95
|
+
href: disabled ? undefined : href,
|
|
96
|
+
"aria-disabled": disabled ? "true" : undefined,
|
|
97
|
+
tabindex: disabled ? -1 : undefined,
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
/** Prevents navigation and click when disabled. */
|
|
102
|
+
function handleClick(event: CustomEvent) {
|
|
103
|
+
if (props.disabled) {
|
|
104
|
+
event.preventDefault();
|
|
105
|
+
event.stopPropagation();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
emit("click", event);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
onMounted(() => {
|
|
112
|
+
loadBaklavaResources();
|
|
113
|
+
});
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<template>
|
|
117
|
+
<bl-link
|
|
118
|
+
v-bind="linkBindings"
|
|
119
|
+
:class="{ 'bv-link--disabled': disabled }"
|
|
120
|
+
@bl-click="handleClick"
|
|
121
|
+
>
|
|
122
|
+
<slot v-if="$slots['icon']" name="icon" />
|
|
123
|
+
<slot />
|
|
124
|
+
</bl-link>
|
|
125
|
+
</template>
|
|
126
|
+
|
|
127
|
+
<style scoped>
|
|
128
|
+
.bv-link--disabled {
|
|
129
|
+
pointer-events: none;
|
|
130
|
+
opacity: 0.5;
|
|
131
|
+
cursor: not-allowed;
|
|
132
|
+
}
|
|
133
|
+
</style>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Link variant - inline (text within content) or standalone (button-like). */
|
|
2
|
+
export type LinkVariant = "inline" | "standalone";
|
|
3
|
+
|
|
4
|
+
/** Link size - only applies to standalone variant. */
|
|
5
|
+
export type LinkSize = "small" | "medium" | "large";
|
|
6
|
+
|
|
7
|
+
/** Link kind - only applies to standalone variant. */
|
|
8
|
+
export type LinkKind = "primary" | "neutral";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Props for the Link component.
|
|
12
|
+
*
|
|
13
|
+
* @interface LinkProps
|
|
14
|
+
*/
|
|
15
|
+
export interface LinkProps {
|
|
16
|
+
/** URL that the hyperlink points to. */
|
|
17
|
+
href?: string;
|
|
18
|
+
/** Where to display the linked URL (e.g. "_self", "_blank"). */
|
|
19
|
+
target?: string;
|
|
20
|
+
/** Whether the link is disabled. */
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
/** Link variant - inline or standalone. */
|
|
23
|
+
variant?: LinkVariant;
|
|
24
|
+
/** Link size - only applies to standalone variant. */
|
|
25
|
+
size?: LinkSize;
|
|
26
|
+
/** Link kind - only applies to standalone variant. */
|
|
27
|
+
kind?: LinkKind;
|
|
28
|
+
/** Aria label for the link. */
|
|
29
|
+
ariaLabel?: string;
|
|
30
|
+
/** Relationship between the current document and the linked document (e.g. "noopener noreferrer"). */
|
|
31
|
+
rel?: string;
|
|
32
|
+
/** Language of the linked document. */
|
|
33
|
+
hreflang?: string;
|
|
34
|
+
/** MIME type of the linked document. */
|
|
35
|
+
type?: string;
|
|
36
|
+
/** Referrer policy for the link. */
|
|
37
|
+
referrerPolicy?: string;
|
|
38
|
+
/** Whether to download the resource instead of navigating to it. */
|
|
39
|
+
download?: string;
|
|
40
|
+
/** Ping URLs to be notified when following the link. */
|
|
41
|
+
ping?: string;
|
|
42
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Notification Component
|
|
4
|
+
*
|
|
5
|
+
* A Vue UI kit component for Baklava's `bl-notification` web component for toast notifications.
|
|
6
|
+
* Place this component once in your app (typically at root or layout level). Notifications
|
|
7
|
+
* are triggered programmatically via the `useNotification` composable.
|
|
8
|
+
*
|
|
9
|
+
* @component
|
|
10
|
+
* @example
|
|
11
|
+
* ```vue
|
|
12
|
+
* <!-- Basic usage: place in app root -->
|
|
13
|
+
* <template>
|
|
14
|
+
* <div>
|
|
15
|
+
* <RouterView />
|
|
16
|
+
* <BvNotification />
|
|
17
|
+
* </div>
|
|
18
|
+
* </template>
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```vue
|
|
23
|
+
* <!-- With useNotification composable -->
|
|
24
|
+
* <template>
|
|
25
|
+
* <div>
|
|
26
|
+
* <BvButton @click="showSuccess">Show Success</BvButton>
|
|
27
|
+
* <BvNotification />
|
|
28
|
+
* </div>
|
|
29
|
+
* </template>
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
import { onMounted } from "vue";
|
|
33
|
+
import { loadBaklavaResources } from "../utils/loadBaklavaResources";
|
|
34
|
+
import type { NotificationProps } from "./notification.types";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Component props with default values.
|
|
38
|
+
*/
|
|
39
|
+
const props = withDefaults(defineProps<NotificationProps>(), {
|
|
40
|
+
duration: 7,
|
|
41
|
+
noAnimation: false,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Lifecycle hook: Component mounted.
|
|
46
|
+
* Loads Baklava resources required for the web component.
|
|
47
|
+
*/
|
|
48
|
+
onMounted(() => {
|
|
49
|
+
loadBaklavaResources();
|
|
50
|
+
});
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<template>
|
|
54
|
+
<bl-notification v-bind="props" />
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<style lang="css" scoped></style>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Props for the Notification component.
|
|
3
|
+
*
|
|
4
|
+
* The Notification component is a Vue UI kit component for Baklava's `bl-notification` web component.
|
|
5
|
+
* It acts as a container for toast notifications triggered via the `useNotification` composable.
|
|
6
|
+
*
|
|
7
|
+
* @interface NotificationProps
|
|
8
|
+
*/
|
|
9
|
+
export interface NotificationProps {
|
|
10
|
+
/**
|
|
11
|
+
* Default duration of notifications in seconds.
|
|
12
|
+
* Individual notifications can override this via the `useNotification` options.
|
|
13
|
+
*
|
|
14
|
+
* @default 7
|
|
15
|
+
*/
|
|
16
|
+
duration?: number;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Whether to disable animations.
|
|
20
|
+
* Respects user's reduced-motion preferences.
|
|
21
|
+
*
|
|
22
|
+
* @default false
|
|
23
|
+
*/
|
|
24
|
+
noAnimation?: boolean;
|
|
25
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Pagination Component
|
|
4
|
+
*
|
|
5
|
+
* A Vue UI kit component for Baklava's `bl-pagination` web component for page navigation.
|
|
6
|
+
* Supports v-model:currentPage for two-way binding and emits change events when
|
|
7
|
+
* the user navigates to a different page.
|
|
8
|
+
*
|
|
9
|
+
* @component
|
|
10
|
+
* @example
|
|
11
|
+
* ```vue
|
|
12
|
+
* <!-- Basic pagination -->
|
|
13
|
+
* <template>
|
|
14
|
+
* <BvPagination
|
|
15
|
+
* v-model:current-page="currentPage"
|
|
16
|
+
* :total-items="100"
|
|
17
|
+
* :page-size="10"
|
|
18
|
+
* @change="handlePageChange"
|
|
19
|
+
* />
|
|
20
|
+
* </template>
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```vue
|
|
25
|
+
* <!-- With jumper - jump directly to a page -->
|
|
26
|
+
* <template>
|
|
27
|
+
* <BvPagination
|
|
28
|
+
* v-model:current-page="currentPage"
|
|
29
|
+
* :total-items="250"
|
|
30
|
+
* :page-size="25"
|
|
31
|
+
* :has-jumper="true"
|
|
32
|
+
* jumper-label="Go to page"
|
|
33
|
+
* />
|
|
34
|
+
* </template>
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```vue
|
|
39
|
+
* <!-- With items-per-page select -->
|
|
40
|
+
* <template>
|
|
41
|
+
* <BvPagination
|
|
42
|
+
* v-model:current-page="currentPage"
|
|
43
|
+
* :total-items="100"
|
|
44
|
+
* :page-size="10"
|
|
45
|
+
* :has-select="true"
|
|
46
|
+
* select-label="Items per page"
|
|
47
|
+
* :items-per-page-options="[
|
|
48
|
+
* { text: '10 Items', value: 10 },
|
|
49
|
+
* { text: '25 Items', value: 25 },
|
|
50
|
+
* { text: '50 Items', value: 50 }
|
|
51
|
+
* ]"
|
|
52
|
+
* />
|
|
53
|
+
* </template>
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
import { onMounted } from "vue";
|
|
57
|
+
import { loadBaklavaResources } from "../utils/loadBaklavaResources";
|
|
58
|
+
import type { PaginationProps } from "./pagination.types";
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Component props with default values.
|
|
62
|
+
*/
|
|
63
|
+
const props = withDefaults(defineProps<PaginationProps>(), {
|
|
64
|
+
currentPage: undefined,
|
|
65
|
+
totalItems: undefined,
|
|
66
|
+
pageSize: undefined,
|
|
67
|
+
hasJumper: false,
|
|
68
|
+
jumperLabel: undefined,
|
|
69
|
+
hasSelect: false,
|
|
70
|
+
selectLabel: undefined,
|
|
71
|
+
itemsPerPageOptions: undefined,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Component events.
|
|
76
|
+
*/
|
|
77
|
+
const emit = defineEmits<{
|
|
78
|
+
/**
|
|
79
|
+
* Emitted when the current page changes.
|
|
80
|
+
* Use v-model:currentPage for two-way binding.
|
|
81
|
+
*
|
|
82
|
+
* @param {number} page - The new current page number.
|
|
83
|
+
*/
|
|
84
|
+
"update:currentPage": [page: number];
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Emitted when the user navigates to a different page.
|
|
88
|
+
* Payload contains selectedPage, prevPage, and itemsPerPage.
|
|
89
|
+
*
|
|
90
|
+
* @param {CustomEvent<{ selectedPage: number; prevPage: number; itemsPerPage: number }>} event - The change event from bl-pagination.
|
|
91
|
+
*/
|
|
92
|
+
change: [event: CustomEvent];
|
|
93
|
+
}>();
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Handles the bl-change event from the underlying bl-pagination component.
|
|
97
|
+
* Extracts selectedPage from event.detail and emits update:currentPage and change.
|
|
98
|
+
*
|
|
99
|
+
* @param {CustomEvent} event - The bl-change event from bl-pagination.
|
|
100
|
+
*/
|
|
101
|
+
const handleChange = (event: CustomEvent) => {
|
|
102
|
+
emit("change", event);
|
|
103
|
+
const detail = event.detail as
|
|
104
|
+
| { selectedPage?: number; prevPage?: number; itemsPerPage?: number }
|
|
105
|
+
| undefined;
|
|
106
|
+
const page = detail?.selectedPage;
|
|
107
|
+
if (page !== undefined) {
|
|
108
|
+
emit("update:currentPage", page);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Lifecycle hook: Component mounted.
|
|
114
|
+
* Loads Baklava resources required for bl-pagination.
|
|
115
|
+
*/
|
|
116
|
+
onMounted(() => {
|
|
117
|
+
loadBaklavaResources();
|
|
118
|
+
});
|
|
119
|
+
</script>
|
|
120
|
+
|
|
121
|
+
<template>
|
|
122
|
+
<bl-pagination
|
|
123
|
+
v-bind="{
|
|
124
|
+
'current-page': props.currentPage,
|
|
125
|
+
'total-items': props.totalItems,
|
|
126
|
+
'items-per-page': props.pageSize,
|
|
127
|
+
'has-jumper': props.hasJumper === true ? true : undefined,
|
|
128
|
+
'jumper-label': props.jumperLabel,
|
|
129
|
+
'has-select': props.hasSelect === true ? true : undefined,
|
|
130
|
+
'select-label': props.selectLabel,
|
|
131
|
+
'items-per-page-options': props.itemsPerPageOptions,
|
|
132
|
+
}"
|
|
133
|
+
@bl-change="handleChange"
|
|
134
|
+
>
|
|
135
|
+
<slot />
|
|
136
|
+
</bl-pagination>
|
|
137
|
+
</template>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for the items-per-page select element.
|
|
3
|
+
*/
|
|
4
|
+
export interface ItemsPerPageOption {
|
|
5
|
+
/** Display text for the option */
|
|
6
|
+
text: string;
|
|
7
|
+
/** Numeric value for the option */
|
|
8
|
+
value: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Props for the Pagination component.
|
|
13
|
+
* Maps to Baklava's bl-pagination attributes.
|
|
14
|
+
*
|
|
15
|
+
* @see https://github.com/Trendyol/baklava
|
|
16
|
+
*/
|
|
17
|
+
export interface PaginationProps {
|
|
18
|
+
/**
|
|
19
|
+
* Current page number (1-based).
|
|
20
|
+
* Supports v-model:currentPage.
|
|
21
|
+
*/
|
|
22
|
+
currentPage?: number;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Total number of items to be paginated.
|
|
26
|
+
* Baklava uses this to compute the total number of pages.
|
|
27
|
+
*/
|
|
28
|
+
totalItems?: number;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Number of items per page.
|
|
32
|
+
* Maps to Baklava's items-per-page attribute.
|
|
33
|
+
*/
|
|
34
|
+
pageSize?: number;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* When true, adds a jumper input to jump directly to a page.
|
|
38
|
+
*/
|
|
39
|
+
hasJumper?: boolean;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Label for the jumper input.
|
|
43
|
+
*/
|
|
44
|
+
jumperLabel?: string;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* When true, adds a select element to choose items per page.
|
|
48
|
+
*/
|
|
49
|
+
hasSelect?: boolean;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Label for the items-per-page select.
|
|
53
|
+
*/
|
|
54
|
+
selectLabel?: string;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Options for the items-per-page select element.
|
|
58
|
+
* Each option has `text` (display) and `value` (number).
|
|
59
|
+
*/
|
|
60
|
+
itemsPerPageOptions?: ItemsPerPageOption[];
|
|
61
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Radio Component
|
|
4
|
+
*
|
|
5
|
+
* A Vue UI kit component for Baklava's `bl-radio` and `bl-radio-group` web components.
|
|
6
|
+
* Can be used as either a single radio option or as a group container for multiple radios.
|
|
7
|
+
*
|
|
8
|
+
* @component
|
|
9
|
+
* @example
|
|
10
|
+
* ```vue
|
|
11
|
+
* <!-- Single radio in a group -->
|
|
12
|
+
* <template>
|
|
13
|
+
* <bl-radio-group :value="choice" @bl-radio-change="choice = $event.detail">
|
|
14
|
+
* <BvRadio v-model="choice" value="yes" label="Yes" name="choice" />
|
|
15
|
+
* <BvRadio v-model="choice" value="no" label="No" name="choice" />
|
|
16
|
+
* </bl-radio-group>
|
|
17
|
+
* </template>
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```vue
|
|
22
|
+
* <!-- Radio group with items -->
|
|
23
|
+
* <template>
|
|
24
|
+
* <BvRadio v-model="choice" :items="items">
|
|
25
|
+
* <template #item="{ item }">{{ item.label }}</template>
|
|
26
|
+
* </BvRadio>
|
|
27
|
+
* </template>
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```vue
|
|
32
|
+
* <!-- Explicit checked control -->
|
|
33
|
+
* <template>
|
|
34
|
+
* <BvRadio :checked="isSelected" @update:checked="isSelected = $event" label="Option" />
|
|
35
|
+
* </template>
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
import { computed, onMounted } from "vue";
|
|
39
|
+
import { loadBaklavaResources } from "../utils/loadBaklavaResources";
|
|
40
|
+
import type { RadioProps } from "./radio.types";
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Component props with default values.
|
|
44
|
+
*/
|
|
45
|
+
const props = withDefaults(defineProps<RadioProps>(), {
|
|
46
|
+
modelValue: undefined,
|
|
47
|
+
value: undefined,
|
|
48
|
+
name: undefined,
|
|
49
|
+
label: undefined,
|
|
50
|
+
checked: undefined,
|
|
51
|
+
disabled: undefined,
|
|
52
|
+
required: undefined,
|
|
53
|
+
items: undefined,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Component events.
|
|
58
|
+
*/
|
|
59
|
+
const emit = defineEmits<{
|
|
60
|
+
/**
|
|
61
|
+
* Emitted when selection changes (for v-model with modelValue).
|
|
62
|
+
* Payload is this radio's value when it becomes selected (single mode) or the selected value (group mode).
|
|
63
|
+
*
|
|
64
|
+
* @param {string | number} value - The selected value.
|
|
65
|
+
*/
|
|
66
|
+
"update:modelValue": [value: string | number];
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Emitted when checked state changes (for explicit checked control).
|
|
70
|
+
*
|
|
71
|
+
* @param {boolean} checked - The new checked state.
|
|
72
|
+
*/
|
|
73
|
+
"update:checked": [checked: boolean];
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Emitted when the radio state changes (native bl-change / bl-radio-change event).
|
|
77
|
+
*
|
|
78
|
+
* @param {CustomEvent} event - The change event from bl-radio or bl-radio-group.
|
|
79
|
+
*/
|
|
80
|
+
change: [event: CustomEvent];
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Emitted on input (native bl-input event).
|
|
84
|
+
*
|
|
85
|
+
* @param {CustomEvent} event - The bl-input event from bl-radio.
|
|
86
|
+
*/
|
|
87
|
+
input: [event: CustomEvent];
|
|
88
|
+
}>();
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Determines if the component should act as a group container.
|
|
92
|
+
* When `items` prop is provided and is an array, it acts as a group.
|
|
93
|
+
*/
|
|
94
|
+
const isGroupMode = computed(
|
|
95
|
+
() => props.items !== undefined && Array.isArray(props.items),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Normalized model value for group mode (string for bl-radio-group).
|
|
100
|
+
*/
|
|
101
|
+
const groupValue = computed(() => {
|
|
102
|
+
const val = props.modelValue;
|
|
103
|
+
if (val === undefined || val === null) return "";
|
|
104
|
+
return String(val);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Computed checked state for bl-radio (single mode).
|
|
109
|
+
* Uses explicit checked prop when provided, otherwise modelValue === value for v-model.
|
|
110
|
+
*/
|
|
111
|
+
const computedChecked = computed(() => {
|
|
112
|
+
if (props.checked === true || props.checked === false) {
|
|
113
|
+
return props.checked;
|
|
114
|
+
}
|
|
115
|
+
if (props.modelValue !== undefined && props.value !== undefined) {
|
|
116
|
+
return String(props.modelValue) === String(props.value);
|
|
117
|
+
}
|
|
118
|
+
return undefined;
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Handles the bl-change event from the underlying bl-radio (single mode).
|
|
123
|
+
* Emits update:modelValue with this radio's value when selected, or update:checked.
|
|
124
|
+
*/
|
|
125
|
+
const handleSingleChange = (event: CustomEvent) => {
|
|
126
|
+
emit("change", event);
|
|
127
|
+
const target = event.target as { checked?: boolean; value?: string | number };
|
|
128
|
+
const checked = target?.checked;
|
|
129
|
+
if (checked === true && props.value !== undefined) {
|
|
130
|
+
emit("update:modelValue", props.value);
|
|
131
|
+
}
|
|
132
|
+
if (checked === true || checked === false) {
|
|
133
|
+
emit("update:checked", checked);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Handles the bl-radio-change event from the underlying bl-radio-group (group mode).
|
|
139
|
+
* Emits update:modelValue with the selected value (preserves original type from items).
|
|
140
|
+
*/
|
|
141
|
+
const handleGroupChange = (event: CustomEvent) => {
|
|
142
|
+
emit("change", event);
|
|
143
|
+
const detail = event.detail as string | undefined;
|
|
144
|
+
if (detail === undefined || detail === "") return;
|
|
145
|
+
const item = props.items?.find(
|
|
146
|
+
(i) => String(i.value) === String(detail),
|
|
147
|
+
);
|
|
148
|
+
const newValue = item !== undefined ? item.value : detail;
|
|
149
|
+
emit("update:modelValue", newValue);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Lifecycle hook: Component mounted.
|
|
154
|
+
* Loads Baklava resources for bl-radio and bl-radio-group.
|
|
155
|
+
*/
|
|
156
|
+
onMounted(() => {
|
|
157
|
+
loadBaklavaResources();
|
|
158
|
+
});
|
|
159
|
+
</script>
|
|
160
|
+
|
|
161
|
+
<template>
|
|
162
|
+
<!-- Group mode: render as bl-radio-group -->
|
|
163
|
+
<bl-radio-group
|
|
164
|
+
v-if="isGroupMode"
|
|
165
|
+
:value="groupValue"
|
|
166
|
+
:required="props.required === true ? true : undefined"
|
|
167
|
+
:label="props.label"
|
|
168
|
+
@bl-radio-change="handleGroupChange"
|
|
169
|
+
>
|
|
170
|
+
<bl-radio
|
|
171
|
+
v-for="(item, index) in props.items"
|
|
172
|
+
:key="String(item.value)"
|
|
173
|
+
v-bind="{
|
|
174
|
+
value: String(item.value),
|
|
175
|
+
disabled: item.disabled === true ? true : undefined,
|
|
176
|
+
name: item.name ?? props.name,
|
|
177
|
+
}"
|
|
178
|
+
>
|
|
179
|
+
<slot name="item" :item="item" :index="index">
|
|
180
|
+
{{ item.label }}
|
|
181
|
+
</slot>
|
|
182
|
+
</bl-radio>
|
|
183
|
+
</bl-radio-group>
|
|
184
|
+
|
|
185
|
+
<!-- Single radio mode: render as bl-radio -->
|
|
186
|
+
<bl-radio
|
|
187
|
+
v-else
|
|
188
|
+
v-bind="{
|
|
189
|
+
value: props.value !== undefined ? String(props.value) : undefined,
|
|
190
|
+
name: props.name,
|
|
191
|
+
disabled: props.disabled === true ? true : undefined,
|
|
192
|
+
required: props.required === true ? true : undefined,
|
|
193
|
+
checked:
|
|
194
|
+
computedChecked === true
|
|
195
|
+
? true
|
|
196
|
+
: computedChecked === false
|
|
197
|
+
? false
|
|
198
|
+
: undefined,
|
|
199
|
+
}"
|
|
200
|
+
@bl-change="handleSingleChange"
|
|
201
|
+
@bl-input="emit('input', $event)"
|
|
202
|
+
>
|
|
203
|
+
<slot>{{ label }}</slot>
|
|
204
|
+
</bl-radio>
|
|
205
|
+
</template>
|