@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.
Files changed (83) hide show
  1. package/.releaserc.json +14 -0
  2. package/CHANGELOG.md +40 -0
  3. package/README.md +15 -0
  4. package/index.ts +1 -0
  5. package/package.json +45 -0
  6. package/src/accordion/Accordion.vue +206 -0
  7. package/src/accordion/accordion.types.ts +109 -0
  8. package/src/accordion/index.ts +3 -0
  9. package/src/alert/Alert.vue +199 -0
  10. package/src/alert/alert.types.ts +58 -0
  11. package/src/alert/index.ts +2 -0
  12. package/src/badge/Badge.vue +20 -0
  13. package/src/badge/badge.types.ts +7 -0
  14. package/src/badge/index.ts +2 -0
  15. package/src/button/Button.vue +45 -0
  16. package/src/button/button.types.ts +30 -0
  17. package/src/button/index.ts +3 -0
  18. package/src/checkbox/Checkbox.vue +148 -0
  19. package/src/checkbox/checkbox.types.ts +108 -0
  20. package/src/checkbox/index.ts +2 -0
  21. package/src/datepicker/Datepicker.vue +172 -0
  22. package/src/datepicker/datepicker.types.ts +39 -0
  23. package/src/datepicker/index.ts +2 -0
  24. package/src/dialog/Dialog.vue +178 -0
  25. package/src/dialog/dialog.types.ts +17 -0
  26. package/src/dialog/index.ts +2 -0
  27. package/src/drawer/Drawer.vue +162 -0
  28. package/src/drawer/drawer.types.ts +17 -0
  29. package/src/drawer/index.ts +2 -0
  30. package/src/dropdown/Dropdown.vue +231 -0
  31. package/src/dropdown/dropdown.types.ts +110 -0
  32. package/src/dropdown/index.ts +2 -0
  33. package/src/icon/Icon.vue +102 -0
  34. package/src/icon/icon.types.ts +25 -0
  35. package/src/icon/index.ts +2 -0
  36. package/src/index.ts +37 -0
  37. package/src/input/Input.vue +148 -0
  38. package/src/input/index.ts +3 -0
  39. package/src/input/input.types.ts +156 -0
  40. package/src/link/Link.vue +133 -0
  41. package/src/link/index.ts +2 -0
  42. package/src/link/link.types.ts +42 -0
  43. package/src/notification/Notification.vue +57 -0
  44. package/src/notification/index.ts +2 -0
  45. package/src/notification/notification.types.ts +25 -0
  46. package/src/pagination/Pagination.vue +137 -0
  47. package/src/pagination/index.ts +2 -0
  48. package/src/pagination/pagination.types.ts +61 -0
  49. package/src/radio/Radio.vue +205 -0
  50. package/src/radio/index.ts +2 -0
  51. package/src/radio/radio.types.ts +95 -0
  52. package/src/select/Select.vue +147 -0
  53. package/src/select/index.ts +2 -0
  54. package/src/select/select.types.ts +53 -0
  55. package/src/spinner/Spinner.vue +49 -0
  56. package/src/spinner/index.ts +2 -0
  57. package/src/spinner/spinner.types.ts +11 -0
  58. package/src/split-button/SplitButton.vue +73 -0
  59. package/src/split-button/index.ts +2 -0
  60. package/src/split-button/split-button.types.ts +19 -0
  61. package/src/stepper/Stepper.vue +100 -0
  62. package/src/stepper/index.ts +2 -0
  63. package/src/stepper/stepper.types.ts +29 -0
  64. package/src/switch/Switch.vue +80 -0
  65. package/src/switch/index.ts +2 -0
  66. package/src/switch/switch.types.ts +13 -0
  67. package/src/tab/Tab.vue +99 -0
  68. package/src/tab/index.ts +2 -0
  69. package/src/tab/tab.types.ts +17 -0
  70. package/src/table/Table.vue +264 -0
  71. package/src/table/index.ts +7 -0
  72. package/src/table/table.types.ts +62 -0
  73. package/src/tag/Tag.vue +83 -0
  74. package/src/tag/index.ts +2 -0
  75. package/src/tag/tag.types.ts +24 -0
  76. package/src/textarea/Textarea.vue +84 -0
  77. package/src/textarea/index.ts +2 -0
  78. package/src/textarea/textarea.types.ts +37 -0
  79. package/src/tooltip/Tooltip.vue +81 -0
  80. package/src/tooltip/index.ts +3 -0
  81. package/src/tooltip/tooltip.types.ts +29 -0
  82. package/src/utils/loadBaklavaResources.ts +24 -0
  83. package/tsconfig.json +28 -0
@@ -0,0 +1,99 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Tab Component
4
+ *
5
+ * A Vue UI kit component for Baklava's `bl-tabs` (bl-tab-group / bl-tab / bl-tab-panel) web components
6
+ * for tab navigation. Use the `tabs` prop for declarative configuration or the default slot
7
+ * for custom tab content.
8
+ *
9
+ * @component
10
+ * @example
11
+ * ```vue
12
+ * <!-- Basic usage with tabs prop -->
13
+ * <template>
14
+ * <BvTab v-model:activeTab="activeTab" :tabs="tabOptions">
15
+ * <div v-if="activeTab === 'tab1'">Content 1</div>
16
+ * <div v-if="activeTab === 'tab2'">Content 2</div>
17
+ * </BvTab>
18
+ * </template>
19
+ * ```
20
+ *
21
+ * @example
22
+ * ```vue
23
+ * <!-- Custom slots -->
24
+ * <template>
25
+ * <BvTab v-model:activeTab="active">
26
+ * <bl-tab value="a">Tab A</bl-tab>
27
+ * <bl-tab value="b">Tab B</bl-tab>
28
+ * <bl-tab-panel tab="a">Panel A content</bl-tab-panel>
29
+ * <bl-tab-panel tab="b">Panel B content</bl-tab-panel>
30
+ * </BvTab>
31
+ * </template>
32
+ * ```
33
+ */
34
+ import { onMounted } from "vue";
35
+ import { loadBaklavaResources } from "../utils/loadBaklavaResources";
36
+ import type { TabProps } from "./tab.types";
37
+
38
+ const props = withDefaults(defineProps<TabProps>(), {
39
+ activeTab: undefined,
40
+ tabs: undefined,
41
+ variant: undefined,
42
+ orientation: undefined,
43
+ });
44
+
45
+ const emit = defineEmits<{
46
+ /**
47
+ * Emitted when the active tab changes (use with v-model:activeTab).
48
+ * @param {string} tab - The new active tab value.
49
+ */
50
+ "update:activeTab": [tab: string];
51
+ /**
52
+ * Emitted when tab selection changes (raw CustomEvent from bl-tabs).
53
+ * @param {CustomEvent} event - The bl-tab-change event.
54
+ */
55
+ "tab-change": [event: CustomEvent];
56
+ }>();
57
+
58
+ /**
59
+ * Handles the bl-tab-selected event from the underlying bl-tab-group.
60
+ * Emits update:activeTab with the new tab value and tab-change with the raw event.
61
+ *
62
+ * @param {CustomEvent} event - The bl-tab-selected event from bl-tab (detail is tab name).
63
+ */
64
+ const handleTabChange = (event: CustomEvent<string>) => {
65
+ emit("tab-change", event);
66
+ const tab = event.detail;
67
+ if (tab !== undefined) emit("update:activeTab", tab);
68
+ };
69
+
70
+ onMounted(() => {
71
+ loadBaklavaResources();
72
+ });
73
+ </script>
74
+
75
+ <template>
76
+ <bl-tab-group @bl-tab-selected="handleTabChange">
77
+ <template v-if="tabs">
78
+ <bl-tab
79
+ v-for="tab in tabs"
80
+ :key="tab.value"
81
+ slot="tabs"
82
+ :name="tab.value"
83
+ :caption="tab.label"
84
+ :disabled="tab.disabled"
85
+ :selected="props.activeTab === tab.value"
86
+ >
87
+ {{ tab.label }}
88
+ </bl-tab>
89
+ <bl-tab-panel
90
+ v-for="tab in tabs"
91
+ :key="`panel-${tab.value}`"
92
+ :tab="tab.value"
93
+ >
94
+ <slot />
95
+ </bl-tab-panel>
96
+ </template>
97
+ <slot v-else />
98
+ </bl-tab-group>
99
+ </template>
@@ -0,0 +1,2 @@
1
+ export { default as BvTab } from "./Tab.vue";
2
+ export type { TabProps, TabOption } from "./tab.types";
@@ -0,0 +1,17 @@
1
+ /** Tab option for the `tabs` prop */
2
+ export interface TabOption {
3
+ label: string;
4
+ value: string;
5
+ disabled?: boolean;
6
+ }
7
+
8
+ export interface TabProps {
9
+ /** Currently active tab value (use with v-model:activeTab) */
10
+ activeTab?: string;
11
+ /** Array of tab options. When provided, tabs are rendered from this array. */
12
+ tabs?: TabOption[];
13
+ /** Tab variant (passed to bl-tabs) */
14
+ variant?: string;
15
+ /** Tab orientation: horizontal or vertical */
16
+ orientation?: string;
17
+ }
@@ -0,0 +1,264 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Table Component
4
+ *
5
+ * A Vue UI kit component for Baklava's `bl-table` web component for displaying tabular data.
6
+ * Supports columns, data, sorting, row selection, loading/empty states, pagination,
7
+ * and custom slots for header actions, empty state, and per-column cell content.
8
+ *
9
+ * @component
10
+ * @example
11
+ * ```vue
12
+ * <!-- Basic usage -->
13
+ * <template>
14
+ * <BvTable :columns="columns" :data="tableData" />
15
+ * </template>
16
+ * ```
17
+ *
18
+ * @example
19
+ * ```vue
20
+ * <!-- With loading and empty states -->
21
+ * <template>
22
+ * <BvTable
23
+ * :columns="columns"
24
+ * :data="tableData"
25
+ * :is-loading="isLoading"
26
+ * >
27
+ * <template #empty-state>
28
+ * <span>No data found</span>
29
+ * </template>
30
+ * </BvTable>
31
+ * </template>
32
+ * ```
33
+ */
34
+ import { computed, onMounted, type PropType } from "vue";
35
+ import { loadBaklavaResources } from "../utils/loadBaklavaResources";
36
+ import BvSpinner from "../spinner/Spinner.vue";
37
+ import type {
38
+ TableColumn,
39
+ TableRow,
40
+ TablePaginationProps,
41
+ } from "./table.types";
42
+
43
+ defineOptions({ inheritAttrs: false });
44
+
45
+ const props = defineProps({
46
+ title: { type: String, default: undefined },
47
+ headerOptions: {
48
+ type: Object as PropType<{ sticky?: boolean; minCellWidth?: string }>,
49
+ default: undefined,
50
+ },
51
+ data: { type: Array as PropType<TableRow[]>, default: () => [] },
52
+ columns: { type: Array as PropType<TableColumn[]>, default: undefined },
53
+ sortable: { type: Boolean, default: undefined },
54
+ selectable: { type: Boolean, default: undefined },
55
+ multiple: { type: Boolean, default: undefined },
56
+ selected: {
57
+ type: Array as PropType<(string | number)[]>,
58
+ default: undefined,
59
+ },
60
+ sortKey: { type: String, default: undefined },
61
+ sortDirection: { type: String, default: undefined },
62
+ stickyFirstColumn: { type: Boolean, default: undefined },
63
+ stickyLastColumn: { type: Boolean, default: undefined },
64
+ isLoading: { type: Boolean, default: undefined },
65
+ pagination: {
66
+ type: Object as PropType<TablePaginationProps>,
67
+ default: undefined,
68
+ },
69
+ loadingText: { type: String, default: "Loading..." },
70
+ });
71
+
72
+ const emit = defineEmits<{
73
+ /**
74
+ * Emitted when a row is clicked.
75
+ * @param {CustomEvent} event - The bl-row-click event.
76
+ */
77
+ "row-click": [event: CustomEvent];
78
+ /**
79
+ * Emitted when table sort options change.
80
+ * @param {CustomEvent} event - The bl-sort event.
81
+ */
82
+ sort: [event: CustomEvent];
83
+ /**
84
+ * Emitted when selected rows change.
85
+ * @param {CustomEvent} event - The bl-row-select event.
86
+ */
87
+ select: [event: CustomEvent];
88
+ /**
89
+ * Emitted when pagination changes.
90
+ * @param {CustomEvent} event - The bl-change event with selectedPage, prevPage, itemsPerPage.
91
+ */
92
+ change: [event: CustomEvent];
93
+ }>();
94
+
95
+ /**
96
+ * Baklava table expects selected IDs to always be strings,
97
+ * even when the actual data IDs might be numbers.
98
+ */
99
+ const selectedIdsAsStrings = computed(() => {
100
+ const s = props.selected;
101
+ if (s == null || !Array.isArray(s)) return [];
102
+ return s.map((id) => String(id));
103
+ });
104
+
105
+ /** Column header text: prefer name, then label, then key */
106
+ const getColumnLabel = (col: { key: string; label?: string; name?: string }) =>
107
+ col.name ?? col.label ?? col.key;
108
+
109
+ /** Row key for :key and selection-key: prefer row.id, fallback to index */
110
+ const getRowKey = (row: Record<string, unknown> & { id?: string | number }, index: number) =>
111
+ row.id != null ? String(row.id) : String(index);
112
+
113
+ onMounted(() => {
114
+ loadBaklavaResources();
115
+ });
116
+ </script>
117
+
118
+ <template>
119
+ <div class="table">
120
+ <div v-if="props.title" class="header">
121
+ <span class="--title">{{ props.title }}</span>
122
+ <slot name="header-actions" />
123
+ </div>
124
+
125
+ <div class="table-content">
126
+ <!-- Loading state -->
127
+ <bl-table v-if="props.isLoading">
128
+ <div class="loading-state">
129
+ <BvSpinner />
130
+ <span>{{ props.loadingText }}</span>
131
+ </div>
132
+ </bl-table>
133
+
134
+ <!-- Empty state -->
135
+ <bl-table v-else-if="!props.data?.length">
136
+ <div class="empty-state">
137
+ <slot name="empty-state" />
138
+ </div>
139
+ </bl-table>
140
+
141
+ <!-- Data table -->
142
+ <bl-table
143
+ v-else
144
+ v-bind="{
145
+ sortable: props.sortable === true ? true : undefined,
146
+ selectable: props.selectable === true ? true : undefined,
147
+ multiple: props.multiple === true ? true : undefined,
148
+ ...(props.selectable && { selected: selectedIdsAsStrings }),
149
+ sortKey: props.sortKey,
150
+ sortDirection: props.sortDirection,
151
+ stickyFirstColumn: props.stickyFirstColumn,
152
+ stickyLastColumn: props.stickyLastColumn,
153
+ }"
154
+ @bl-sort="emit('sort', $event)"
155
+ @bl-row-select="emit('select', $event)"
156
+ >
157
+ <bl-table-header :sticky="props.headerOptions?.sticky">
158
+ <bl-table-row>
159
+ <bl-table-header-cell
160
+ v-for="column in props.columns"
161
+ :key="column.key"
162
+ :sort-key="
163
+ props.sortable && column.sortable !== false ? column.key : undefined
164
+ "
165
+ :style="{
166
+ '--bl-table-header-cell-min-width':
167
+ props.headerOptions?.minCellWidth || '100px',
168
+ }"
169
+ >
170
+ {{ getColumnLabel(column) }}
171
+ </bl-table-header-cell>
172
+ </bl-table-row>
173
+ </bl-table-header>
174
+
175
+ <bl-table-body>
176
+ <bl-table-row
177
+ v-for="(row, index) in props.data"
178
+ :key="getRowKey(row, index)"
179
+ :selection-key="props.selectable ? getRowKey(row, index) : undefined"
180
+ >
181
+ <bl-table-cell v-for="column in props.columns" :key="column.key">
182
+ <slot
183
+ :name="column.key"
184
+ :row="row"
185
+ :value="row[column.key]"
186
+ >
187
+ {{ row[column.key] }}
188
+ </slot>
189
+ </bl-table-cell>
190
+ </bl-table-row>
191
+ </bl-table-body>
192
+ </bl-table>
193
+ </div>
194
+
195
+ <div v-if="props.pagination" class="pagination-wrapper">
196
+ <bl-pagination
197
+ :current-page="props.pagination!.currentPage"
198
+ :total-items="props.pagination!.totalItems"
199
+ :items-per-page="props.pagination!.itemsPerPage"
200
+ :has-jumper="props.pagination!.hasJumper ?? undefined"
201
+ :has-select="props.pagination!.hasSelect ?? undefined"
202
+ :jumper-label="props.pagination!.jumperLabel ?? 'Go to page'"
203
+ :select-label="props.pagination!.selectLabel ?? 'Items per page'"
204
+ :items-per-page-options="props.pagination!.itemsPerPageOptions"
205
+ @bl-change="emit('change', $event)"
206
+ />
207
+ </div>
208
+ </div>
209
+ </template>
210
+
211
+ <style lang="css" scoped>
212
+ .table {
213
+ display: flex;
214
+ flex-direction: column;
215
+ gap: 24px;
216
+ }
217
+
218
+ .header {
219
+ display: flex;
220
+ align-items: center;
221
+ justify-content: space-between;
222
+ }
223
+
224
+ .--title {
225
+ font: var(--bl-font-title-1-medium);
226
+ }
227
+
228
+ .table-content {
229
+ width: 100%;
230
+ overflow-x: auto;
231
+ }
232
+
233
+ .table-content > bl-table {
234
+ min-width: max-content;
235
+ }
236
+
237
+ .loading-state {
238
+ display: flex;
239
+ flex-direction: column;
240
+ align-items: center;
241
+ justify-content: center;
242
+ padding: 48px;
243
+ gap: 16px;
244
+ }
245
+
246
+ .loading-state span {
247
+ font: var(--bl-font-body-2-regular);
248
+ color: var(--bl-color-primary);
249
+ }
250
+
251
+ .empty-state {
252
+ display: flex;
253
+ flex-direction: column;
254
+ align-items: center;
255
+ justify-content: center;
256
+ padding: 48px;
257
+ gap: 12px;
258
+ }
259
+
260
+ .pagination-wrapper {
261
+ display: flex;
262
+ justify-content: flex-end;
263
+ }
264
+ </style>
@@ -0,0 +1,7 @@
1
+ export { default as BvTable } from "./Table.vue";
2
+ export type {
3
+ TableProps,
4
+ TableColumn,
5
+ TableRow,
6
+ TablePaginationProps,
7
+ } from "./table.types";
@@ -0,0 +1,62 @@
1
+ import type { ItemsPerPageOption } from "../pagination/pagination.types";
2
+
3
+ /** Table column definition */
4
+ export interface TableColumn {
5
+ key: string;
6
+ /** Column header text (alias: use `name` or `label`) */
7
+ label?: string;
8
+ /** Column header text (alias: use `name` or `label`) */
9
+ name?: string;
10
+ sortable?: boolean;
11
+ }
12
+
13
+ /** Table row data (record of column keys to values). When selectable, rows should have `id`. */
14
+ export type TableRow = Record<string, unknown> & { id?: string | number };
15
+
16
+ /** Pagination props for table integration */
17
+ export interface TablePaginationProps {
18
+ currentPage: number;
19
+ totalItems: number;
20
+ itemsPerPage: number;
21
+ hasJumper?: boolean;
22
+ jumperLabel?: string;
23
+ hasSelect?: boolean;
24
+ selectLabel?: string;
25
+ itemsPerPageOptions?: ItemsPerPageOption[];
26
+ }
27
+
28
+ export interface TableProps {
29
+ /** Optional title displayed above the table */
30
+ title?: string;
31
+ /** Header options: sticky, minCellWidth */
32
+ headerOptions?: {
33
+ sticky?: boolean;
34
+ minCellWidth?: string;
35
+ };
36
+ /** Table data rows */
37
+ data?: TableRow[];
38
+ /** Column definitions */
39
+ columns?: TableColumn[];
40
+ /** Enable column sorting */
41
+ sortable?: boolean;
42
+ /** Enable row selection */
43
+ selectable?: boolean;
44
+ /** Enable multiple row selection */
45
+ multiple?: boolean;
46
+ /** Selected row selection keys (use with v-model:selected when selectable). Accepts string or number ids. */
47
+ selected?: (string | number)[];
48
+ /** Sort key for the sorted column */
49
+ sortKey?: string;
50
+ /** Sort direction: '' | 'asc' | 'desc' */
51
+ sortDirection?: string;
52
+ /** Make first column sticky */
53
+ stickyFirstColumn?: boolean;
54
+ /** Make last column sticky */
55
+ stickyLastColumn?: boolean;
56
+ /** Show loading state */
57
+ isLoading?: boolean;
58
+ /** Pagination configuration */
59
+ pagination?: TablePaginationProps;
60
+ /** Text shown in loading state */
61
+ loadingText?: string;
62
+ }
@@ -0,0 +1,83 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Tag Component
4
+ *
5
+ * A Vue UI kit component for Baklava's `bl-tag` web component for displaying tags or labels.
6
+ * Supports selectable and removable (closable) variants, sizes, and icons.
7
+ *
8
+ * @component
9
+ * @example
10
+ * ```vue
11
+ * <!-- Basic usage -->
12
+ * <template>
13
+ * <BvTag>Label</BvTag>
14
+ * </template>
15
+ * ```
16
+ *
17
+ * @example
18
+ * ```vue
19
+ * <!-- Closable tag -->
20
+ * <template>
21
+ * <BvTag closable @close="removeTag">Removable</BvTag>
22
+ * </template>
23
+ * ```
24
+ *
25
+ * @example
26
+ * ```vue
27
+ * <!-- With icon -->
28
+ * <template>
29
+ * <BvTag icon="info">With icon</BvTag>
30
+ * </template>
31
+ * ```
32
+ */
33
+ import { onMounted } from "vue";
34
+ import { loadBaklavaResources } from "../utils/loadBaklavaResources";
35
+ import type { TagProps } from "./tag.types";
36
+
37
+ const props = withDefaults(defineProps<TagProps>(), {
38
+ variant: undefined,
39
+ size: undefined,
40
+ closable: undefined,
41
+ icon: undefined,
42
+ });
43
+
44
+ const emit = defineEmits<{
45
+ /**
46
+ * Emitted when the close button is clicked (removable/closable variant only).
47
+ */
48
+ close: [];
49
+ /**
50
+ * Emitted when a selectable tag is clicked. Payload contains the new selected state.
51
+ */
52
+ "update:selected": [selected: boolean];
53
+ }>();
54
+
55
+ /**
56
+ * Handles bl-tag-click. For removable variant, emit 'close'. For selectable, emit 'update:selected'.
57
+ */
58
+ const handleTagClick = (event: CustomEvent<{ value: string | null; selected: boolean }>) => {
59
+ if (props.closable) {
60
+ emit("close");
61
+ } else if (props.variant === "selectable" && event.detail) {
62
+ emit("update:selected", event.detail.selected);
63
+ }
64
+ };
65
+
66
+ onMounted(() => {
67
+ loadBaklavaResources();
68
+ });
69
+ </script>
70
+
71
+ <template>
72
+ <bl-tag
73
+ v-bind="{
74
+ ...props,
75
+ variant: props.closable ? 'removable' : props.variant,
76
+ closable: undefined,
77
+ }"
78
+ @bl-tag-click="handleTagClick"
79
+ >
80
+ <slot v-if="$slots['icon']" name="icon" />
81
+ <slot />
82
+ </bl-tag>
83
+ </template>
@@ -0,0 +1,2 @@
1
+ export { default as BvTag } from "./Tag.vue";
2
+ export type { TagProps, TagVariant, TagSize } from "./tag.types";
@@ -0,0 +1,24 @@
1
+ import type { BaklavaIcon } from "@trendyol/baklava-icons";
2
+
3
+ /** Tag variant: selectable (default) or removable (closable) */
4
+ export type TagVariant = "selectable" | "removable";
5
+
6
+ /** Tag size */
7
+ export type TagSize = "small" | "medium" | "large";
8
+
9
+ export interface TagProps {
10
+ /** Tag variant: selectable or removable */
11
+ variant?: TagVariant;
12
+ /** Tag size */
13
+ size?: TagSize;
14
+ /** When true, shows close button (uses variant="removable" under the hood) */
15
+ closable?: boolean;
16
+ /** Icon name from Baklava icons */
17
+ icon?: BaklavaIcon;
18
+ /** Selected state (for selectable variant) */
19
+ selected?: boolean;
20
+ /** Disabled state */
21
+ disabled?: boolean;
22
+ /** Value for form/selection */
23
+ value?: string | null;
24
+ }
@@ -0,0 +1,84 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Textarea Component
4
+ *
5
+ * A Vue UI kit component for Baklava's `bl-textarea` web component with v-model support.
6
+ * Supports label, placeholder, validation, character counter, and help text.
7
+ *
8
+ * @component
9
+ * @example
10
+ * ```vue
11
+ * <!-- Basic usage with v-model -->
12
+ * <template>
13
+ * <BvTextarea v-model="message" label="Message" placeholder="Enter your message" />
14
+ * </template>
15
+ * ```
16
+ *
17
+ * @example
18
+ * ```vue
19
+ * <!-- With validation -->
20
+ * <template>
21
+ * <BvTextarea
22
+ * v-model="comment"
23
+ * label="Comment"
24
+ * :maxlength="500"
25
+ * help-text="Max 500 characters"
26
+ * invalid-text="Please enter a valid comment"
27
+ * />
28
+ * </template>
29
+ * ```
30
+ */
31
+ import { onMounted } from "vue";
32
+ import { loadBaklavaResources } from "../utils/loadBaklavaResources";
33
+ import type { TextareaProps } from "./textarea.types";
34
+
35
+ const props = withDefaults(defineProps<TextareaProps>(), {
36
+ modelValue: undefined,
37
+ label: undefined,
38
+ placeholder: undefined,
39
+ rows: undefined,
40
+ maxlength: undefined,
41
+ disabled: undefined,
42
+ required: undefined,
43
+ });
44
+
45
+ const emit = defineEmits<{
46
+ /**
47
+ * Emitted when the value changes (use with v-model).
48
+ * @param {string | null} value - The new textarea value.
49
+ */
50
+ "update:modelValue": [value: string | null];
51
+ /**
52
+ * Emitted on user input (mirrors native bl-input).
53
+ * @param {CustomEvent} event - The bl-input event.
54
+ */
55
+ input: [event: CustomEvent];
56
+ /**
57
+ * Emitted when the value changes (blur or commit).
58
+ * @param {CustomEvent} event - The bl-change event.
59
+ */
60
+ change: [event: CustomEvent];
61
+ }>();
62
+
63
+ onMounted(() => {
64
+ loadBaklavaResources();
65
+ });
66
+ </script>
67
+
68
+ <template>
69
+ <bl-textarea
70
+ v-bind="{
71
+ ...props,
72
+ disabled: props.disabled === true ? true : undefined,
73
+ required: props.required === true ? true : undefined,
74
+ }"
75
+ :value="props.modelValue"
76
+ @bl-input="
77
+ emit('input', $event);
78
+ emit('update:modelValue', ($event.target as HTMLTextAreaElement)?.value || null);
79
+ "
80
+ @bl-change="emit('change', $event)"
81
+ >
82
+ <slot />
83
+ </bl-textarea>
84
+ </template>
@@ -0,0 +1,2 @@
1
+ export { default as BvTextarea } from "./Textarea.vue";
2
+ export type { TextareaProps, TextareaSize } from "./textarea.types";
@@ -0,0 +1,37 @@
1
+ /** Textarea size */
2
+ export type TextareaSize = "small" | "medium" | "large";
3
+
4
+ export interface TextareaProps {
5
+ /** Textarea value (use with v-model) */
6
+ modelValue?: string | null;
7
+ /** Label text */
8
+ label?: string;
9
+ /** Placeholder text */
10
+ placeholder?: string;
11
+ /** Number of visible rows */
12
+ rows?: number;
13
+ /** Maximum character length */
14
+ maxlength?: number;
15
+ /** Minimum character length */
16
+ minlength?: number;
17
+ /** Disabled state */
18
+ disabled?: boolean;
19
+ /** Required field */
20
+ required?: boolean;
21
+ /** Readonly state */
22
+ readonly?: boolean;
23
+ /** Help text below the textarea */
24
+ helpText?: string;
25
+ /** Custom error/invalid message */
26
+ invalidText?: string;
27
+ /** Enable character counter */
28
+ characterCounter?: boolean;
29
+ /** Enable auto-expand up to maxRows */
30
+ expand?: boolean;
31
+ /** Max rows when expand is true */
32
+ maxRows?: number;
33
+ /** Textarea size */
34
+ size?: TextareaSize;
35
+ /** Name attribute for forms */
36
+ name?: string;
37
+ }