@kikiloaw/simple-table 1.0.2 → 1.0.3

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/README.md CHANGED
@@ -87,6 +87,307 @@ public function getData(Request $request)
87
87
 
88
88
  ---
89
89
 
90
+ ## 📦 Using Predefined/Static Data
91
+
92
+ **Want to use static data instead of an API?** SimpleTable handles this perfectly in client-side mode!
93
+
94
+ ### Basic Setup
95
+
96
+ ```vue
97
+ <script setup lang="ts">
98
+ import SimpleTable from '@kikiloaw/simple-table'
99
+
100
+ // Define your static data
101
+ const data = [
102
+ { id: 1, name: 'John Doe', email: 'john@example.com', status: 'active' },
103
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'active' },
104
+ { id: 3, name: 'Bob Johnson', email: 'bob@example.com', status: 'inactive' },
105
+ // ... more rows
106
+ ]
107
+
108
+ const columns = [
109
+ { key: 'id', label: '#', sortable: true, width: '80px' },
110
+ { key: 'name', label: 'Name', sortable: true },
111
+ { key: 'email', label: 'Email', sortable: true },
112
+ { key: 'status', label: 'Status', width: '120px' }
113
+ ]
114
+
115
+ const pageSizes = [
116
+ { label: '50 Rows', value: 50 },
117
+ { label: '100 Rows', value: 100 },
118
+ ]
119
+ </script>
120
+
121
+ <template>
122
+ <SimpleTable
123
+ :data="data"
124
+ :columns="columns"
125
+ :page-sizes="pageSizes"
126
+ :per-page="50"
127
+ mode="client"
128
+ searchable
129
+ />
130
+ </template>
131
+ ```
132
+
133
+ ### Key Props for Static Data
134
+
135
+ | Prop | Required | Default | Description |
136
+ |------|----------|---------|-------------|
137
+ | `:data` | **Yes** | `[]` | Your static array of objects |
138
+ | `mode` | **Yes** | `'auto'` | Set to `"client"` for static data |
139
+ | `:per-page` | Recommended | `10` | Initial page size (should match first option in pageSizes) |
140
+ | `:page-sizes` | Optional | `[10,20,30,50,100]` | Available page size options |
141
+
142
+ ### ⚠️ Common Pitfalls
143
+
144
+ #### 1. **Don't Mix Static Data with `fetch-url`**
145
+
146
+ ❌ **Wrong:**
147
+ ```vue
148
+ <!-- This will ignore your static data! -->
149
+ <SimpleTable
150
+ :data="myData"
151
+ fetch-url="/api/users" <!-- ❌ Conflicts with :data -->
152
+ />
153
+ ```
154
+
155
+ ✅ **Correct:**
156
+ ```vue
157
+ <!-- Remove fetch-url when using static data -->
158
+ <SimpleTable
159
+ :data="myData"
160
+ mode="client"
161
+ />
162
+ ```
163
+
164
+ #### 2. **Set Initial Page Size to Match Your Options**
165
+
166
+ ❌ **Wrong:**
167
+ ```vue
168
+ <!-- Component defaults to 10, but you only have 50/100 options -->
169
+ <SimpleTable
170
+ :data="data"
171
+ :page-sizes="[{ label: '50 Rows', value: 50 }, { label: '100 Rows', value: 100 }]"
172
+ <!-- ❌ Will show "10 Rows" which doesn't exist in dropdown -->
173
+ />
174
+ ```
175
+
176
+ ✅ **Correct:**
177
+ ```vue
178
+ <SimpleTable
179
+ :data="data"
180
+ :page-sizes="[50, 100]"
181
+ :per-page="50" <!-- ✅ Matches first option -->
182
+ />
183
+ ```
184
+
185
+ #### 3. **Columns Must Match Your Data Structure**
186
+
187
+ ❌ **Wrong:**
188
+ ```vue
189
+ <script setup>
190
+ const data = [
191
+ { CourseCode: 'CS101', Description: 'Intro to CS', Units: 3 }
192
+ ]
193
+
194
+ const columns = [
195
+ { key: 'course_code', label: 'Code' }, // ❌ Wrong key!
196
+ { key: 'description', label: 'Name' }, // ❌ Wrong key!
197
+ ]
198
+ </script>
199
+ ```
200
+
201
+ ✅ **Correct:**
202
+ ```vue
203
+ <script setup>
204
+ const data = [
205
+ { CourseCode: 'CS101', Description: 'Intro to CS', Units: 3 }
206
+ ]
207
+
208
+ const columns = [
209
+ { key: 'CourseCode', label: 'Code' }, // ✅ Matches data
210
+ { key: 'Description', label: 'Name' }, // ✅ Matches data
211
+ { key: 'Units', label: 'Units' },
212
+ ]
213
+ </script>
214
+ ```
215
+
216
+ ### Features Available in Client-Side Mode
217
+
218
+ ✅ **Works:**
219
+ - Client-side searching (filters through your data array)
220
+ - Client-side sorting (by sortable columns)
221
+ - Client-side pagination (chunks your data into pages)
222
+ - Data transformation via `beforeRender`
223
+ - Custom cell rendering
224
+ - Group headers
225
+ - Auto-numbering
226
+
227
+ ❌ **Not Available:**
228
+ - Server-side sorting (data is sorted locally)
229
+ - API caching (no API calls)
230
+ - Query parameters (no server to send them to)
231
+
232
+ ### Complete Example with Group Headers
233
+
234
+ ```vue
235
+ <script setup lang="ts">
236
+ import SimpleTable from '@kikiloaw/simple-table'
237
+
238
+ // Static course data
239
+ const data = [
240
+ {
241
+ CourseCode: 'CS101',
242
+ Description: 'Intro to Computer Science',
243
+ Units: 3,
244
+ Grade: 'A',
245
+ semester: '1st Semester, 2023-2024'
246
+ },
247
+ {
248
+ CourseCode: 'MATH101',
249
+ Description: 'Calculus I',
250
+ Units: 4,
251
+ Grade: 'B+',
252
+ semester: '1st Semester, 2023-2024'
253
+ },
254
+ {
255
+ CourseCode: 'CS102',
256
+ Description: 'Data Structures',
257
+ Units: 3,
258
+ Grade: 'A-',
259
+ semester: '2nd Semester, 2023-2024'
260
+ },
261
+ ]
262
+
263
+ const columns = [
264
+ { key: 'CourseCode', label: 'Course Code', sortable: true, width: '150px' },
265
+ { key: 'Description', label: 'Description', sortable: true, width: '300px' },
266
+ { key: 'Units', label: 'Units', width: '80px' },
267
+ { key: 'Grade', label: 'Grade', sortable: true, width: '80px' },
268
+ ]
269
+
270
+ // Add group headers by semester
271
+ const addGroupHeaders = (rows) => {
272
+ // DON'T sort here if your data is already in the correct order!
273
+ // Sorting will override your predefined order
274
+
275
+ const result = []
276
+ let currentSemester = null
277
+
278
+ rows.forEach(row => {
279
+ const semester = row.semester || 'No Semester'
280
+
281
+ // When semester changes, add a header row
282
+ if (semester !== currentSemester) {
283
+ result.push({
284
+ _isGroupHeader: true,
285
+ _groupTitle: semester,
286
+ // Empty values for all columns
287
+ CourseCode: '',
288
+ Description: semester,
289
+ Units: '',
290
+ Grade: '',
291
+ })
292
+ currentSemester = semester
293
+ }
294
+
295
+ result.push(row)
296
+ })
297
+
298
+ return result
299
+ }
300
+ </script>
301
+
302
+ <template>
303
+ <SimpleTable
304
+ :data="data"
305
+ :columns="columns"
306
+ :page-sizes="[50, 100]"
307
+ :per-page="50"
308
+ :before-render="addGroupHeaders"
309
+ mode="client"
310
+ searchable
311
+ odd-row-color="bg-gray-50"
312
+ even-row-color="bg-white"
313
+ hover-color="hover:bg-green-100"
314
+ />
315
+ </template>
316
+
317
+ ### 📏 Row Height Control
318
+
319
+ Control the exact height of your table rows with the `rowHeight` prop:
320
+
321
+ ```vue
322
+ <template>
323
+ <SimpleTable
324
+ :data="data"
325
+ :columns="columns"
326
+ :row-height="38" <!-- Rows will be exactly 38px tall -->
327
+ />
328
+ </template>
329
+ ```
330
+
331
+ **How it works:**
332
+ - Sets the `height` style on both header and data rows
333
+ - Automatically adjusts cell padding based on the height
334
+ - Default: `38px` (compact and readable)
335
+
336
+ **Recommended Values:**
337
+
338
+ | Height | Padding | Use Case |
339
+ |--------|---------|----------|
340
+ | `30-36px` | `p-2` (8px) | **Extra compact** - Maximum rows visible, dense data |
341
+ | `38-42px` | `p-2` (8px) | **Standard** - Good balance (default: 38px) |
342
+ | `44-55px` | `p-3` (12px) | **Comfortable** - Easy to read, spacious |
343
+ | `56px+` | `p-4` (16px) | **Very spacious** - Accessibility-friendly, large text |
344
+
345
+ **Examples:**
346
+
347
+ ```vue
348
+ <!-- Ultra compact for dashboards -->
349
+ <SimpleTable :row-height="32" />
350
+
351
+ <!-- Default - balanced -->
352
+ <SimpleTable :row-height="38" /> <!-- or omit for default -->
353
+
354
+ <!-- Comfortable reading -->
355
+ <SimpleTable :row-height="48" />
356
+
357
+ <!-- Accessibility-friendly -->
358
+ <SimpleTable :row-height="60" />
359
+
360
+ <!-- No prop = uses default 38px -->
361
+ <SimpleTable :data="data" :columns="columns" />
362
+ ```
363
+
364
+ **Padding Auto-Adjustment:**
365
+
366
+ The component automatically adjusts internal padding:
367
+ - **< 44px**: Uses `p-2` (8px) - Compact
368
+ - **44-55px**: Uses `p-3` (12px) - Normal
369
+ - **56px+**: Uses `p-4` (16px) - Comfortable
370
+
371
+ ### 💡 Best Practices
372
+
373
+ 1. **Data Order Preservation**
374
+ - If your data is already sorted correctly, **don't sort it again** in `beforeRender`
375
+ - Let users sort by clicking column headers if needed
376
+
377
+ 2. **Performance**
378
+ - Client-side mode works great for **< 1,000 rows**
379
+ - For larger datasets, consider server-side mode with `fetch-url`
380
+
381
+ 3. **Reactivity**
382
+ - Use `ref()` or `reactive()` if your data changes
383
+ - The table will automatically update when data changes
384
+
385
+ 4. **Page Size Options**
386
+ - Keep options reasonable: `[10, 25, 50, 100]`
387
+ - Set `:per-page` to match your first option
388
+
389
+ ---
390
+
90
391
  ## 📖 Table of Contents
91
392
 
92
393
  - [Core Concepts](#-core-concepts)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kikiloaw/simple-table",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "A lightweight, dependency-light DataTable component for Vue 3 with Tailwind CSS",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -45,6 +45,7 @@ interface Props {
45
45
  queryParams?: Record<string, any> // Additional parameters to send with every request (e.g., filters, user context)
46
46
 
47
47
  // Style Props
48
+ rowHeight?: number // Table row height in pixels (default: 38)
48
49
  oddRowColor?: string // Tailwind color class, e.g. 'bg-white'
49
50
  evenRowColor?: string // Tailwind color class, e.g. 'bg-gray-50'
50
51
  hoverColor?: string // Tailwind color class for hover, e.g. 'hover:bg-gray-100'. If passed, we'll try to apply group-hover for fixed cols.
@@ -60,6 +61,7 @@ const props = withDefaults(defineProps<Props>(), {
60
61
  queryParams: () => ({}),
61
62
  perPage: 10,
62
63
  pageSizes: () => [10, 20, 30, 50, 100],
64
+ rowHeight: 38,
63
65
  oddRowColor: 'bg-background',
64
66
  evenRowColor: 'bg-background',
65
67
  hoverColor: 'hover:bg-muted/50'
@@ -88,6 +90,37 @@ const normalizedPageSizes = computed(() => {
88
90
  return []
89
91
  })
90
92
 
93
+ // -- Computed: Row height-based sizing --
94
+ const densityConfig = computed(() => {
95
+ const height = props.rowHeight || 38
96
+
97
+ // Calculate padding based on height
98
+ // For 38px height: use p-2 (8px)
99
+ // For 48px height: use p-3 (12px)
100
+ // For 56px+ height: use p-4 (16px)
101
+ let cellPadding = 'p-2'
102
+ let headerPadding = 'px-2'
103
+ let groupHeaderPadding = 'py-1'
104
+
105
+ if (height >= 56) {
106
+ cellPadding = 'p-4'
107
+ headerPadding = 'px-4'
108
+ groupHeaderPadding = 'py-2'
109
+ } else if (height >= 44) {
110
+ cellPadding = 'p-3'
111
+ headerPadding = 'px-3'
112
+ groupHeaderPadding = 'py-1.5'
113
+ }
114
+
115
+ return {
116
+ cellPadding,
117
+ cellHeight: `${height}px`,
118
+ headerHeight: `h-[${height}px]`,
119
+ headerPadding,
120
+ groupHeaderPadding
121
+ }
122
+ })
123
+
91
124
 
92
125
 
93
126
  // <template>
@@ -722,12 +755,14 @@ function getCellStyle(col: any) {
722
755
  <!-- We add min-w-full to Table to ensure it stretches -->
723
756
  <Table class="min-w-full table-fixed">
724
757
  <TableHeader>
725
- <TableRow>
758
+ <TableRow :style="{ height: densityConfig.cellHeight }">
726
759
  <TableHead
727
760
  v-for="(col, idx) in columns"
728
761
  :key="col.key"
729
762
  :class="getCellClass(col, idx, columns.length)"
730
763
  :style="getCellStyle(col)"
764
+ :height="densityConfig.headerHeight"
765
+ :padding="densityConfig.headerPadding"
731
766
  >
732
767
  <div
733
768
  v-if="col.sortable"
@@ -757,11 +792,12 @@ function getCellStyle(col: any) {
757
792
  :key="idx"
758
793
  class="group"
759
794
  :class="getRowClass(row, idx)"
795
+ :style="{ height: densityConfig.cellHeight }"
760
796
  >
761
797
  <!-- Group Header Row: Single cell spanning all columns -->
762
798
  <template v-if="row._isGroupHeader">
763
799
  <TableCell :colspan="columns.length" class="border-b border-gray-200">
764
- <div class="px-2 py-2 font-semibold text-gray-700 text-sm uppercase tracking-wide">
800
+ <div :class="['px-2', densityConfig.groupHeaderPadding, 'font-semibold text-gray-700 text-sm uppercase tracking-wide']">
765
801
  {{ row._groupTitle }}
766
802
  </div>
767
803
  </TableCell>
@@ -774,6 +810,8 @@ function getCellStyle(col: any) {
774
810
  :key="col.key"
775
811
  :class="getCellClass(col, cIdx, columns.length, idx)"
776
812
  :style="getCellStyle(col)"
813
+ :padding="densityConfig.cellPadding"
814
+ :height="densityConfig.cellHeight"
777
815
  >
778
816
  <!-- Auto-numbering or custom cell rendering -->
779
817
  <div>
@@ -790,7 +828,7 @@ function getCellStyle(col: any) {
790
828
  </template>
791
829
  </TableRow>
792
830
  </template>
793
- <TableRow v-else>
831
+ <TableRow v-else :style="{ height: densityConfig.cellHeight }">
794
832
  <TableCell :colspan="columns.length" class="h-24 text-center">
795
833
  No results.
796
834
  </TableCell>
@@ -4,18 +4,29 @@ import { cn } from '@/lib/utils'
4
4
 
5
5
  const props = defineProps<{
6
6
  class?: HTMLAttributes['class']
7
+ style?: any
8
+ padding?: string
9
+ height?: string
7
10
  }>()
8
11
 
9
12
  const delegatedProps = computed(() => {
10
- const { class: _, ...delegated } = props
13
+ const { class: _, padding: __, height: ___, style: ____, ...delegated } = props
11
14
 
12
15
  return delegated
13
16
  })
17
+
18
+ const cellStyle = computed(() => {
19
+ const baseStyle = (props as any).style || {}
20
+ const heightStyle = props.height ? { minHeight: props.height } : {}
21
+
22
+ return { ...baseStyle, ...heightStyle }
23
+ })
14
24
  </script>
15
25
 
16
26
  <template>
17
27
  <td
18
- :class="cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', props.class)"
28
+ :class="cn(props.padding || 'p-2', 'align-middle [&:has([role=checkbox])]:pr-0', props.class)"
29
+ :style="cellStyle"
19
30
  v-bind="delegatedProps"
20
31
  >
21
32
  <slot />
@@ -4,10 +4,12 @@ import { cn } from '@/lib/utils'
4
4
 
5
5
  const props = defineProps<{
6
6
  class?: HTMLAttributes['class']
7
+ height?: string
8
+ padding?: string
7
9
  }>()
8
10
 
9
11
  const delegatedProps = computed(() => {
10
- const { class: _, ...delegated } = props
12
+ const { class: _, height: __, padding: ___, ...delegated } = props
11
13
 
12
14
  return delegated
13
15
  })
@@ -17,7 +19,9 @@ const delegatedProps = computed(() => {
17
19
  <th
18
20
  :class="
19
21
  cn(
20
- 'h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0',
22
+ props.height || 'h-[38px]',
23
+ props.padding || 'px-2',
24
+ 'text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0',
21
25
  props.class,
22
26
  )
23
27
  "