@happyvertical/smrt-products 0.34.5 → 0.34.7

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.
@@ -1,5 +1,7 @@
1
1
  <script lang="ts">
2
+ import { Form, Input, Textarea } from '@happyvertical/smrt-ui/forms';
2
3
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
4
+ import { Button } from '@happyvertical/smrt-ui/ui';
3
5
  import { M } from '../i18n.js';
4
6
  import type { ProductData } from '../types';
5
7
 
@@ -65,128 +67,126 @@ function handleSubmit(event: Event) {
65
67
  }
66
68
  </script>
67
69
 
68
- <form onsubmit={handleSubmit} class="product-form">
69
- <div class="form-group">
70
- <label for="name">{t(M['products.product_form.name_label'])}</label>
71
- <input
72
- id="name"
73
- type="text"
74
- bind:value={formData.name}
75
- disabled={loading}
76
- class="form-input"
77
- class:error={errors.name}
78
- placeholder={t(M['products.product_form.name_placeholder'])}
79
- />
80
- {#if errors.name}
81
- <span class="error-message">{errors.name}</span>
82
- {/if}
83
- </div>
84
-
85
- <div class="form-group">
86
- <label for="description">Description</label>
87
- <textarea
88
- id="description"
89
- bind:value={formData.description}
90
- disabled={loading}
91
- class="form-textarea"
92
- placeholder={t(M['products.product_form.description_placeholder'])}
93
- rows="3"
94
- ></textarea>
95
- </div>
96
-
97
- <div class="form-row">
70
+ <div class="product-form-shell">
71
+ <Form onsubmit={handleSubmit} class="product-form">
98
72
  <div class="form-group">
99
- <label for="price">Price *</label>
100
- <input
101
- id="price"
102
- type="number"
103
- step="0.01"
104
- min="0"
105
- bind:value={formData.price}
73
+ <label for="name">{t(M['products.product_form.name_label'])}</label>
74
+ <Input
75
+ id="name"
76
+ type="text"
77
+ bind:value={formData.name}
106
78
  disabled={loading}
107
- class="form-input"
108
- class:error={errors.price}
109
- placeholder="0.00"
79
+ class={errors.name ? 'error' : ''}
80
+ placeholder={t(M['products.product_form.name_placeholder'])}
110
81
  />
111
- {#if errors.price}
112
- <span class="error-message">{errors.price}</span>
82
+ {#if errors.name}
83
+ <span class="error-message">{errors.name}</span>
113
84
  {/if}
114
85
  </div>
115
86
 
116
87
  <div class="form-group">
117
- <label for="category">Category</label>
118
- <input
119
- id="category"
120
- type="text"
121
- bind:value={formData.category}
88
+ <label for="description">Description</label>
89
+ <Textarea
90
+ id="description"
91
+ bind:value={formData.description}
122
92
  disabled={loading}
123
- class="form-input"
124
- placeholder={t(M['products.product_form.category_placeholder'])}
125
- />
93
+ placeholder={t(M['products.product_form.description_placeholder'])}
94
+ rows={3}
95
+ ></Textarea>
126
96
  </div>
127
- </div>
128
97
 
129
- <div class="form-group">
130
- <label for="tags">Tags</label>
131
- <input
132
- id="tags"
133
- type="text"
134
- bind:value={formData.tags}
135
- disabled={loading}
136
- class="form-input"
137
- placeholder={t(M['products.product_form.tags_placeholder'])}
138
- />
139
- <small class="form-hint">{t(M['products.product_form.tags_hint'])}</small>
140
- </div>
98
+ <div class="form-row">
99
+ <div class="form-group">
100
+ <label for="price">Price *</label>
101
+ <Input
102
+ id="price"
103
+ type="number"
104
+ step="0.01"
105
+ min="0"
106
+ bind:value={formData.price}
107
+ disabled={loading}
108
+ class={errors.price ? 'error' : ''}
109
+ placeholder="0.00"
110
+ />
111
+ {#if errors.price}
112
+ <span class="error-message">{errors.price}</span>
113
+ {/if}
114
+ </div>
115
+
116
+ <div class="form-group">
117
+ <label for="category">Category</label>
118
+ <Input
119
+ id="category"
120
+ type="text"
121
+ bind:value={formData.category}
122
+ disabled={loading}
123
+ placeholder={t(M['products.product_form.category_placeholder'])}
124
+ />
125
+ </div>
126
+ </div>
141
127
 
142
- <div class="form-group">
143
- <label class="checkbox-label">
144
- <input
145
- type="checkbox"
146
- bind:checked={formData.inStock}
128
+ <div class="form-group">
129
+ <label for="tags">Tags</label>
130
+ <Input
131
+ id="tags"
132
+ type="text"
133
+ bind:value={formData.tags}
147
134
  disabled={loading}
148
- class="form-checkbox"
135
+ placeholder={t(M['products.product_form.tags_placeholder'])}
149
136
  />
150
- {t(M['products.product_form.in_stock_label'])}
151
- </label>
152
- </div>
137
+ <small class="form-hint">{t(M['products.product_form.tags_hint'])}</small>
138
+ </div>
139
+
140
+ <div class="form-group">
141
+ <label class="checkbox-label">
142
+ <!-- raw-primitive-allow: native checkbox; no Provider-free checkbox primitive (Toggle is a switch with different semantics, CheckboxInput requires a Provider) -->
143
+ <input
144
+ type="checkbox"
145
+ bind:checked={formData.inStock}
146
+ disabled={loading}
147
+ class="form-checkbox"
148
+ />
149
+ {t(M['products.product_form.in_stock_label'])}
150
+ </label>
151
+ </div>
153
152
 
154
- <div class="form-actions">
155
- {#if onCancel}
156
- <button type="button" onclick={onCancel} disabled={loading} class="cancel-btn">
157
- Cancel
158
- </button>
159
- {/if}
160
-
161
- <button type="submit" disabled={loading} class="submit-btn">
162
- {#if loading}
163
- Saving...
164
- {:else}
165
- {product.id ? 'Update Product' : 'Create Product'}
153
+ <div class="form-actions">
154
+ {#if onCancel}
155
+ <Button type="button" variant="secondary" onclick={onCancel} disabled={loading}>
156
+ Cancel
157
+ </Button>
166
158
  {/if}
167
- </button>
168
- </div>
169
- </form>
159
+
160
+ <Button type="submit" variant="primary" disabled={loading}>
161
+ {#if loading}
162
+ Saving...
163
+ {:else}
164
+ {product.id ? 'Update Product' : 'Create Product'}
165
+ {/if}
166
+ </Button>
167
+ </div>
168
+ </Form>
169
+ </div>
170
170
 
171
171
  <style>
172
- .product-form {
172
+ .product-form-shell :global(.product-form) {
173
173
  max-width: 500px;
174
174
  padding: 1.5rem;
175
175
  background: var(--smrt-color-surface, #fff);
176
176
  border-radius: var(--smrt-radius-md, 8px);
177
177
  border: 1px solid var(--smrt-color-outline-variant, #e2e8f0);
178
178
  }
179
-
179
+
180
180
  .form-group {
181
181
  margin-bottom: 1rem;
182
182
  }
183
-
183
+
184
184
  .form-row {
185
185
  display: grid;
186
186
  grid-template-columns: 1fr 1fr;
187
187
  gap: 1rem;
188
188
  }
189
-
189
+
190
190
  label {
191
191
  display: block;
192
192
  margin-bottom: 0.25rem;
@@ -195,30 +195,13 @@ function handleSubmit(event: Event) {
195
195
  font-size: var(--smrt-typography-label-large-size, 0.875rem);
196
196
  }
197
197
 
198
- .form-input, .form-textarea {
199
- width: 100%;
200
- padding: 0.5rem;
201
- border: 1px solid var(--smrt-color-outline-variant, #d1d5db);
202
- border-radius: var(--smrt-radius-sm, 4px);
203
- font-size: var(--smrt-typography-body-medium-size, 0.875rem);
204
- transition: border-color 0.2s;
205
- }
206
-
207
- .form-input:focus, .form-textarea:focus {
208
- outline: none;
209
- border-color: var(--smrt-color-primary, #3b82f6);
210
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--smrt-color-primary, #3b82f6) 10%, transparent);
211
- }
212
-
213
- .form-input.error {
198
+ /* Error border on the migrated <Input>. The primitive renders the inner
199
+ <input class="input error"> inside its own component, so the scoped class
200
+ can't reach it without :global (#1589). */
201
+ .product-form-shell :global(.input.error) {
214
202
  border-color: var(--smrt-color-error, #dc2626);
215
203
  }
216
-
217
- .form-textarea {
218
- resize: vertical;
219
- min-height: 80px;
220
- }
221
-
204
+
222
205
  .checkbox-label {
223
206
  display: flex;
224
207
  align-items: center;
@@ -251,39 +234,4 @@ function handleSubmit(event: Event) {
251
234
  padding-top: 1rem;
252
235
  border-top: 1px solid var(--smrt-color-outline-variant, #f3f4f6);
253
236
  }
254
-
255
- .cancel-btn, .submit-btn {
256
- padding: 0.5rem 1rem;
257
- border-radius: var(--smrt-radius-sm, 4px);
258
- font-size: var(--smrt-typography-label-large-size, 0.875rem);
259
- font-weight: var(--smrt-typography-weight-medium, 500);
260
- cursor: pointer;
261
- border: 1px solid;
262
- transition: all 0.2s;
263
- }
264
-
265
- .cancel-btn {
266
- background: var(--smrt-color-surface, #fff);
267
- border-color: var(--smrt-color-outline-variant, #d1d5db);
268
- color: var(--smrt-color-on-surface, #374151);
269
- }
270
-
271
- .cancel-btn:hover:not(:disabled) {
272
- background: var(--smrt-color-surface-container-low, #f9fafb);
273
- }
274
-
275
- .submit-btn {
276
- background: var(--smrt-color-primary, #3b82f6);
277
- border-color: var(--smrt-color-primary, #3b82f6);
278
- color: var(--smrt-color-on-primary, white);
279
- }
280
-
281
- .submit-btn:hover:not(:disabled) {
282
- background: var(--smrt-color-primary, #2563eb);
283
- }
284
-
285
- .submit-btn:disabled, .cancel-btn:disabled {
286
- opacity: 0.5;
287
- cursor: not-allowed;
288
- }
289
237
  </style>
@@ -1 +1 @@
1
- {"version":3,"file":"ProductForm.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/ProductForm.svelte"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,UAAU,KAAK;IACb,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/B,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAClD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AA0HD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"ProductForm.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/ProductForm.svelte"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,UAAU,KAAK;IACb,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/B,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAClD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AA+HD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -4,7 +4,9 @@
4
4
  * Demonstrates "Define Once, Consume Everywhere" - form is generated from Product class definition
5
5
  */
6
6
 
7
+ import { Form } from '@happyvertical/smrt-ui/forms';
7
8
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
9
+ import { Button } from '@happyvertical/smrt-ui/ui';
8
10
  import { M } from '../../i18n.js';
9
11
  import type { ProductData } from '../../types';
10
12
  import FieldRenderer from './FieldRenderer.svelte';
@@ -112,31 +114,33 @@ function _getFieldType(
112
114
  </div>
113
115
  </header>
114
116
 
115
- <form class="form-content" onsubmit={handleSubmit}>
116
- {#each fieldSchema as field}
117
- <FieldRenderer
118
- fieldName={field.name}
119
- fieldType={field.type}
120
- value={formData[field.name]}
121
- label={field.label}
122
- placeholder={field.placeholder}
123
- required={field.required || false}
124
- {readonly}
125
- onUpdate={(value) => updateField(field.name, value)}
126
- />
127
- {/each}
128
-
129
- {#if !readonly}
130
- <div class="form-actions">
131
- <button type="submit" class="submit-btn">
132
- {submitLabel}
133
- </button>
134
- <button type="button" class="reset-btn" onclick={() => formData = {}}>
135
- Reset
136
- </button>
137
- </div>
138
- {/if}
139
- </form>
117
+ <div class="form-content-shell">
118
+ <Form class="form-content" onsubmit={handleSubmit}>
119
+ {#each fieldSchema as field}
120
+ <FieldRenderer
121
+ fieldName={field.name}
122
+ fieldType={field.type}
123
+ value={formData[field.name]}
124
+ label={field.label}
125
+ placeholder={field.placeholder}
126
+ required={field.required || false}
127
+ {readonly}
128
+ onUpdate={(value) => updateField(field.name, value)}
129
+ />
130
+ {/each}
131
+
132
+ {#if !readonly}
133
+ <div class="form-actions">
134
+ <Button type="submit" variant="primary">
135
+ {submitLabel}
136
+ </Button>
137
+ <Button type="button" variant="secondary" onclick={() => formData = {}}>
138
+ Reset
139
+ </Button>
140
+ </div>
141
+ {/if}
142
+ </Form>
143
+ </div>
140
144
 
141
145
  <!-- Demo: Show current form state -->
142
146
  <details class="form-debug">
@@ -173,7 +177,7 @@ function _getFieldType(
173
177
  font-style: italic;
174
178
  }
175
179
 
176
- .form-content {
180
+ .form-content-shell :global(.form-content) {
177
181
  display: flex;
178
182
  flex-direction: column;
179
183
  gap: 1rem;
@@ -187,36 +191,6 @@ function _getFieldType(
187
191
  border-top: 1px solid var(--smrt-color-outline-variant, #e5e7eb);
188
192
  }
189
193
 
190
- .submit-btn {
191
- background: var(--smrt-color-primary, #3b82f6);
192
- color: var(--smrt-color-on-primary, white);
193
- border: none;
194
- padding: 0.75rem 1.5rem;
195
- border-radius: var(--smrt-radius-sm, 0.375rem);
196
- font-weight: var(--smrt-typography-weight-medium, 500);
197
- cursor: pointer;
198
- transition: background-color 0.2s;
199
- }
200
-
201
- .submit-btn:hover {
202
- background: var(--smrt-color-primary, #2563eb);
203
- }
204
-
205
- .reset-btn {
206
- background: var(--smrt-color-surface-container, #f3f4f6);
207
- color: var(--smrt-color-on-surface, #374151);
208
- border: 1px solid var(--smrt-color-outline-variant, #d1d5db);
209
- padding: 0.75rem 1.5rem;
210
- border-radius: var(--smrt-radius-sm, 0.375rem);
211
- font-weight: var(--smrt-typography-weight-medium, 500);
212
- cursor: pointer;
213
- transition: background-color 0.2s;
214
- }
215
-
216
- .reset-btn:hover {
217
- background: var(--smrt-color-surface-container-high, #e5e7eb);
218
- }
219
-
220
194
  .form-debug {
221
195
  margin-top: 2rem;
222
196
  padding: 1rem;
@@ -1 +1 @@
1
- {"version":3,"file":"AutoForm.svelte.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/auto-generated/AutoForm.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,UAAU,KAAK;IACb,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACvC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;CACxC;AAmID,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"AutoForm.svelte.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/auto-generated/AutoForm.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,UAAU,KAAK;IACb,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACvC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;CACxC;AAuID,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -4,6 +4,7 @@
4
4
  * This demonstrates the "Define Once, Consume Everywhere" vision
5
5
  */
6
6
 
7
+ import { Input, Textarea } from '@happyvertical/smrt-ui/forms';
7
8
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
8
9
  import { M } from '../../i18n.js';
9
10
 
@@ -92,10 +93,9 @@ function handleObjectInput(event: Event) {
92
93
  </label>
93
94
 
94
95
  {#if fieldType === 'string'}
95
- <input
96
+ <Input
96
97
  id={fieldId}
97
98
  type="text"
98
- class="field-input"
99
99
  {value}
100
100
  {placeholder}
101
101
  {readonly}
@@ -103,10 +103,9 @@ function handleObjectInput(event: Event) {
103
103
  oninput={handleStringInput}
104
104
  />
105
105
  {:else if fieldType === 'number'}
106
- <input
106
+ <Input
107
107
  id={fieldId}
108
108
  type="number"
109
- class="field-input"
110
109
  value={value || 0}
111
110
  {placeholder}
112
111
  {readonly}
@@ -114,6 +113,7 @@ function handleObjectInput(event: Event) {
114
113
  oninput={handleNumberInput}
115
114
  />
116
115
  {:else if fieldType === 'boolean'}
116
+ <!-- raw-primitive-allow: native checkbox; no Provider-free checkbox primitive (Toggle is a switch with different semantics, CheckboxInput requires a Provider) -->
117
117
  <input
118
118
  id={fieldId}
119
119
  type="checkbox"
@@ -123,26 +123,24 @@ function handleObjectInput(event: Event) {
123
123
  onchange={handleBooleanInput}
124
124
  />
125
125
  {:else if fieldType === 'array'}
126
- <textarea
126
+ <Textarea
127
127
  id={fieldId}
128
- class="field-textarea"
129
128
  value={Array.isArray(value) ? value.join(', ') : ''}
130
129
  placeholder={placeholder || 'Enter comma-separated values'}
131
130
  {readonly}
132
131
  {required}
133
132
  oninput={handleArrayInput}
134
- />
133
+ ></Textarea>
135
134
  <div class="field-hint">{t(M['products.field_renderer.array_hint'])}</div>
136
135
  {:else if fieldType === 'object'}
137
- <textarea
136
+ <Textarea
138
137
  id={fieldId}
139
- class="field-textarea"
140
138
  value={typeof value === 'object' ? JSON.stringify(value, null, 2) : '{}'}
141
139
  placeholder={placeholder || 'Enter JSON object'}
142
140
  {readonly}
143
141
  {required}
144
142
  oninput={handleObjectInput}
145
- />
143
+ ></Textarea>
146
144
  <div class="field-hint">{t(M['products.field_renderer.object_hint'])}</div>
147
145
  {/if}
148
146
  </div>
@@ -165,27 +163,6 @@ function handleObjectInput(event: Event) {
165
163
  color: var(--smrt-color-error, #dc2626);
166
164
  }
167
165
 
168
- .field-input,
169
- .field-textarea {
170
- padding: 0.5rem 0.75rem;
171
- border: 1px solid var(--smrt-color-outline-variant, #d1d5db);
172
- border-radius: var(--smrt-radius-sm, 0.375rem);
173
- font-size: var(--smrt-typography-body-medium-size, 0.875rem);
174
- transition: border-color 0.2s, box-shadow 0.2s;
175
- }
176
-
177
- .field-input:focus,
178
- .field-textarea:focus {
179
- outline: none;
180
- border-color: var(--smrt-color-primary, #3b82f6);
181
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--smrt-color-primary, #3b82f6) 10%, transparent);
182
- }
183
-
184
- .field-textarea {
185
- min-height: 4rem;
186
- resize: vertical;
187
- }
188
-
189
166
  .field-checkbox {
190
167
  width: 1rem;
191
168
  height: 1rem;
@@ -196,10 +173,4 @@ function handleObjectInput(event: Event) {
196
173
  color: var(--smrt-color-on-surface-variant, #6b7280);
197
174
  font-style: italic;
198
175
  }
199
-
200
- .field-input:read-only,
201
- .field-textarea:read-only {
202
- background-color: var(--smrt-color-surface-container-low, #f9fafb);
203
- cursor: not-allowed;
204
- }
205
176
  </style>
@@ -1 +1 @@
1
- {"version":3,"file":"FieldRenderer.svelte.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/auto-generated/FieldRenderer.svelte.ts"],"names":[],"mappings":"AAWA,UAAU,KAAK;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;IAChE,KAAK,EAAE,GAAG,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;CACjC;AAmGD,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"FieldRenderer.svelte.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/auto-generated/FieldRenderer.svelte.ts"],"names":[],"mappings":"AAYA,UAAU,KAAK;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;IAChE,KAAK,EAAE,GAAG,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;CACjC;AAqGD,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -1,5 +1,7 @@
1
1
  <script lang="ts">
2
+ import { Input, Select } from '@happyvertical/smrt-ui/forms';
2
3
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
4
+ import { Button } from '@happyvertical/smrt-ui/ui';
3
5
  import { onMount } from 'svelte';
4
6
  import ProductCard from '../components/ProductCard.svelte';
5
7
  import ProductForm from '../components/ProductForm.svelte';
@@ -102,30 +104,30 @@ function handleCancelForm() {
102
104
 
103
105
  <div class="catalog-controls">
104
106
  <div class="search-filters">
105
- <input
107
+ <Input
106
108
  type="text"
107
109
  bind:value={searchQuery}
108
110
  placeholder={t(M['products.product_catalog.search_placeholder'])}
109
111
  aria-label={t(M['products.product_catalog.search_placeholder'])}
110
112
  class="search-input"
111
113
  />
112
-
113
- <select bind:value={selectedCategory} class="category-filter">
114
+
115
+ <Select bind:value={selectedCategory} class="category-filter">
114
116
  <option value="">{t(M['products.product_catalog.all_categories'])}</option>
115
117
  {#each productStore.categories as category}
116
118
  <option value={category}>{category}</option>
117
119
  {/each}
118
- </select>
120
+ </Select>
119
121
  </div>
120
122
 
121
123
  {#if !readonly && (showCreateForm || productStore.items.length === 0)}
122
- <button
123
- type="button"
124
+ <Button
125
+ type="button"
126
+ variant="primary"
124
127
  onclick={handleCreateProduct}
125
- class="create-btn"
126
128
  >
127
129
  {t(M['products.product_catalog.add_product'])}
128
- </button>
130
+ </Button>
129
131
  {/if}
130
132
  </div>
131
133
 
@@ -136,18 +138,18 @@ function handleCancelForm() {
136
138
  {:else if productStore.error}
137
139
  <div class="error-state" role="alert" aria-live="assertive">
138
140
  <p>Error: {productStore.error}</p>
139
- <button type="button" onclick={() => productStore.loadProducts()}>
141
+ <Button type="button" variant="danger" onclick={() => productStore.loadProducts()}>
140
142
  Retry
141
- </button>
143
+ </Button>
142
144
  </div>
143
145
  {:else if filteredProducts.length === 0}
144
146
  <div class="empty-state" role="status" aria-live="polite">
145
147
  {#if productStore.items.length === 0}
146
148
  <p>{t(M['products.product_catalog.empty'])}</p>
147
149
  {#if !readonly}
148
- <button type="button" onclick={handleCreateProduct} class="create-btn">
150
+ <Button type="button" variant="primary" onclick={handleCreateProduct}>
149
151
  {t(M['products.product_catalog.create_first'])}
150
- </button>
152
+ </Button>
151
153
  {/if}
152
154
  {:else}
153
155
  <p>{t(M['products.product_catalog.no_match'])}</p>
@@ -223,36 +225,18 @@ function handleCancelForm() {
223
225
  gap: 0.75rem;
224
226
  flex: 1;
225
227
  }
226
-
227
- .search-input, .category-filter {
228
- padding: 0.5rem;
229
- border: 1px solid var(--smrt-color-outline-variant, #d1d5db);
230
- border-radius: var(--smrt-radius-sm, 4px);
231
- font-size: var(--smrt-typography-body-medium-size, 0.875rem);
232
- }
233
-
234
- .search-input {
228
+
229
+ /* Layout sizing for the migrated <Input>/<Select>. The primitives render the
230
+ inner element with the forwarded class, so pierce with :global to keep the
231
+ flex sizing the old scoped rules supplied (#1589); padding/border/font come
232
+ from the primitives' tokenised styling now. */
233
+ .search-filters :global(.search-input) {
235
234
  flex: 1;
236
235
  max-width: 300px;
237
236
  }
238
-
239
- .category-filter {
240
- min-width: 150px;
241
- }
242
-
243
- .create-btn {
244
- background: var(--smrt-color-primary, #3b82f6);
245
- color: var(--smrt-color-on-primary, white);
246
- border: none;
247
- padding: 0.5rem 1rem;
248
- border-radius: var(--smrt-radius-sm, 4px);
249
- font-weight: var(--smrt-typography-weight-medium, 500);
250
- cursor: pointer;
251
- transition: background-color 0.2s;
252
- }
253
237
 
254
- .create-btn:hover {
255
- background: var(--smrt-color-primary, #2563eb);
238
+ .search-filters :global(.category-filter) {
239
+ min-width: 150px;
256
240
  }
257
241
 
258
242
  .products-grid {
@@ -267,14 +251,12 @@ function handleCancelForm() {
267
251
  color: var(--smrt-color-on-surface-variant, #6b7280);
268
252
  }
269
253
 
270
- .error-state button {
254
+ /* Restore the retry button's spacing from the error message. The old
255
+ `.error-state button` rule can't reach the <button> rendered inside the
256
+ smrt-ui <Button>, so pierce into it with `:global` (#1589); the danger
257
+ variant already supplies the background/color the old rule set. */
258
+ .error-state :global(.button) {
271
259
  margin-top: 0.5rem;
272
- background: var(--smrt-color-error, #dc2626);
273
- color: var(--smrt-color-on-error, white);
274
- border: none;
275
- padding: 0.5rem 1rem;
276
- border-radius: var(--smrt-radius-sm, 4px);
277
- cursor: pointer;
278
260
  }
279
261
 
280
262
  .form-overlay {