@smartnet360/svelte-components 0.0.37 → 0.0.39

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 (32) hide show
  1. package/dist/apps/site-check/SiteCheck.svelte +7 -6
  2. package/dist/apps/site-check/SiteCheck.svelte.d.ts +2 -1
  3. package/dist/apps/site-check/default-cell-styling.d.ts +6 -0
  4. package/dist/apps/site-check/default-cell-styling.js +24 -0
  5. package/dist/apps/site-check/default-cell-styling.json +20 -0
  6. package/dist/apps/site-check/helper.d.ts +3 -2
  7. package/dist/apps/site-check/helper.js +16 -15
  8. package/dist/apps/site-check/index.d.ts +2 -1
  9. package/dist/apps/site-check/index.js +4 -2
  10. package/dist/apps/site-check/transforms.d.ts +32 -0
  11. package/dist/apps/site-check/transforms.js +101 -3
  12. package/dist/core/Charts/ChartCard.svelte +7 -5
  13. package/dist/core/Charts/ChartCard.svelte.d.ts +3 -1
  14. package/dist/core/Charts/ChartComponent.svelte +3 -1
  15. package/dist/core/Charts/adapt.js +19 -27
  16. package/dist/core/Charts/charts.model.d.ts +9 -0
  17. package/dist/core/Charts/data-utils.d.ts +4 -4
  18. package/dist/core/Charts/data-utils.js +69 -10
  19. package/dist/core/Charts/index.d.ts +1 -1
  20. package/dist/core/Settings/FieldRenderer.svelte +234 -0
  21. package/dist/core/Settings/FieldRenderer.svelte.d.ts +30 -0
  22. package/dist/core/Settings/Settings.svelte +199 -0
  23. package/dist/core/Settings/Settings.svelte.d.ts +24 -0
  24. package/dist/core/Settings/index.d.ts +9 -0
  25. package/dist/core/Settings/index.js +8 -0
  26. package/dist/core/Settings/store.d.ts +56 -0
  27. package/dist/core/Settings/store.js +184 -0
  28. package/dist/core/Settings/types.d.ts +162 -0
  29. package/dist/core/Settings/types.js +7 -0
  30. package/dist/core/index.d.ts +1 -0
  31. package/dist/core/index.js +2 -0
  32. package/package.json +1 -1
@@ -11,6 +11,27 @@ const modernColors = [
11
11
  '#EC4899', // Pink
12
12
  '#6B7280' // Gray
13
13
  ];
14
+ /**
15
+ * Darken a hex color by a given amount
16
+ * @param color - Hex color string (e.g., '#3B82F6')
17
+ * @param amount - Amount to darken (0-1, where 0.2 = 20% darker)
18
+ * @returns Darkened hex color
19
+ */
20
+ function darkenColor(color, amount) {
21
+ // Remove # if present
22
+ const hex = color.replace('#', '');
23
+ // Parse RGB components
24
+ const r = parseInt(hex.substr(0, 2), 16);
25
+ const g = parseInt(hex.substr(2, 2), 16);
26
+ const b = parseInt(hex.substr(4, 2), 16);
27
+ // Darken each component
28
+ const newR = Math.round(r * (1 - amount));
29
+ const newG = Math.round(g * (1 - amount));
30
+ const newB = Math.round(b * (1 - amount));
31
+ // Convert back to hex
32
+ const toHex = (n) => n.toString(16).padStart(2, '0');
33
+ return `#${toHex(newR)}${toHex(newG)}${toHex(newB)}`;
34
+ }
14
35
  // Cache for processed KPI data to avoid reprocessing on every render
15
36
  const dataCache = new WeakMap();
16
37
  export function processKPIData(data, kpi) {
@@ -72,7 +93,7 @@ export function calculateMovingAverage(values, window) {
72
93
  maCache.set(cacheKey, result);
73
94
  return result;
74
95
  }
75
- export function createTimeSeriesTrace(values, timestamps, kpi, yaxis = 'y1', colorIndex = 0, chartType = 'line', stackGroup) {
96
+ export function createTimeSeriesTrace(values, timestamps, kpi, yaxis = 'y1', colorIndex = 0, chartType = 'line', stackGroup, coloredHover = true) {
76
97
  // Use KPI color if provided, otherwise cycle through modern colors
77
98
  const traceColor = kpi.color || modernColors[colorIndex % modernColors.length];
78
99
  // Base trace configuration
@@ -85,6 +106,18 @@ export function createTimeSeriesTrace(values, timestamps, kpi, yaxis = 'y1', col
85
106
  `Value: %{y:,.2f} ${kpi.unit}<br>` +
86
107
  '<extra></extra>'
87
108
  };
109
+ // Add colored hover styling if enabled
110
+ if (coloredHover) {
111
+ baseTrace.hoverlabel = {
112
+ bgcolor: traceColor,
113
+ bordercolor: traceColor,
114
+ font: {
115
+ color: '#ffffff', // White text for better contrast
116
+ family: 'Inter, Segoe UI, Tahoma, Geneva, Verdana, sans-serif',
117
+ size: 11
118
+ }
119
+ };
120
+ }
88
121
  // Configure based on chart type
89
122
  switch (chartType) {
90
123
  case 'stacked-area':
@@ -94,7 +127,10 @@ export function createTimeSeriesTrace(values, timestamps, kpi, yaxis = 'y1', col
94
127
  mode: 'lines',
95
128
  fill: 'tonexty',
96
129
  stackgroup: stackGroup || 'one',
97
- line: { width: 0.5, color: traceColor },
130
+ line: {
131
+ width: 1.5, // Visible border width
132
+ color: darkenColor(traceColor, 0.25) // 25% darker border for better separation
133
+ },
98
134
  fillcolor: traceColor
99
135
  };
100
136
  case 'stacked-percentage':
@@ -105,7 +141,10 @@ export function createTimeSeriesTrace(values, timestamps, kpi, yaxis = 'y1', col
105
141
  fill: 'tonexty',
106
142
  stackgroup: stackGroup || 'one',
107
143
  groupnorm: 'percent',
108
- line: { width: 0.5, color: traceColor },
144
+ line: {
145
+ width: 1.5, // Visible border width
146
+ color: darkenColor(traceColor, 0.25) // 25% darker border for better separation
147
+ },
109
148
  fillcolor: traceColor,
110
149
  hovertemplate: `<b>${kpi.name}</b><br>` +
111
150
  `Percentage: %{y:.1f}%<br>` +
@@ -138,7 +177,7 @@ export function createTimeSeriesTrace(values, timestamps, kpi, yaxis = 'y1', col
138
177
  width: 3,
139
178
  shape: 'spline',
140
179
  smoothing: 0.3,
141
- dash: yaxis === 'y1' ? 'solid' : 'dot'
180
+ dash: kpi.lineStyle || (yaxis === 'y1' ? 'solid' : 'dot')
142
181
  }
143
182
  };
144
183
  }
@@ -154,12 +193,12 @@ export function createTimeSeriesTrace(values, timestamps, kpi, yaxis = 'y1', col
154
193
  * @param stackGroup - Optional stack group identifier
155
194
  * @returns Array of traces (original + MA if configured)
156
195
  */
157
- export function createTimeSeriesTraceWithMA(values, timestamps, kpi, yaxis = 'y1', colorIndex = 0, chartType = 'line', stackGroup) {
196
+ export function createTimeSeriesTraceWithMA(values, timestamps, kpi, yaxis = 'y1', colorIndex = 0, chartType = 'line', stackGroup, coloredHover = true) {
158
197
  const traces = [];
159
198
  const traceColor = kpi.color || modernColors[colorIndex % modernColors.length];
160
199
  // Add original trace (unless explicitly disabled)
161
200
  if (!kpi.movingAverage || kpi.movingAverage.showOriginal !== false) {
162
- const originalTrace = createTimeSeriesTrace(values, timestamps, kpi, yaxis, colorIndex, chartType, stackGroup);
201
+ const originalTrace = createTimeSeriesTrace(values, timestamps, kpi, yaxis, colorIndex, chartType, stackGroup, coloredHover);
163
202
  // If MA is enabled, make the original line slightly transparent
164
203
  if (kpi.movingAverage?.enabled) {
165
204
  originalTrace.opacity = 0.4;
@@ -190,7 +229,18 @@ export function createTimeSeriesTraceWithMA(values, timestamps, kpi, yaxis = 'y1
190
229
  },
191
230
  hovertemplate: `<b>${maLabel}</b><br>` +
192
231
  `Value: %{y:,.2f} ${kpi.unit}<br>` +
193
- '<extra></extra>'
232
+ '<extra></extra>',
233
+ ...(coloredHover && {
234
+ hoverlabel: {
235
+ bgcolor: traceColor,
236
+ bordercolor: traceColor,
237
+ font: {
238
+ color: '#ffffff',
239
+ family: 'Inter, Segoe UI, Tahoma, Geneva, Verdana, sans-serif',
240
+ size: 11
241
+ }
242
+ }
243
+ })
194
244
  };
195
245
  traces.push(maTrace);
196
246
  }
@@ -209,7 +259,7 @@ export function formatValue(value, scale, unit) {
209
259
  }
210
260
  return `${value.toLocaleString()}${unit}`;
211
261
  }
212
- export function createDefaultPlotlyLayout(title) {
262
+ export function createDefaultPlotlyLayout(title, hoverMode, coloredHover = true) {
213
263
  return {
214
264
  title: title ? {
215
265
  text: title,
@@ -254,8 +304,17 @@ export function createDefaultPlotlyLayout(title) {
254
304
  paper_bgcolor: 'rgba(0,0,0,0)',
255
305
  plot_bgcolor: 'rgba(0,0,0,0)',
256
306
  font: { family: 'Inter, -apple-system, BlinkMacSystemFont, sans-serif' },
257
- hovermode: 'x',
258
- hoverlabel: {
307
+ hovermode: hoverMode !== undefined ? hoverMode : 'x',
308
+ hoverlabel: coloredHover ? {
309
+ // When coloredHover is enabled, let each trace control its own hover colors
310
+ font: {
311
+ family: 'Inter, Segoe UI, Tahoma, Geneva, Verdana, sans-serif',
312
+ size: 11
313
+ // color will be set per trace when coloredHover is true
314
+ }
315
+ // bgcolor and bordercolor will be set per trace when coloredHover is true
316
+ } : {
317
+ // Default hover styling when coloredHover is disabled
259
318
  font: {
260
319
  family: 'Inter, Segoe UI, Tahoma, Geneva, Verdana, sans-serif',
261
320
  size: 11,
@@ -1,6 +1,6 @@
1
1
  export { default as ChartComponent } from './ChartComponent.svelte';
2
2
  export { default as ChartCard } from './ChartCard.svelte';
3
- export type { Layout, Section, Chart, KPI, Mode, Scale, ChartMarker, ChartGrid, ChartPosition } from './charts.model.js';
3
+ export type { Layout, Section, Chart, KPI, Mode, Scale, ChartMarker, ChartGrid, ChartPosition, HoverMode, LineStyle, CellStylingConfig } from './charts.model.js';
4
4
  export { createTimeSeriesTrace, getYAxisTitle, formatValue, processKPIData, createDefaultPlotlyLayout } from './data-utils.js';
5
5
  export { adaptPlotlyLayout, getSizeCategory, createMarkerShapes, createMarkerAnnotations, addMarkersToLayout } from './adapt.js';
6
6
  export type { ContainerSize, ChartInfo, AdaptationConfig } from './adapt.js';
@@ -0,0 +1,234 @@
1
+ <script lang="ts">
2
+ import type { FieldDefinition } from './types';
3
+
4
+ /**
5
+ * Generic Field Renderer
6
+ *
7
+ * Renders a form field based on its type definition.
8
+ * Uses Bootstrap form components for styling.
9
+ * Works with hierarchical store structure.
10
+ */
11
+
12
+ export let field: FieldDefinition;
13
+ export let segmentId: string;
14
+ export let store: any; // The hierarchical settings store
15
+ export let value: any;
16
+
17
+ // Generate unique ID for form elements
18
+ const inputId = `field-${segmentId}-${field.id}`;
19
+
20
+ // Handle value changes - update the hierarchical store
21
+ function handleChange(newValue: any) {
22
+ store.update(segmentId, field.id, newValue);
23
+ }
24
+ </script>
25
+
26
+ <div class="field-wrapper mb-3">
27
+ {#if field.type === 'boolean'}
28
+ <div class="form-check form-switch">
29
+ <input
30
+ id={inputId}
31
+ type="checkbox"
32
+ class="form-check-input"
33
+ checked={value}
34
+ disabled={field.disabled}
35
+ on:change={(e) => handleChange(e.currentTarget.checked)}
36
+ />
37
+ <label class="form-check-label" for={inputId}>
38
+ {field.label}
39
+ </label>
40
+ {#if field.description}
41
+ <div class="form-text">{field.description}</div>
42
+ {/if}
43
+ </div>
44
+ {:else if field.type === 'text'}
45
+ <label for={inputId} class="form-label">
46
+ {field.label}
47
+ {#if field.tooltip}
48
+ <span class="text-muted" title={field.tooltip}>ⓘ</span>
49
+ {/if}
50
+ </label>
51
+ <input
52
+ id={inputId}
53
+ type="text"
54
+ class="form-control"
55
+ value={value}
56
+ placeholder={field.placeholder}
57
+ maxlength={field.maxLength}
58
+ pattern={field.pattern}
59
+ disabled={field.disabled}
60
+ on:input={(e) => handleChange(e.currentTarget.value)}
61
+ />
62
+ {#if field.description}
63
+ <div class="form-text">{field.description}</div>
64
+ {/if}
65
+ {:else if field.type === 'number'}
66
+ <label for={inputId} class="form-label">
67
+ {field.label}
68
+ {#if field.tooltip}
69
+ <span class="text-muted" title={field.tooltip}>ⓘ</span>
70
+ {/if}
71
+ </label>
72
+ <div class="input-group">
73
+ <input
74
+ id={inputId}
75
+ type="number"
76
+ class="form-control"
77
+ value={value}
78
+ min={field.min}
79
+ max={field.max}
80
+ step={field.step}
81
+ disabled={field.disabled}
82
+ on:input={(e) => handleChange(parseFloat(e.currentTarget.value))}
83
+ />
84
+ {#if field.unit}
85
+ <span class="input-group-text">{field.unit}</span>
86
+ {/if}
87
+ </div>
88
+ {#if field.description}
89
+ <div class="form-text">{field.description}</div>
90
+ {/if}
91
+ {:else if field.type === 'range'}
92
+ <label for={inputId} class="form-label">
93
+ {field.label}
94
+ {#if field.showValue !== false}
95
+ <span class="badge bg-secondary ms-2">{value}{field.unit || ''}</span>
96
+ {/if}
97
+ {#if field.tooltip}
98
+ <span class="text-muted" title={field.tooltip}>ⓘ</span>
99
+ {/if}
100
+ </label>
101
+ <input
102
+ id={inputId}
103
+ type="range"
104
+ class="form-range"
105
+ value={value}
106
+ min={field.min}
107
+ max={field.max}
108
+ step={field.step || 1}
109
+ disabled={field.disabled}
110
+ on:input={(e) => handleChange(parseFloat(e.currentTarget.value))}
111
+ />
112
+ {#if field.description}
113
+ <div class="form-text">{field.description}</div>
114
+ {/if}
115
+ {:else if field.type === 'color'}
116
+ <label for={inputId} class="form-label">
117
+ {field.label}
118
+ {#if field.tooltip}
119
+ <span class="text-muted" title={field.tooltip}>ⓘ</span>
120
+ {/if}
121
+ </label>
122
+ <div class="input-group" style="max-width: 200px;">
123
+ <input
124
+ id={inputId}
125
+ type="color"
126
+ class="form-control form-control-color"
127
+ value={value}
128
+ disabled={field.disabled}
129
+ on:input={(e) => handleChange(e.currentTarget.value)}
130
+ />
131
+ <input
132
+ type="text"
133
+ class="form-control"
134
+ value={value}
135
+ disabled={field.disabled}
136
+ on:input={(e) => handleChange(e.currentTarget.value)}
137
+ />
138
+ </div>
139
+ {#if field.description}
140
+ <div class="form-text">{field.description}</div>
141
+ {/if}
142
+ {:else if field.type === 'select'}
143
+ <label for={inputId} class="form-label">
144
+ {field.label}
145
+ {#if field.tooltip}
146
+ <span class="text-muted" title={field.tooltip}>ⓘ</span>
147
+ {/if}
148
+ </label>
149
+ <select
150
+ id={inputId}
151
+ class="form-select"
152
+ value={value}
153
+ disabled={field.disabled}
154
+ on:change={(e) => handleChange(e.currentTarget.value)}
155
+ >
156
+ {#each field.options as option}
157
+ <option value={option.value}>
158
+ {option.label}
159
+ </option>
160
+ {/each}
161
+ </select>
162
+ {#if field.description}
163
+ <div class="form-text">{field.description}</div>
164
+ {/if}
165
+ {:else if field.type === 'radio'}
166
+ <label class="form-label">
167
+ {field.label}
168
+ {#if field.tooltip}
169
+ <span class="text-muted" title={field.tooltip}>ⓘ</span>
170
+ {/if}
171
+ </label>
172
+ {#each field.options as option, index}
173
+ <div class="form-check">
174
+ <input
175
+ id={`${inputId}-${index}`}
176
+ type="radio"
177
+ class="form-check-input"
178
+ name={field.id}
179
+ value={option.value}
180
+ checked={value === option.value}
181
+ disabled={field.disabled}
182
+ on:change={() => handleChange(option.value)}
183
+ />
184
+ <label class="form-check-label" for={`${inputId}-${index}`}>
185
+ {option.label}
186
+ {#if option.description}
187
+ <small class="text-muted d-block">{option.description}</small>
188
+ {/if}
189
+ </label>
190
+ </div>
191
+ {/each}
192
+ {#if field.description}
193
+ <div class="form-text mt-2">{field.description}</div>
194
+ {/if}
195
+ {:else if field.type === 'custom'}
196
+ <label class="form-label">
197
+ {field.label}
198
+ {#if field.tooltip}
199
+ <span class="text-muted" title={field.tooltip}>ⓘ</span>
200
+ {/if}
201
+ </label>
202
+ <svelte:component
203
+ this={field.component}
204
+ {value}
205
+ disabled={field.disabled}
206
+ on:change={(e: CustomEvent) => handleChange(e.detail)}
207
+ {...field.componentProps}
208
+ />
209
+ {#if field.description}
210
+ <div class="form-text">{field.description}</div>
211
+ {/if}
212
+ {/if}
213
+ </div>
214
+
215
+ <style>
216
+ .field-wrapper {
217
+ animation: fadeIn 0.2s ease-in;
218
+ }
219
+
220
+ @keyframes fadeIn {
221
+ from {
222
+ opacity: 0;
223
+ transform: translateY(-5px);
224
+ }
225
+ to {
226
+ opacity: 1;
227
+ transform: translateY(0);
228
+ }
229
+ }
230
+
231
+ .text-muted {
232
+ cursor: help;
233
+ }
234
+ </style>
@@ -0,0 +1,30 @@
1
+ import type { FieldDefinition } from './types';
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: Props & {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const FieldRenderer: $$__sveltets_2_IsomorphicComponent<{
16
+ /**
17
+ * Generic Field Renderer
18
+ *
19
+ * Renders a form field based on its type definition.
20
+ * Uses Bootstrap form components for styling.
21
+ * Works with hierarchical store structure.
22
+ */ field: FieldDefinition;
23
+ segmentId: string;
24
+ store: any;
25
+ value: any;
26
+ }, {
27
+ [evt: string]: CustomEvent<any>;
28
+ }, {}, {}, string>;
29
+ type FieldRenderer = InstanceType<typeof FieldRenderer>;
30
+ export default FieldRenderer;
@@ -0,0 +1,199 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import type { SettingsSchema } from './types';
4
+ import { createSettingsStore } from './store';
5
+ import FieldRenderer from './FieldRenderer.svelte';
6
+
7
+ /**
8
+ * Settings Component
9
+ *
10
+ * Reusable settings panel that accepts a schema and renders
11
+ * a data-driven UI with automatic persistence.
12
+ * Provides hierarchical access: $settings.segment.field
13
+ *
14
+ * @example
15
+ * ```svelte
16
+ * const settings = createSettingsStore(mySchema, 'myApp');
17
+ * <Settings schema={mySchema} {settings} />
18
+ *
19
+ * // Access settings in parent:
20
+ * $settings.appearance.theme
21
+ * $settings.editor.fontSize
22
+ * ```
23
+ */
24
+
25
+ /** The settings schema defining all segments and fields */
26
+ export let schema: SettingsSchema;
27
+
28
+ /** The settings store instance (create externally for access in parent) */
29
+ export let settings: ReturnType<typeof createSettingsStore>;
30
+
31
+ /** Optional: Callback when settings change */
32
+ export let onChange: ((values: any) => void) | undefined = undefined;
33
+
34
+ // Current values (hierarchical structure: { segment: { field: value } })
35
+ let currentValues: Record<string, Record<string, any>> = {};
36
+
37
+ // Track collapsed state of segments
38
+ let collapsedSegments = new Map<string, boolean>();
39
+
40
+ onMount(() => {
41
+ // Initialize collapsed states from schema
42
+ schema.segments.forEach((segment) => {
43
+ collapsedSegments.set(segment.id, segment.collapsed || false);
44
+ });
45
+
46
+ // Subscribe to store changes
47
+ const unsubscribe = settings.subscribe((values) => {
48
+ currentValues = values;
49
+ if (onChange) {
50
+ onChange(values);
51
+ }
52
+ });
53
+
54
+ return unsubscribe;
55
+ });
56
+
57
+ function toggleSegment(segmentId: string) {
58
+ const current = collapsedSegments.get(segmentId) || false;
59
+ collapsedSegments.set(segmentId, !current);
60
+ collapsedSegments = collapsedSegments; // Trigger reactivity
61
+ }
62
+
63
+ function resetSegmentHandler(segmentId: string) {
64
+ if (confirm(`Reset "${segmentId}" settings to defaults?`)) {
65
+ settings.resetSegment(segmentId);
66
+ }
67
+ }
68
+
69
+ function resetAll() {
70
+ if (confirm('Reset all settings to defaults?')) {
71
+ settings.resetAll();
72
+ }
73
+ }
74
+
75
+ // Check if a field should be visible based on visibleIf condition
76
+ function isFieldVisible(field: any, segmentId: string): boolean {
77
+ if (!field.visibleIf) return true;
78
+ // Pass the entire hierarchical structure to visibleIf
79
+ return field.visibleIf(currentValues);
80
+ }
81
+ </script>
82
+
83
+ <div class="settings-container">
84
+ <!-- Header -->
85
+ <div class="settings-header mb-4">
86
+ <div class="d-flex justify-content-between align-items-center">
87
+ <div>
88
+ <h2 class="mb-1">Settings</h2>
89
+ {#if schema.version}
90
+ <small class="text-muted">Schema v{schema.version}</small>
91
+ {/if}
92
+ </div>
93
+ <button class="btn btn-outline-secondary btn-sm" on:click={resetAll}>
94
+ Reset All
95
+ </button>
96
+ </div>
97
+ </div>
98
+
99
+ <!-- Settings Cards -->
100
+ <div class="settings-grid">
101
+ {#each schema.segments as segment (segment.id)}
102
+ <div class="card settings-card">
103
+ <!-- Card Header -->
104
+ <div class="card-header">
105
+ <div class="d-flex justify-content-between align-items-center">
106
+ <div class="d-flex align-items-center gap-2">
107
+ {#if segment.icon}
108
+ <span class="segment-icon">{segment.icon}</span>
109
+ {/if}
110
+ <div>
111
+ <h5 class="mb-0">{segment.title}</h5>
112
+ {#if segment.description}
113
+ <small class="text-muted">{segment.description}</small>
114
+ {/if}
115
+ </div>
116
+ </div>
117
+ <div class="d-flex gap-2">
118
+ <button
119
+ class="btn btn-sm btn-link text-decoration-none p-0"
120
+ on:click={() => resetSegmentHandler(segment.id)}
121
+ title="Reset to defaults"
122
+ >
123
+
124
+ </button>
125
+ <button
126
+ class="btn btn-sm btn-link text-decoration-none p-0"
127
+ on:click={() => toggleSegment(segment.id)}
128
+ title={collapsedSegments.get(segment.id) ? 'Expand' : 'Collapse'}
129
+ >
130
+ {collapsedSegments.get(segment.id) ? '▶' : '▼'}
131
+ </button>
132
+ </div>
133
+ </div>
134
+ </div>
135
+
136
+ <!-- Card Body -->
137
+ {#if !collapsedSegments.get(segment.id)}
138
+ <div class="card-body">
139
+ {#each segment.fields as field (field.id)}
140
+ {#if isFieldVisible(field, segment.id)}
141
+ <FieldRenderer
142
+ {field}
143
+ segmentId={segment.id}
144
+ store={settings}
145
+ value={currentValues[segment.id]?.[field.id]}
146
+ />
147
+ {/if}
148
+ {/each}
149
+ </div>
150
+ {/if}
151
+ </div>
152
+ {/each}
153
+ </div>
154
+ </div>
155
+
156
+ <style>
157
+ .settings-container {
158
+ max-width: 1200px;
159
+ margin: 0 auto;
160
+ padding: 1rem;
161
+ }
162
+
163
+ .settings-grid {
164
+ display: grid;
165
+ grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
166
+ gap: 1.5rem;
167
+ }
168
+
169
+ .settings-card {
170
+ border: 1px solid var(--bs-border-color);
171
+ border-radius: 0.5rem;
172
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
173
+ transition: box-shadow 0.2s ease;
174
+ }
175
+
176
+ .settings-card:hover {
177
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
178
+ }
179
+
180
+ .card-header {
181
+ background-color: var(--bs-light);
182
+ border-bottom: 1px solid var(--bs-border-color);
183
+ padding: 1rem;
184
+ }
185
+
186
+ .segment-icon {
187
+ font-size: 1.5rem;
188
+ }
189
+
190
+ .card-body {
191
+ padding: 1.5rem;
192
+ }
193
+
194
+ @media (max-width: 768px) {
195
+ .settings-grid {
196
+ grid-template-columns: 1fr;
197
+ }
198
+ }
199
+ </style>
@@ -0,0 +1,24 @@
1
+ import type { SettingsSchema } from './types';
2
+ import { createSettingsStore } from './store';
3
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
4
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
5
+ $$bindings?: Bindings;
6
+ } & Exports;
7
+ (internal: unknown, props: Props & {
8
+ $$events?: Events;
9
+ $$slots?: Slots;
10
+ }): Exports & {
11
+ $set?: any;
12
+ $on?: any;
13
+ };
14
+ z_$$bindings?: Bindings;
15
+ }
16
+ declare const Settings: $$__sveltets_2_IsomorphicComponent<{
17
+ /** The settings schema defining all segments and fields */ schema: SettingsSchema;
18
+ /** The settings store instance (create externally for access in parent) */ settings: ReturnType<typeof createSettingsStore>;
19
+ /** Optional: Callback when settings change */ onChange?: ((values: any) => void) | undefined;
20
+ }, {
21
+ [evt: string]: CustomEvent<any>;
22
+ }, {}, {}, string>;
23
+ type Settings = InstanceType<typeof Settings>;
24
+ export default Settings;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Settings System - Public API
3
+ *
4
+ * A data-driven, reusable settings component for SvelteKit applications.
5
+ * Supports hierarchical access with TypeScript autocomplete.
6
+ */
7
+ export { default as Settings } from './Settings.svelte';
8
+ export { createSettingsStore, createFieldMap, type InferSettingsType } from './store';
9
+ export type { SettingsSchema, SettingsStore, SettingsValues, SegmentDefinition, FieldDefinition, BaseFieldDefinition, BooleanFieldDefinition, TextFieldDefinition, NumberFieldDefinition, RangeFieldDefinition, ColorFieldDefinition, SelectFieldDefinition, RadioFieldDefinition, CustomFieldDefinition } from './types';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Settings System - Public API
3
+ *
4
+ * A data-driven, reusable settings component for SvelteKit applications.
5
+ * Supports hierarchical access with TypeScript autocomplete.
6
+ */
7
+ export { default as Settings } from './Settings.svelte';
8
+ export { createSettingsStore, createFieldMap } from './store';