Skip to content

Commit 7ce21d6

Browse files
feat: introducing custom row styles and better type inference for smart table (#35)
1 parent b366c3d commit 7ce21d6

File tree

3 files changed

+122
-25
lines changed

3 files changed

+122
-25
lines changed

src/components/smart/Input.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const props = withDefaults(
4949
defineProps<{
5050
id: string
5151
styles: string
52-
modelValue: string | null
52+
modelValue: string | null | undefined
5353
placeholder: string
5454
inputStyles: string | (string | false)[]
5555
type: string
@@ -67,7 +67,7 @@ const props = withDefaults(
6767
label: "",
6868
disabled: false,
6969
autofocus: true,
70-
}
70+
},
7171
)
7272
7373
const emit = defineEmits<{
@@ -84,6 +84,6 @@ onKeyStroke(
8484
return emit("submit")
8585
}
8686
},
87-
{ target: inputRef, eventName: "keydown" }
87+
{ target: inputRef, eventName: "keydown" },
8888
)
8989
</script>

src/components/smart/Table.vue

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@
5555
v-for="(rowData, rowIndex) in workingList"
5656
:key="rowIndex"
5757
class="rounded-xl text-secondaryDark hover:cursor-pointer hover:bg-divider"
58-
:class="{ 'divide-x divide-divider': showYBorder }"
58+
:class="[
59+
{ 'divide-x divide-divider': showYBorder },
60+
getRowCustomStyle(rowData),
61+
]"
5962
@click="onRowClicked(rowData)"
6063
>
6164
<td v-if="selectedRows">
@@ -95,56 +98,74 @@
9598
import { useVModel } from "@vueuse/core"
9699
import { isEqual } from "lodash-es"
97100
import { computed, ref, watch, watchEffect } from "vue"
98-
import { HoppSmartSpinner } from ".."
101+
import HoppSmartSpinner from "./Spinner.vue"
99102
100103
export type CellHeading = {
101104
key: string
102105
label?: string
103106
preventClick?: boolean
104107
}
105108
106-
export type Item = Record<string, unknown>
109+
export type RowStyle = {
110+
classes?: string
111+
style?: Record<string, string>
112+
}
113+
114+
export type Item<T = Record<string, unknown>> = T
107115
108116
const props = withDefaults(
109117
defineProps<{
110-
/** Whether to show the vertical border between columns */
118+
// Whether to show vertical borders between cells
111119
showYBorder?: boolean
112-
/** The list of items to be displayed in the table */
113-
list: Item[]
114-
/** The headings of the table */
120+
121+
// List of items to be displayed in the table
122+
list: Item<any>[]
123+
124+
// Headings for the table
115125
headings?: CellHeading[]
116126
117-
/** Contains the rows selected */
118-
selectedRows?: Item[]
127+
// Currently selected rows
128+
selectedRows?: Item<any>[]
119129
120-
/** Whether to enable sorting */
130+
// Sorting configurations
121131
sort?: {
122-
/** The key to sort the list by */
123132
key: string
124133
direction: Direction
125134
}
126135
127-
/** Whether to show a loading spinner */
136+
// Whether the table is in loading state
128137
loading?: boolean
138+
139+
/**
140+
* Custom styles for rows:
141+
* - Object: key-based (determined by getRowStyleKey)
142+
* - String: applied as CSS class to all rows
143+
*/
144+
rowStyles?: Record<string, RowStyle> | string
145+
/**
146+
* Function to determine which row style to apply
147+
* Returns rowStyles directly if it's a string
148+
* R eturns a key that exists in rowStyles (when rowStyles is an object), or null if no style should be applied
149+
*/
150+
getRowStyleKey?: (rowData: Item<any>) => string | null
129151
}>(),
130152
{
131153
showYBorder: false,
132154
sort: undefined,
133155
selectedRows: undefined,
134156
loading: false,
157+
rowStyles: () => ({}),
158+
getRowStyleKey: () => null,
135159
},
136160
)
137161
138162
const emit = defineEmits<{
139-
(event: "onRowClicked", item: Item): void
140-
(event: "update:list", list: Item[]): void
141-
(event: "update:selectedRows", selectedRows: Item[]): void
163+
(event: "onRowClicked", item: Item<any>): void
164+
(event: "update:list", list: Item<any>[]): void
165+
(event: "update:selectedRows", selectedRows: Item<any>[]): void
142166
}>()
143167
144-
// The working version of the list that is used to perform operations upon
145168
const workingList = useVModel(props, "list", emit)
146-
147-
// Checkbox functionality
148169
const selectedRows = useVModel(props, "selectedRows", emit)
149170
150171
watch(workingList.value, (updatedList) => {
@@ -156,14 +177,14 @@ watch(workingList.value, (updatedList) => {
156177
}
157178
})
158179
159-
const onRowClicked = (item: Item) => emit("onRowClicked", item)
180+
const onRowClicked = (item: Item<any>) => emit("onRowClicked", item)
160181
161-
const isRowSelected = (item: Item) => {
182+
const isRowSelected = (item: Item<any>) => {
162183
const { selected, ...data } = item
163184
return selectedRows.value?.some((row) => isEqual(row, data))
164185
}
165186
166-
const toggleRow = (item: Item) => {
187+
const toggleRow = (item: Item<any>) => {
167188
item.selected = !item.selected
168189
const { selected, ...data } = item
169190
@@ -212,7 +233,16 @@ watchEffect(() => {
212233
}
213234
})
214235
215-
// Sort List by key and direction which can set to ascending or descending
236+
const getRowCustomStyle = (rowData: Item<any>) => {
237+
if (typeof props.rowStyles === "string") {
238+
return props.rowStyles
239+
}
240+
241+
const styleKey = props.getRowStyleKey?.(rowData)
242+
if (!styleKey || !props.rowStyles[styleKey]) return ""
243+
return props.rowStyles[styleKey].classes || ""
244+
}
245+
216246
export type Direction = "ascending" | "descending"
217247
218248
const sortList = (key: string, direction: Direction) => {

src/stories/Table.story.vue

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
<template>
22
<Story title="Table">
33
<Variant title="General">
4+
<div class="mb-3">
5+
<p class="mb-1 text-sm">
6+
This example demonstrates how to implement a table with default
7+
configurations
8+
</p>
9+
</div>
410
<HoppSmartTable
511
:headings="headings"
612
:loading="loading"
@@ -11,6 +17,12 @@
1117

1218
<!-- Custom implementation of the Table -->
1319
<Variant title="Custom">
20+
<div class="mb-3">
21+
<p class="mb-1 text-sm">
22+
This example demonstrates how to use the table component with custom
23+
configurations
24+
</p>
25+
</div>
1426
<HoppSmartTable :loading="loading" :list="list">
1527
<template #head>
1628
<th
@@ -49,8 +61,47 @@
4961
</HoppSmartTable>
5062
</Variant>
5163

64+
<!-- Custom Row Styles -->
65+
<Variant title="Custom Row Styles">
66+
<div class="mb-3">
67+
<p class="mb-1 text-sm">
68+
This example demonstrates how to customize row styles based on data:
69+
</p>
70+
<ul class="list-disc pl-5 text-xs">
71+
<li>
72+
Rows with members > 10 are highlighted in
73+
<span class="text-green-500">green</span>
74+
</li>
75+
<li>
76+
Rows with role containing "Lab" are highlighted in
77+
<span class="text-yellow-500">yellow</span>
78+
</li>
79+
</ul>
80+
</div>
81+
<HoppSmartTable
82+
:headings="headings"
83+
:list="list"
84+
:row-styles="rowStyles"
85+
:get-row-style-key="getRowStyleKey"
86+
/>
87+
</Variant>
88+
5289
<!-- Extending the Table functionality -->
5390
<Variant title="Extension">
91+
<div class="mb-3">
92+
<p class="mb-1 text-sm">
93+
This example demonstrates how to extend the table functionality:
94+
</p>
95+
<ul class="list-disc pl-5 text-xs">
96+
<li>
97+
Added a search functionality to filter rows based on the search
98+
query
99+
</li>
100+
<li>
101+
Added a sort functionality to sort rows based on the name column
102+
</li>
103+
</ul>
104+
</div>
54105
<HoppSmartTable
55106
:headings="headings"
56107
:loading="loading"
@@ -151,4 +202,20 @@ const extensionList = computed(() => {
151202
)
152203
})
153204
})
205+
206+
// Custom Row Styles
207+
const rowStyles = {
208+
majorityMembers: {
209+
classes: "bg-green-400 text-green-900 hover:bg-green-200",
210+
},
211+
labRole: {
212+
classes: "bg-yellow-300 text-yellow-900 hover:bg-yellow-100",
213+
},
214+
}
215+
216+
const getRowStyleKey = (row: List) => {
217+
if (row.members > 10) return "majorityMembers"
218+
if (row.role.includes("Lab")) return "labRole"
219+
return null
220+
}
154221
</script>

0 commit comments

Comments
 (0)