snice 1.14.3 → 2.1.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.
Files changed (185) hide show
  1. package/bin/templates/base/tsconfig.json +5 -4
  2. package/components/accordion/demo.html +403 -0
  3. package/components/accordion/snice-accordion-item.css +85 -0
  4. package/components/accordion/snice-accordion-item.ts +226 -0
  5. package/components/accordion/snice-accordion.css +31 -0
  6. package/components/accordion/snice-accordion.ts +182 -0
  7. package/components/accordion/snice-accordion.types.ts +32 -0
  8. package/components/alert/demo.html +445 -0
  9. package/components/alert/snice-alert.css +195 -0
  10. package/components/alert/snice-alert.ts +141 -0
  11. package/components/alert/snice-alert.types.ts +12 -0
  12. package/components/avatar/demo.html +598 -0
  13. package/components/avatar/snice-avatar.css +131 -0
  14. package/components/avatar/snice-avatar.ts +136 -0
  15. package/components/avatar/snice-avatar.types.ts +13 -0
  16. package/components/badge/demo.html +523 -0
  17. package/components/badge/snice-badge.css +161 -0
  18. package/components/badge/snice-badge.ts +117 -0
  19. package/components/badge/snice-badge.types.ts +16 -0
  20. package/components/breadcrumbs/demo.html +404 -0
  21. package/components/breadcrumbs/snice-breadcrumbs.css +133 -0
  22. package/components/breadcrumbs/snice-breadcrumbs.ts +191 -0
  23. package/components/breadcrumbs/snice-breadcrumbs.types.ts +26 -0
  24. package/components/breadcrumbs/snice-crumb.ts +26 -0
  25. package/components/button/demo.html +42 -0
  26. package/components/button/snice-button.css +230 -0
  27. package/components/button/snice-button.ts +169 -0
  28. package/components/button/snice-button.types.ts +25 -0
  29. package/components/card/demo.html +525 -0
  30. package/components/card/snice-card.css +140 -0
  31. package/components/card/snice-card.ts +102 -0
  32. package/components/card/snice-card.types.ts +10 -0
  33. package/components/checkbox/demo.html +253 -0
  34. package/components/checkbox/snice-checkbox.css +164 -0
  35. package/components/checkbox/snice-checkbox.ts +223 -0
  36. package/components/checkbox/snice-checkbox.types.ts +22 -0
  37. package/components/chip/demo.html +383 -0
  38. package/components/chip/snice-chip.css +195 -0
  39. package/components/chip/snice-chip.ts +139 -0
  40. package/components/chip/snice-chip.types.ts +15 -0
  41. package/components/date-picker/README.md +233 -0
  42. package/components/date-picker/demo.html +191 -0
  43. package/components/date-picker/snice-date-picker.css +330 -0
  44. package/components/date-picker/snice-date-picker.ts +777 -0
  45. package/components/date-picker/snice-date-picker.types.ts +83 -0
  46. package/components/divider/demo.html +233 -0
  47. package/components/divider/snice-divider.css +155 -0
  48. package/components/divider/snice-divider.ts +69 -0
  49. package/components/divider/snice-divider.types.ts +15 -0
  50. package/components/drawer/demo.html +328 -0
  51. package/components/drawer/snice-drawer.css +476 -0
  52. package/components/drawer/snice-drawer.ts +287 -0
  53. package/components/drawer/snice-drawer.types.ts +17 -0
  54. package/components/global.d.ts +14 -0
  55. package/components/input/demo.html +303 -0
  56. package/components/input/snice-input.css +257 -0
  57. package/components/input/snice-input.ts +442 -0
  58. package/components/input/snice-input.types.ts +59 -0
  59. package/components/input/test.html +77 -0
  60. package/components/layout/README.md +260 -0
  61. package/components/layout/demo.html +538 -0
  62. package/components/layout/snice-layout-blog.css +129 -0
  63. package/components/layout/snice-layout-blog.ts +48 -0
  64. package/components/layout/snice-layout-card.css +104 -0
  65. package/components/layout/snice-layout-card.ts +35 -0
  66. package/components/layout/snice-layout-centered.css +51 -0
  67. package/components/layout/snice-layout-centered.ts +22 -0
  68. package/components/layout/snice-layout-dashboard.css +98 -0
  69. package/components/layout/snice-layout-dashboard.ts +45 -0
  70. package/components/layout/snice-layout-fullscreen.css +72 -0
  71. package/components/layout/snice-layout-fullscreen.ts +34 -0
  72. package/components/layout/snice-layout-landing.css +92 -0
  73. package/components/layout/snice-layout-landing.ts +47 -0
  74. package/components/layout/snice-layout-minimal.css +16 -0
  75. package/components/layout/snice-layout-minimal.ts +19 -0
  76. package/components/layout/snice-layout-sidebar.css +117 -0
  77. package/components/layout/snice-layout-sidebar.ts +48 -0
  78. package/components/layout/snice-layout-split.css +103 -0
  79. package/components/layout/snice-layout-split.ts +29 -0
  80. package/components/layout/snice-layout.css +72 -0
  81. package/components/layout/snice-layout.ts +35 -0
  82. package/components/layout/snice-layout.types.ts +5 -0
  83. package/components/login/demo-auth-controller.ts +185 -0
  84. package/components/login/demo.html +470 -0
  85. package/components/login/snice-login.css +204 -0
  86. package/components/login/snice-login.ts +337 -0
  87. package/components/login/snice-login.types.ts +34 -0
  88. package/components/modal/demo.html +291 -0
  89. package/components/modal/snice-modal.css +203 -0
  90. package/components/modal/snice-modal.ts +233 -0
  91. package/components/modal/snice-modal.types.ts +21 -0
  92. package/components/pagination/demo.html +395 -0
  93. package/components/pagination/snice-pagination.ts +333 -0
  94. package/components/pagination/snice-pagination.types.ts +21 -0
  95. package/components/progress/demo.html +510 -0
  96. package/components/progress/snice-progress.css +267 -0
  97. package/components/progress/snice-progress.ts +247 -0
  98. package/components/progress/snice-progress.types.ts +19 -0
  99. package/components/radio/demo.html +287 -0
  100. package/components/radio/snice-radio.css +171 -0
  101. package/components/radio/snice-radio.ts +218 -0
  102. package/components/radio/snice-radio.types.ts +21 -0
  103. package/components/select/demo.html +511 -0
  104. package/components/select/snice-option.ts +52 -0
  105. package/components/select/snice-option.types.ts +14 -0
  106. package/components/select/snice-select.css +392 -0
  107. package/components/select/snice-select.ts +796 -0
  108. package/components/select/snice-select.types.ts +55 -0
  109. package/components/skeleton/demo.html +514 -0
  110. package/components/skeleton/snice-skeleton.css +109 -0
  111. package/components/skeleton/snice-skeleton.ts +126 -0
  112. package/components/skeleton/snice-skeleton.types.ts +11 -0
  113. package/components/switch/demo.html +284 -0
  114. package/components/switch/snice-switch.css +221 -0
  115. package/components/switch/snice-switch.ts +229 -0
  116. package/components/switch/snice-switch.types.ts +23 -0
  117. package/components/symbols.ts +23 -0
  118. package/components/table/demo-table-controller.ts +100 -0
  119. package/components/table/demo.html +480 -0
  120. package/components/table/snice-cell-boolean.ts +112 -0
  121. package/components/table/snice-cell-date.ts +210 -0
  122. package/components/table/snice-cell-duration.ts +91 -0
  123. package/components/table/snice-cell-filesize.ts +90 -0
  124. package/components/table/snice-cell-number.ts +165 -0
  125. package/components/table/snice-cell-progress.ts +83 -0
  126. package/components/table/snice-cell-rating.ts +82 -0
  127. package/components/table/snice-cell-sparkline.ts +253 -0
  128. package/components/table/snice-cell-text.ts +125 -0
  129. package/components/table/snice-cell.css +296 -0
  130. package/components/table/snice-cell.ts +473 -0
  131. package/components/table/snice-column.ts +353 -0
  132. package/components/table/snice-header.css +243 -0
  133. package/components/table/snice-header.ts +261 -0
  134. package/components/table/snice-progress.ts +66 -0
  135. package/components/table/snice-rating.ts +45 -0
  136. package/components/table/snice-row.css +255 -0
  137. package/components/table/snice-row.ts +331 -0
  138. package/components/table/snice-table.css +241 -0
  139. package/components/table/snice-table.ts +737 -0
  140. package/components/table/snice-table.types.ts +158 -0
  141. package/components/tabs/demo.html +487 -0
  142. package/components/tabs/snice-tab-panel.css +264 -0
  143. package/components/tabs/snice-tab-panel.ts +47 -0
  144. package/components/tabs/snice-tab.css +96 -0
  145. package/components/tabs/snice-tab.ts +65 -0
  146. package/components/tabs/snice-tabs.css +189 -0
  147. package/components/tabs/snice-tabs.ts +332 -0
  148. package/components/tabs/snice-tabs.types.ts +28 -0
  149. package/components/theme/theme.css +234 -0
  150. package/components/toast/demo.html +329 -0
  151. package/components/toast/snice-toast-container.ts +256 -0
  152. package/components/toast/snice-toast.css +213 -0
  153. package/components/toast/snice-toast.ts +276 -0
  154. package/components/toast/snice-toast.types.ts +35 -0
  155. package/components/tooltip/demo.html +350 -0
  156. package/components/tooltip/snice-tooltip-portal.css +79 -0
  157. package/components/tooltip/snice-tooltip.css +117 -0
  158. package/components/tooltip/snice-tooltip.ts +612 -0
  159. package/components/tooltip/snice-tooltip.types.ts +32 -0
  160. package/components/transitions.ts +94 -0
  161. package/components/tsconfig.json +18 -0
  162. package/dist/index.cjs +441 -329
  163. package/dist/index.cjs.map +1 -1
  164. package/dist/index.cjs.min.map +1 -1
  165. package/dist/index.esm.js +441 -329
  166. package/dist/index.esm.js.map +1 -1
  167. package/dist/index.esm.min.js +3 -3
  168. package/dist/index.esm.min.js.map +1 -1
  169. package/dist/index.iife.js +441 -329
  170. package/dist/index.iife.js.map +1 -1
  171. package/dist/index.iife.min.js +3 -3
  172. package/dist/index.iife.min.js.map +1 -1
  173. package/dist/symbols.esm.js +1 -1
  174. package/dist/transitions.esm.js +1 -1
  175. package/dist/types/controller.d.ts +1 -1
  176. package/dist/types/element.d.ts +10 -10
  177. package/dist/types/events.d.ts +2 -2
  178. package/dist/types/index.d.ts +1 -1
  179. package/dist/types/observe.d.ts +1 -1
  180. package/dist/types/request-response.d.ts +2 -3
  181. package/dist/types/router.d.ts +1 -1
  182. package/package.json +9 -3
  183. package/dist/index.cjs.min +0 -15
  184. package/dist/symbols.cjs +0 -103
  185. package/dist/transitions.cjs +0 -219
@@ -0,0 +1,261 @@
1
+ import { element, property, on, watch, ready, dispatch } from 'snice';
2
+ import type {
3
+ SniceHeaderElement,
4
+ ColumnDefinition,
5
+ TableSort,
6
+ SortDirection
7
+ } from './snice-table.types';
8
+
9
+ @element('snice-header')
10
+ export class SniceHeader extends HTMLElement implements SniceHeaderElement {
11
+ @property({ type: Boolean, reflect: true })
12
+ sticky: boolean = false;
13
+
14
+ @property({ type: Array })
15
+ columns: ColumnDefinition[] = [];
16
+
17
+ @property({ type: Boolean, reflect: true })
18
+ selectable: boolean = false;
19
+
20
+ @property({ type: Boolean, reflect: true })
21
+ sortable: boolean = false;
22
+
23
+ @property({ type: Object })
24
+ currentSort: TableSort = { column: '', direction: null };
25
+
26
+ @property({ type: Boolean, reflect: true, attribute: 'all-selected' })
27
+ allSelected: boolean = false;
28
+
29
+ @property({ type: Boolean, reflect: true, attribute: 'some-selected' })
30
+ someSelected: boolean = false;
31
+
32
+ html() {
33
+ return `
34
+ <div class="header-container" part="container">
35
+ ${this.selectable ? this.renderSelectAllCheckbox() : ''}
36
+ ${this.renderHeaderCells()}
37
+ </div>
38
+ `;
39
+ }
40
+
41
+ @ready()
42
+ init() {
43
+ this.updateHeaderAttributes();
44
+ }
45
+
46
+ @watch('sticky', 'selectable', 'sortable')
47
+ updateHeaderAttributes() {
48
+ this.classList.toggle('header--sticky', this.sticky);
49
+ this.classList.toggle('header--selectable', this.selectable);
50
+ this.classList.toggle('header--sortable', this.sortable);
51
+ }
52
+
53
+ @watch('columns', 'currentSort')
54
+ updateHeaderCells() {
55
+ const cellsContainer = this.shadowRoot?.querySelector('.cells-container');
56
+ if (cellsContainer) {
57
+ cellsContainer.innerHTML = this.renderHeaderCells();
58
+ }
59
+ }
60
+
61
+ @watch('allSelected', 'someSelected')
62
+ updateSelectAllCheckbox() {
63
+ const checkbox = this.shadowRoot?.querySelector('.select-all-checkbox') as HTMLInputElement;
64
+ if (checkbox) {
65
+ checkbox.checked = this.allSelected;
66
+ checkbox.indeterminate = this.someSelected && !this.allSelected;
67
+ }
68
+ }
69
+
70
+ @on('click', '.header-cell[data-sortable="true"]')
71
+ handleSort(e: MouseEvent) {
72
+ const cell = e.currentTarget as HTMLElement;
73
+ const columnKey = cell.dataset.columnKey;
74
+
75
+ if (columnKey && this.sortable) {
76
+ this.dispatchSort(columnKey);
77
+ }
78
+ }
79
+
80
+ @on('change', '.select-all-checkbox')
81
+ handleSelectAll(e: Event) {
82
+ const checkbox = e.target as HTMLInputElement;
83
+ this.dispatchSelectAll(checkbox.checked);
84
+ }
85
+
86
+ @on('keydown', '.header-cell[data-sortable="true"]')
87
+ handleKeyDown(e: KeyboardEvent) {
88
+ if (e.key === 'Enter' || e.key === ' ') {
89
+ e.preventDefault();
90
+ const cell = e.currentTarget as HTMLElement;
91
+ const columnKey = cell.dataset.columnKey;
92
+
93
+ if (columnKey && this.sortable) {
94
+ this.dispatchSort(columnKey);
95
+ }
96
+ }
97
+ }
98
+
99
+ private renderSelectAllCheckbox(): string {
100
+ return `
101
+ <div class="header-cell header-cell--checkbox" part="checkbox-cell">
102
+ <input
103
+ type="checkbox"
104
+ class="select-all-checkbox"
105
+ ${this.allSelected ? 'checked' : ''}
106
+ title="Select all rows"
107
+ tabindex="0"
108
+ />
109
+ </div>
110
+ `;
111
+ }
112
+
113
+ private renderHeaderCells(): string {
114
+ if (!this.columns.length) {
115
+ return '';
116
+ }
117
+
118
+ return `
119
+ <div class="cells-container">
120
+ ${this.columns.map((column, index) => this.renderHeaderCell(column, index)).join('')}
121
+ </div>
122
+ `;
123
+ }
124
+
125
+ private renderHeaderCell(column: ColumnDefinition, index: number): string {
126
+ const isSortable = this.sortable && column.sortable !== false;
127
+ const isCurrentSort = this.currentSort.column === column.key;
128
+ const sortDirection = isCurrentSort ? this.currentSort.direction : null;
129
+
130
+ return `
131
+ <div
132
+ class="header-cell header-cell--${column.type || 'text'}"
133
+ part="cell"
134
+ data-column-key="${column.key}"
135
+ data-column-index="${index}"
136
+ data-sortable="${isSortable}"
137
+ style="${this.getHeaderCellStyles(column)}"
138
+ ${isSortable ? 'role="button" tabindex="0"' : ''}
139
+ ${isSortable ? `aria-label="Sort by ${column.label}"` : ''}
140
+ ${isCurrentSort ? `aria-sort="${this.getAriaSortValue(sortDirection)}"` : ''}
141
+ >
142
+ <div class="header-cell-content">
143
+ <span class="header-cell-label">${column.label}</span>
144
+ ${isSortable ? this.renderSortIndicator(sortDirection) : ''}
145
+ </div>
146
+ ${column.filterable ? this.renderFilterButton(column) : ''}
147
+ </div>
148
+ `;
149
+ }
150
+
151
+ private renderSortIndicator(direction: SortDirection): string {
152
+ const ascIcon = '▲';
153
+ const descIcon = '▼';
154
+ const neutralIcon = '⇅';
155
+
156
+ let icon = neutralIcon;
157
+ let state = 'neutral';
158
+
159
+ if (direction === 'asc') {
160
+ icon = ascIcon;
161
+ state = 'ascending';
162
+ } else if (direction === 'desc') {
163
+ icon = descIcon;
164
+ state = 'descending';
165
+ }
166
+
167
+ return `
168
+ <span
169
+ class="sort-indicator sort-indicator--${state}"
170
+ part="sort-indicator"
171
+ aria-hidden="true"
172
+ >${icon}</span>
173
+ `;
174
+ }
175
+
176
+ private renderFilterButton(column: ColumnDefinition): string {
177
+ return `
178
+ <button
179
+ class="filter-button"
180
+ part="filter-button"
181
+ data-column-key="${column.key}"
182
+ title="Filter ${column.label}"
183
+ tabindex="-1"
184
+ >
185
+ 🔽
186
+ </button>
187
+ `;
188
+ }
189
+
190
+ private getHeaderCellStyles(column: ColumnDefinition): string {
191
+ let styles = [];
192
+
193
+ if (column.width) {
194
+ styles.push(`width: ${column.width}`);
195
+ styles.push(`min-width: ${column.width}`);
196
+ styles.push(`max-width: ${column.width}`);
197
+ }
198
+
199
+ if (column.align) {
200
+ styles.push(`text-align: ${column.align}`);
201
+ }
202
+
203
+ return styles.join('; ');
204
+ }
205
+
206
+ private getAriaSortValue(direction: SortDirection): string {
207
+ switch (direction) {
208
+ case 'asc':
209
+ return 'ascending';
210
+ case 'desc':
211
+ return 'descending';
212
+ default:
213
+ return 'none';
214
+ }
215
+ }
216
+
217
+ @dispatch('@snice/header-sort', { bubbles: true, composed: true })
218
+ private dispatchSort(columnKey: string) {
219
+ return { column: columnKey };
220
+ }
221
+
222
+ @dispatch('@snice/header-select-all', { bubbles: true, composed: true })
223
+ private dispatchSelectAll(checked: boolean) {
224
+ return { checked };
225
+ }
226
+
227
+ @dispatch('@snice/header-filter', { bubbles: true, composed: true })
228
+ private dispatchFilter(columnKey: string) {
229
+ return { column: columnKey };
230
+ }
231
+
232
+ // Public API methods
233
+ setSortState(column: string, direction: SortDirection) {
234
+ this.currentSort = { column, direction };
235
+ }
236
+
237
+ setSelectionState(allSelected: boolean, someSelected: boolean = false) {
238
+ this.allSelected = allSelected;
239
+ this.someSelected = someSelected && !allSelected;
240
+ }
241
+
242
+ getColumnElement(columnKey: string): HTMLElement | null {
243
+ return this.shadowRoot?.querySelector(`[data-column-key="${columnKey}"]`) || null;
244
+ }
245
+
246
+ // Method to update column definitions
247
+ updateColumns(columns: ColumnDefinition[]) {
248
+ this.columns = columns;
249
+ }
250
+
251
+ // Method to highlight a column temporarily
252
+ highlightColumn(columnKey: string, duration: number = 2000) {
253
+ const columnElement = this.getColumnElement(columnKey);
254
+ if (columnElement) {
255
+ columnElement.classList.add('header-cell--highlighted');
256
+ setTimeout(() => {
257
+ columnElement.classList.remove('header-cell--highlighted');
258
+ }, duration);
259
+ }
260
+ }
261
+ }
@@ -0,0 +1,66 @@
1
+ import { element, property } from 'snice';
2
+
3
+ @element('snice-progress')
4
+ export class SniceProgress extends HTMLElement {
5
+ @property({ type: Number })
6
+ value = 0;
7
+
8
+ @property({ type: Number })
9
+ max = 100;
10
+
11
+ @property()
12
+ color = '#3b82f6';
13
+
14
+ @property()
15
+ backgroundColor = '#e5e7eb';
16
+
17
+ @property()
18
+ height = '0.5rem'; /* 8px */
19
+
20
+ @property({ type: Boolean })
21
+ showPercentage = false;
22
+
23
+ html() {
24
+ const percentage = Math.min(100, (this.value / this.max) * 100);
25
+
26
+ return `
27
+ <div class="progress-container">
28
+ <div class="progress-bar" style="width: ${percentage}%"></div>
29
+ </div>
30
+ ${this.showPercentage ? `
31
+ <span class="percentage">${percentage.toFixed(0)}%</span>
32
+ ` : ''}
33
+ `;
34
+ }
35
+
36
+ css() {
37
+ return `
38
+ :host {
39
+ display: inline-flex;
40
+ align-items: center;
41
+ width: 100%;
42
+ gap: 0.5rem; /* 8px */
43
+ }
44
+
45
+ .progress-container {
46
+ flex: 1;
47
+ height: ${this.height};
48
+ background: ${this.backgroundColor};
49
+ border-radius: 0.25rem; /* 4px */
50
+ overflow: hidden;
51
+ }
52
+
53
+ .progress-bar {
54
+ height: 100%;
55
+ background: ${this.color};
56
+ transition: width 0.3s ease;
57
+ }
58
+
59
+ .percentage {
60
+ font-size: 0.875rem; /* 14px */
61
+ color: #6b7280;
62
+ min-width: 2.1875rem; /* 35px */
63
+ }
64
+ `;
65
+ }
66
+ }
@@ -0,0 +1,45 @@
1
+ import { element, property } from 'snice';
2
+
3
+ @element('snice-rating')
4
+ export class SniceRating extends HTMLElement {
5
+ @property({ type: Number })
6
+ value = 0;
7
+
8
+ @property({ type: Number })
9
+ max = 5;
10
+
11
+ @property()
12
+ symbol = '★';
13
+
14
+ @property()
15
+ emptySymbol = '☆';
16
+
17
+ @property()
18
+ color = '#facc15';
19
+
20
+ html() {
21
+ const filled = Math.round(this.value);
22
+ const empty = this.max - filled;
23
+
24
+ return `
25
+ <span class="filled">${this.symbol.repeat(filled)}</span><span class="empty">${this.emptySymbol.repeat(empty)}</span>
26
+ `;
27
+ }
28
+
29
+ css() {
30
+ return `
31
+ :host {
32
+ display: inline-block;
33
+ font-size: 1.2em;
34
+ }
35
+
36
+ .filled {
37
+ color: ${this.color};
38
+ }
39
+
40
+ .empty {
41
+ color: #d1d5db;
42
+ }
43
+ `;
44
+ }
45
+ }
@@ -0,0 +1,255 @@
1
+ :host {
2
+ display: block;
3
+ width: 100%;
4
+ min-height: 2.5rem; /* 40px */
5
+ }
6
+
7
+ /* Row Container */
8
+ .row-container {
9
+ display: flex;
10
+ align-items: stretch;
11
+ width: 100%;
12
+ min-height: 2.5rem; /* 40px */
13
+ height: 2.5rem; /* 40px */
14
+ background: #ffffff;
15
+ border-bottom: 1px solid #ddd;
16
+ transition: background-color 0.15s ease;
17
+ }
18
+
19
+ /* Cell Base */
20
+ .cell {
21
+ display: flex;
22
+ align-items: center;
23
+ border-right: 1px solid #ddd;
24
+ min-height: 2.5rem; /* 40px */
25
+ height: 2.5rem; /* 40px */
26
+ min-width: 6.25rem; /* 100px */
27
+ flex: 1;
28
+ word-wrap: break-word;
29
+ overflow: hidden;
30
+ }
31
+
32
+ .cell:last-child {
33
+ border-right: none;
34
+ }
35
+
36
+ /* Checkbox Cell */
37
+ .cell--checkbox {
38
+ width: 3rem; /* 48px */
39
+ min-width: 3rem; /* 48px */
40
+ max-width: 3rem; /* 48px */
41
+ padding: 0.75rem; /* 12px */
42
+ justify-content: center;
43
+ }
44
+
45
+ .row-checkbox {
46
+ width: 1rem; /* 16px */
47
+ height: 1rem; /* 16px */
48
+ cursor: pointer;
49
+ }
50
+
51
+ /* Cells Container */
52
+ .cells-container {
53
+ display: flex;
54
+ flex: 1;
55
+ align-items: stretch;
56
+ }
57
+
58
+ /* Cell Type Specific Styles */
59
+ .cell--text {
60
+ text-align: left;
61
+ }
62
+
63
+ .cell--number,
64
+ .cell--currency,
65
+ .cell--percent,
66
+ .cell--accounting,
67
+ .cell--scientific,
68
+ .cell--fraction {
69
+ text-align: right;
70
+ font-variant-numeric: tabular-nums;
71
+ }
72
+
73
+ .cell--date {
74
+ min-width: 8rem; /* 128px */
75
+ font-variant-numeric: tabular-nums;
76
+ }
77
+
78
+ .cell--boolean {
79
+ text-align: center;
80
+ font-size: 1rem; /* 16px */
81
+ }
82
+
83
+ .cell--rating {
84
+ text-align: center;
85
+ min-width: 6rem; /* 96px */
86
+ }
87
+
88
+ .cell--progress {
89
+ min-width: 8rem; /* 128px */
90
+ }
91
+
92
+ .cell--sparkline {
93
+ text-align: center;
94
+ min-width: 6rem; /* 96px */
95
+ }
96
+
97
+ .cell--duration,
98
+ .cell--filesize {
99
+ text-align: right;
100
+ font-variant-numeric: tabular-nums;
101
+ }
102
+
103
+ /* Row States */
104
+ :host(.row--hoverable) .row-container:hover {
105
+ background-color: var(--snice-color-background-secondary);
106
+ }
107
+
108
+ :host(.row--clickable) .row-container {
109
+ cursor: pointer;
110
+ }
111
+
112
+ :host(.row--clickable) .row-container:hover {
113
+ background-color: var(--snice-color-background-tertiary);
114
+ }
115
+
116
+ :host(.row--selected) .row-container {
117
+ background-color: var(--snice-color-primary-light);
118
+ }
119
+
120
+ :host(.row--selected.row--hoverable) .row-container:hover {
121
+ background-color: var(--snice-color-primary-lighter);
122
+ }
123
+
124
+ /* Highlighted State */
125
+ :host(.row--highlighted) .row-container {
126
+ background-color: #fff3cd !important;
127
+ animation: row-highlight-fade 2s ease-out;
128
+ }
129
+
130
+ @keyframes row-highlight-fade {
131
+ 0% {
132
+ background-color: #ffeaa7;
133
+ }
134
+ 100% {
135
+ background-color: #fff3cd;
136
+ }
137
+ }
138
+
139
+ /* Focus States */
140
+ :host(:focus) .row-container {
141
+ outline: 2px solid #007bff;
142
+ outline-offset: -2px;
143
+ }
144
+
145
+ :host(.row--selectable) {
146
+ tabindex: 0;
147
+ }
148
+
149
+ /* Striped Rows (applied by parent table) */
150
+ :host(.row--striped) .row-container {
151
+ background-color: var(--snice-color-background-secondary);
152
+ }
153
+
154
+ /* Bordered Rows (applied by parent table) */
155
+ :host(.row--bordered) .row-container {
156
+ border-bottom: 1px solid var(--snice-color-border);
157
+ }
158
+
159
+ /* Size Variants */
160
+ :host(.row--small) .cell {
161
+ padding: 0.5rem 0.75rem; /* 8px 12px */
162
+ min-height: 2rem; /* 32px */
163
+ font-size: 0.75rem; /* 12px */
164
+ }
165
+
166
+ :host(.row--small) .row-container {
167
+ min-height: 2rem; /* 32px */
168
+ }
169
+
170
+ :host(.row--large) .cell {
171
+ padding: 1rem 1.25rem; /* 16px 20px */
172
+ min-height: 3.5rem; /* 56px */
173
+ font-size: 1rem; /* 16px */
174
+ }
175
+
176
+ :host(.row--large) .row-container {
177
+ min-height: 3.5rem; /* 56px */
178
+ }
179
+
180
+ /* Responsive Design */
181
+ @media (max-width: 768px) {
182
+ .cell {
183
+ padding: 0.5rem 0.75rem; /* 8px 12px */
184
+ font-size: 0.875rem; /* 14px */
185
+ }
186
+
187
+ .cell--checkbox {
188
+ width: 2.5rem; /* 40px */
189
+ min-width: 2.5rem; /* 40px */
190
+ max-width: 2.5rem; /* 40px */
191
+ padding: 0.5rem; /* 8px */
192
+ }
193
+
194
+ .row-checkbox {
195
+ width: 0.875rem; /* 14px */
196
+ height: 0.875rem; /* 14px */
197
+ }
198
+ }
199
+
200
+ /* Loading State */
201
+ :host(.row--loading) .row-container {
202
+ opacity: 0.6;
203
+ pointer-events: none;
204
+ }
205
+
206
+ :host(.row--loading) .row-container::after {
207
+ content: '';
208
+ position: absolute;
209
+ top: 0;
210
+ left: 0;
211
+ right: 0;
212
+ bottom: 0;
213
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent);
214
+ animation: loading-shimmer 1.5s infinite;
215
+ }
216
+
217
+ @keyframes loading-shimmer {
218
+ 0% {
219
+ transform: translateX(-100%);
220
+ }
221
+ 100% {
222
+ transform: translateX(100%);
223
+ }
224
+ }
225
+
226
+ /* Accessibility */
227
+ @media (prefers-reduced-motion: reduce) {
228
+ .row-container {
229
+ transition: none;
230
+ }
231
+
232
+ :host(.row--highlighted) .row-container {
233
+ animation: none;
234
+ background-color: #fff3cd !important;
235
+ }
236
+
237
+ :host(.row--loading) .row-container::after {
238
+ animation: none;
239
+ }
240
+ }
241
+
242
+ /* High Contrast Mode */
243
+ @media (prefers-contrast: high) {
244
+ .cell {
245
+ border-right: 1px solid;
246
+ }
247
+
248
+ .row-container {
249
+ border-bottom: 1px solid;
250
+ }
251
+
252
+ :host(.row--selected) .row-container {
253
+ border: 2px solid;
254
+ }
255
+ }