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,82 @@
1
+ import { element, property, watch, ready, query } from 'snice';
2
+ import css from './snice-cell.css?inline';
3
+ import type { RatingFormat, SniceCellElement, ColumnType, ColumnAlign, ColumnDefinition } from './snice-table.types';
4
+ import './snice-rating';
5
+
6
+ @element('snice-cell-rating')
7
+ export class SniceCellRating extends HTMLElement implements SniceCellElement {
8
+ @property({ reflect: true })
9
+ align: ColumnAlign = 'center';
10
+
11
+ @property({ reflect: true })
12
+ type: ColumnType = 'rating';
13
+
14
+ @property({ reflect: true })
15
+ value: any = 0;
16
+
17
+ @property({ type: Object })
18
+ column: ColumnDefinition = {
19
+ key: '',
20
+ label: '',
21
+ type: 'rating',
22
+ align: 'center'
23
+ };
24
+
25
+ @property({ type: Object })
26
+ rowData: any = null;
27
+
28
+ @query('.cell-content')
29
+ contentElement?: HTMLElement;
30
+
31
+ html() {
32
+ return `
33
+ <div class="cell-content cell-content--rating" part="content">
34
+ <!-- Rating component will be created here -->
35
+ </div>
36
+ `;
37
+ }
38
+
39
+ css() {
40
+ return css;
41
+ }
42
+
43
+ @ready()
44
+ init() {
45
+ this.applyAlignment();
46
+ this.createRatingElement();
47
+ }
48
+
49
+ private applyAlignment() {
50
+ this.style.textAlign = this.align;
51
+ }
52
+
53
+ @watch('align')
54
+ updateAlignment() {
55
+ this.applyAlignment();
56
+ }
57
+
58
+ @watch('value', 'column')
59
+ updateContent() {
60
+ this.createRatingElement();
61
+ }
62
+
63
+ private createRatingElement() {
64
+ if (!this.contentElement) return;
65
+
66
+ // Clear existing content
67
+ this.contentElement.innerHTML = '';
68
+
69
+ // Create rating element
70
+ const rating = document.createElement('snice-rating') as any;
71
+ rating.value = Number(this.value) || 0;
72
+
73
+ // Apply format from column
74
+ const format: RatingFormat = this.column.ratingFormat || {};
75
+ rating.max = format.max ?? 5;
76
+ rating.symbol = format.symbol ?? '★';
77
+ rating.emptySymbol = format.emptySymbol ?? '☆';
78
+ rating.color = format.color ?? '#facc15';
79
+
80
+ this.contentElement.appendChild(rating);
81
+ }
82
+ }
@@ -0,0 +1,253 @@
1
+ import { element, property, watch, ready, query, SimpleArray } from 'snice';
2
+ import css from './snice-cell.css?inline';
3
+ import type { SparklineFormat, SniceCellElement, ColumnType, ColumnAlign, ColumnDefinition } from './snice-table.types';
4
+
5
+ @element('snice-cell-sparkline')
6
+ export class SniceCellSparkline extends HTMLElement implements SniceCellElement {
7
+ @property({ reflect: true })
8
+ align: ColumnAlign = 'left';
9
+
10
+ @property({ reflect: true })
11
+ type: ColumnType = 'sparkline';
12
+
13
+ @property({ reflect: true })
14
+ value: any = '';
15
+
16
+ @property({ type: Object })
17
+ column: ColumnDefinition = {
18
+ key: '',
19
+ label: '',
20
+ type: 'sparkline',
21
+ align: 'left'
22
+ };
23
+
24
+ @property({ type: Object })
25
+ rowData: any = null;
26
+
27
+ @query('.cell-content')
28
+ contentElement?: HTMLElement;
29
+
30
+ @property({ reflect: true, attribute: 'chart-type' })
31
+ chartType: 'line' | 'bar' | 'area' = 'line';
32
+
33
+ @property({ reflect: true })
34
+ color: string = 'var(--snice-color-primary)';
35
+
36
+ @property({ type: Number, reflect: true })
37
+ width: number = 80;
38
+
39
+ @property({ type: Number, reflect: true })
40
+ height: number = 24;
41
+
42
+ @property({ type: Boolean, reflect: true, attribute: 'show-dots' })
43
+ showDots: boolean = false;
44
+
45
+ @property({ type: Boolean, reflect: true, attribute: 'show-baseline' })
46
+ showBaseline: boolean = false;
47
+
48
+ @property({ type: Number, reflect: true, attribute: 'stroke-width' })
49
+ strokeWidth: number = 1.5;
50
+
51
+ @property({ type: Number, reflect: true, attribute: 'min-value' })
52
+ minValue?: number;
53
+
54
+ @property({ type: Number, reflect: true, attribute: 'max-value' })
55
+ maxValue?: number;
56
+
57
+ @property({ type: SimpleArray, reflect: true })
58
+ data: number[] = [];
59
+
60
+ html() {
61
+ return `
62
+ <div class="cell-content cell-content--sparkline" part="content">
63
+ ${this.renderSparkline()}
64
+ </div>
65
+ `;
66
+ }
67
+
68
+ css() {
69
+ return css;
70
+ }
71
+
72
+ @ready()
73
+ init() {
74
+ this.applyAlignment();
75
+ this.updateSparkline();
76
+ }
77
+
78
+ private applyAlignment() {
79
+ this.style.textAlign = this.align;
80
+ }
81
+
82
+ @watch('align')
83
+ updateAlignment() {
84
+ this.applyAlignment();
85
+ }
86
+
87
+ @watch('value', 'data', 'column')
88
+ updateContent() {
89
+ if (this.contentElement) {
90
+ this.contentElement.innerHTML = this.renderSparkline();
91
+ }
92
+ }
93
+
94
+ @watch('value', 'data', 'chartType', 'color', 'width', 'height', 'showDots', 'showBaseline', 'strokeWidth', 'minValue', 'maxValue')
95
+ updateSparkline() {
96
+ if (this.contentElement) {
97
+ this.contentElement.innerHTML = this.renderSparkline();
98
+ }
99
+ }
100
+
101
+ private renderSparkline(): string {
102
+ if ((this.value === null || this.value === undefined) && (!this.data || this.data.length === 0)) {
103
+ return '';
104
+ }
105
+
106
+ // Use custom formatter if provided
107
+ if (this.column.formatter) {
108
+ return this.column.formatter(this.value, this.rowData);
109
+ }
110
+
111
+ const data = this.parseData();
112
+ if (data.length === 0) {
113
+ return '<span style="color: #6c757d; font-style: italic;">No data</span>';
114
+ }
115
+
116
+ // Use column sparkline format or component properties
117
+ const format: SparklineFormat = this.column.sparklineFormat || {
118
+ type: this.chartType,
119
+ color: this.color,
120
+ width: this.width,
121
+ height: this.height
122
+ };
123
+
124
+ const width = format.width ?? this.width;
125
+ const height = format.height ?? this.height;
126
+ const color = format.color ?? this.color;
127
+ const type = format.type ?? this.chartType;
128
+
129
+ return this.createCanvas(data, width, height, color, type);
130
+ }
131
+
132
+ private parseData(): number[] {
133
+ // First, try the dedicated data property (using SimpleArray)
134
+ if (this.data && Array.isArray(this.data) && this.data.length > 0) {
135
+ return this.data.map(v => Number(v)).filter(n => !isNaN(n));
136
+ }
137
+
138
+ // Fallback to value property for backwards compatibility
139
+ if (Array.isArray(this.value)) {
140
+ return this.value.map(v => Number(v)).filter(n => !isNaN(n));
141
+ }
142
+
143
+ if (typeof this.value === 'string') {
144
+ // Try to parse as JSON array first (e.g., "[1,2,3]")
145
+ if (this.value.startsWith('[') && this.value.endsWith(']')) {
146
+ try {
147
+ const parsed = JSON.parse(this.value);
148
+ if (Array.isArray(parsed)) {
149
+ return parsed.map(v => Number(v)).filter(n => !isNaN(n));
150
+ }
151
+ } catch {
152
+ // Fall through to comma-separated parsing
153
+ }
154
+ }
155
+
156
+ // Try to parse comma-separated values
157
+ try {
158
+ return this.value.split(',').map(v => Number(v.trim())).filter(n => !isNaN(n));
159
+ } catch {
160
+ return [];
161
+ }
162
+ }
163
+
164
+ return [];
165
+ }
166
+
167
+ private createCanvas(data: number[], width: number, height: number, color: string, type: string): string {
168
+ if (data.length === 0) return '';
169
+
170
+ // Create a canvas element
171
+ const canvas = document.createElement('canvas');
172
+ canvas.width = width;
173
+ canvas.height = height;
174
+ const ctx = canvas.getContext('2d');
175
+
176
+ if (!ctx) return '<div style="color: red;">Canvas not supported</div>';
177
+
178
+ const padding = 2;
179
+ const chartWidth = width - (padding * 2);
180
+ const chartHeight = height - (padding * 2);
181
+
182
+ const min = this.minValue ?? Math.min(...data);
183
+ const max = this.maxValue ?? Math.max(...data);
184
+ const range = max - min || 1;
185
+
186
+ // Clear canvas
187
+ ctx.clearRect(0, 0, width, height);
188
+
189
+ // Set color and line properties
190
+ ctx.strokeStyle = color;
191
+ ctx.fillStyle = color;
192
+ ctx.lineWidth = this.strokeWidth;
193
+ ctx.lineCap = 'round';
194
+ ctx.lineJoin = 'round';
195
+
196
+ if (type === 'line' || type === 'area') {
197
+ const points = data.map((val, i) => {
198
+ const x = padding + (i / (data.length - 1)) * chartWidth;
199
+ const y = padding + chartHeight - ((val - min) / range) * chartHeight;
200
+ return [x, y];
201
+ });
202
+
203
+ // Draw area fill if needed
204
+ if (type === 'area') {
205
+ ctx.beginPath();
206
+ ctx.moveTo(points[0][0], points[0][1]);
207
+ points.forEach(([x, y]) => ctx.lineTo(x, y));
208
+ ctx.lineTo(points[points.length - 1][0], height - padding);
209
+ ctx.lineTo(points[0][0], height - padding);
210
+ ctx.closePath();
211
+ ctx.globalAlpha = 0.3;
212
+ ctx.fill();
213
+ ctx.globalAlpha = 1;
214
+ }
215
+
216
+ // Draw line
217
+ ctx.beginPath();
218
+ ctx.moveTo(points[0][0], points[0][1]);
219
+ points.forEach(([x, y]) => ctx.lineTo(x, y));
220
+ ctx.stroke();
221
+
222
+ // Draw dots if enabled
223
+ if (this.showDots) {
224
+ points.forEach(([x, y]) => {
225
+ ctx.beginPath();
226
+ ctx.arc(x, y, 1.5, 0, 2 * Math.PI);
227
+ ctx.fill();
228
+ });
229
+ }
230
+
231
+ } else if (type === 'bar') {
232
+ const barWidth = (chartWidth / data.length) * 0.8;
233
+ const barSpacing = (chartWidth / data.length) * 0.2;
234
+
235
+ data.forEach((val, i) => {
236
+ const barHeight = ((val - min) / range) * chartHeight;
237
+ const x = padding + i * (barWidth + barSpacing);
238
+ const y = padding + chartHeight - barHeight;
239
+
240
+ ctx.fillRect(x, y, barWidth, barHeight);
241
+ });
242
+ }
243
+
244
+ // Convert canvas to data URL
245
+ try {
246
+ const dataUrl = canvas.toDataURL('image/png');
247
+ return `<img src="${dataUrl}" width="${width}" height="${height}" alt="Sparkline chart" style="display: block; vertical-align: middle;">`;
248
+ } catch (error) {
249
+ console.error('Error creating canvas image:', error);
250
+ return `<div style="color: red;">Canvas error</div>`;
251
+ }
252
+ }
253
+ }
@@ -0,0 +1,125 @@
1
+ import { element, property, watch, ready, query } from 'snice';
2
+ import css from './snice-cell.css?inline';
3
+ import type { SniceCellElement, ColumnType, ColumnAlign, ColumnDefinition } from './snice-table.types';
4
+
5
+ @element('snice-cell-text')
6
+ export class SniceCellText extends HTMLElement implements SniceCellElement {
7
+ @property({ reflect: true })
8
+ align: ColumnAlign = 'left';
9
+
10
+ @property({ reflect: true })
11
+ type: ColumnType = 'text';
12
+
13
+ @property({ reflect: true })
14
+ value: any = '';
15
+
16
+ @property({ type: Object })
17
+ column: ColumnDefinition = {
18
+ key: '',
19
+ label: '',
20
+ type: 'text',
21
+ align: 'left'
22
+ };
23
+
24
+ @property({ type: Object })
25
+ rowData: any = null;
26
+
27
+ @query('.cell-content')
28
+ contentElement?: HTMLElement;
29
+
30
+ @property({ type: Boolean, reflect: true })
31
+ multiline: boolean = false;
32
+
33
+ @property({ type: Number, attribute: 'max-lines' })
34
+ maxLines?: number;
35
+
36
+ html() {
37
+ const content = this.formatTextContent();
38
+ const styles = this.getTextStyles();
39
+
40
+ return `
41
+ <div class="cell-content cell-content--text" part="content" style="${styles}">
42
+ ${content}
43
+ </div>
44
+ `;
45
+ }
46
+
47
+ css() {
48
+ return css;
49
+ }
50
+
51
+ @ready()
52
+ init() {
53
+ this.applyAlignment();
54
+ }
55
+
56
+ private applyAlignment() {
57
+ this.style.textAlign = this.align;
58
+ }
59
+
60
+ @watch('align')
61
+ updateAlignment() {
62
+ this.applyAlignment();
63
+ }
64
+
65
+ @watch('value', 'column')
66
+ updateContent() {
67
+ if (this.contentElement) {
68
+ const content = this.formatTextContent();
69
+ const styles = this.getTextStyles();
70
+ this.contentElement.innerHTML = content;
71
+ this.contentElement.setAttribute('style', styles);
72
+ }
73
+ }
74
+
75
+ private formatTextContent(): string {
76
+ if (this.value === null || this.value === undefined) {
77
+ return '';
78
+ }
79
+
80
+ let text = String(this.value);
81
+
82
+ // Apply custom formatter if provided
83
+ if (this.column.formatter) {
84
+ text = this.column.formatter(this.value, this.rowData);
85
+ }
86
+
87
+ // Handle multiline text
88
+ if (this.multiline) {
89
+ text = text.replace(/\n/g, '<br>');
90
+ }
91
+
92
+ // Handle ellipsis for single line
93
+ if (!this.multiline && this.column.ellipsis) {
94
+ // CSS will handle the ellipsis
95
+ }
96
+
97
+ return text;
98
+ }
99
+
100
+ private getTextStyles(): string {
101
+ let styles = [];
102
+
103
+ if (!this.multiline) {
104
+ styles.push('white-space: nowrap');
105
+ if (this.column.ellipsis) {
106
+ styles.push('overflow: hidden');
107
+ styles.push('text-overflow: ellipsis');
108
+ }
109
+ } else {
110
+ if (this.column.wrap) {
111
+ styles.push('white-space: pre-wrap');
112
+ styles.push('word-wrap: break-word');
113
+ }
114
+
115
+ if (this.maxLines) {
116
+ styles.push('display: -webkit-box');
117
+ styles.push('-webkit-box-orient: vertical');
118
+ styles.push(`-webkit-line-clamp: ${this.maxLines}`);
119
+ styles.push('overflow: hidden');
120
+ }
121
+ }
122
+
123
+ return styles.join('; ');
124
+ }
125
+ }