@axenstudio/axen-form 0.1.10 → 0.1.12

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 (88) hide show
  1. package/README.md +783 -270
  2. package/dist/core/dateUtils.cjs +7 -3
  3. package/dist/core/dateUtils.d.ts +1 -1
  4. package/dist/core/dateUtils.d.ts.map +1 -1
  5. package/dist/core/dateUtils.js +7 -3
  6. package/dist/core/validation.cjs +4 -1
  7. package/dist/core/validation.d.ts.map +1 -1
  8. package/dist/core/validation.js +4 -1
  9. package/dist/fields/AutocompleteField/AutocompleteField.cjs +32 -32
  10. package/dist/fields/AutocompleteField/AutocompleteField.js +32 -32
  11. package/dist/fields/AutocompleteField/AutocompleteField.module.css.cjs +34 -13
  12. package/dist/fields/AutocompleteField/AutocompleteField.module.css.js +34 -13
  13. package/dist/fields/AutocompleteMultiField/AutocompleteMultiField.cjs +90 -88
  14. package/dist/fields/AutocompleteMultiField/AutocompleteMultiField.d.ts.map +1 -1
  15. package/dist/fields/AutocompleteMultiField/AutocompleteMultiField.js +90 -88
  16. package/dist/fields/AutocompletePredictField/AutocompletePredictField.cjs +81 -64
  17. package/dist/fields/AutocompletePredictField/AutocompletePredictField.d.ts.map +1 -1
  18. package/dist/fields/AutocompletePredictField/AutocompletePredictField.js +81 -64
  19. package/dist/fields/CheckboxField/CheckboxField.module.css.cjs +12 -0
  20. package/dist/fields/CheckboxField/CheckboxField.module.css.js +12 -0
  21. package/dist/fields/ColorField/ColorField.module.css.cjs +12 -0
  22. package/dist/fields/ColorField/ColorField.module.css.js +12 -0
  23. package/dist/fields/DateField/DateField.cjs +56 -4
  24. package/dist/fields/DateField/DateField.d.ts +1 -1
  25. package/dist/fields/DateField/DateField.d.ts.map +1 -1
  26. package/dist/fields/DateField/DateField.js +56 -4
  27. package/dist/fields/DateField/DatePicker.cjs +194 -0
  28. package/dist/fields/DateField/DatePicker.d.ts +11 -0
  29. package/dist/fields/DateField/DatePicker.d.ts.map +1 -0
  30. package/dist/fields/DateField/DatePicker.js +194 -0
  31. package/dist/fields/DateField/DatePicker.module.css.cjs +62 -0
  32. package/dist/fields/DateField/DatePicker.module.css.js +62 -0
  33. package/dist/fields/DateTimeField/DateTimeField.cjs +126 -8
  34. package/dist/fields/DateTimeField/DateTimeField.d.ts +2 -1
  35. package/dist/fields/DateTimeField/DateTimeField.d.ts.map +1 -1
  36. package/dist/fields/DateTimeField/DateTimeField.js +126 -8
  37. package/dist/fields/PasswordField/PasswordField.module.css.cjs +12 -0
  38. package/dist/fields/PasswordField/PasswordField.module.css.js +12 -0
  39. package/dist/fields/RadioField/RadioField.module.css.cjs +12 -0
  40. package/dist/fields/RadioField/RadioField.module.css.js +12 -0
  41. package/dist/fields/SelectField/SelectField.cjs +6 -1
  42. package/dist/fields/SelectField/SelectField.d.ts.map +1 -1
  43. package/dist/fields/SelectField/SelectField.js +6 -1
  44. package/dist/fields/SliderField/SliderField.module.css.cjs +15 -3
  45. package/dist/fields/SliderField/SliderField.module.css.js +15 -3
  46. package/dist/fields/SwitchField/SwitchField.module.css.cjs +12 -0
  47. package/dist/fields/SwitchField/SwitchField.module.css.js +12 -0
  48. package/dist/fields/TimeField/TimeField.cjs +60 -5
  49. package/dist/fields/TimeField/TimeField.d.ts +2 -1
  50. package/dist/fields/TimeField/TimeField.d.ts.map +1 -1
  51. package/dist/fields/TimeField/TimeField.js +60 -5
  52. package/dist/fields/TimeField/TimePicker.cjs +182 -0
  53. package/dist/fields/TimeField/TimePicker.d.ts +11 -0
  54. package/dist/fields/TimeField/TimePicker.d.ts.map +1 -0
  55. package/dist/fields/TimeField/TimePicker.js +182 -0
  56. package/dist/fields/TimeField/TimePicker.module.css.cjs +50 -0
  57. package/dist/fields/TimeField/TimePicker.module.css.js +50 -0
  58. package/dist/fields/defaultPlaceholders.cjs +1 -0
  59. package/dist/fields/defaultPlaceholders.d.ts.map +1 -1
  60. package/dist/fields/defaultPlaceholders.js +1 -0
  61. package/dist/fields/fieldBase.module.css.cjs +373 -18
  62. package/dist/fields/fieldBase.module.css.js +373 -18
  63. package/dist/fields/icons.cjs +2 -0
  64. package/dist/fields/icons.d.ts +4 -0
  65. package/dist/fields/icons.d.ts.map +1 -1
  66. package/dist/fields/icons.js +2 -0
  67. package/dist/index.cjs +230 -3
  68. package/dist/index.d.ts +2 -0
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +230 -3
  71. package/dist/layout/Box/Box.module.css.cjs +12 -0
  72. package/dist/layout/Box/Box.module.css.js +12 -0
  73. package/dist/layout/Divider/Divider.module.css.cjs +12 -0
  74. package/dist/layout/Divider/Divider.module.css.js +12 -0
  75. package/dist/layout/Grid/Grid.module.css.cjs +12 -0
  76. package/dist/layout/Grid/Grid.module.css.js +12 -0
  77. package/dist/layout/Stack/Stack.module.css.cjs +12 -0
  78. package/dist/layout/Stack/Stack.module.css.js +12 -0
  79. package/dist/react/AxenForm.cjs +228 -3
  80. package/dist/react/AxenForm.js +228 -3
  81. package/dist/react/fieldGroup.module.css.cjs +12 -0
  82. package/dist/react/fieldGroup.module.css.js +12 -0
  83. package/dist/react/gridCell.module.css.cjs +12 -0
  84. package/dist/react/gridCell.module.css.js +12 -0
  85. package/dist/types.d.ts +1 -0
  86. package/dist/types.d.ts.map +1 -1
  87. package/package.json +4 -9
  88. package/dist/axen-form.css +0 -1057
@@ -1,23 +1,370 @@
1
+ (function() {
2
+ "use strict";
3
+ try {
4
+ if (typeof document != "undefined") {
5
+ var elementStyle = document.createElement("style");
6
+ elementStyle.appendChild(document.createTextNode(`/* ═══════════════════════════════════════════════
7
+ axen-form Field Base Styles
8
+ Shared styles for all field components
9
+ A11y-aware + Responsive
10
+ ═══════════════════════════════════════════════ */
11
+
12
+ /* ── Field Wrapper ───────────────────────────── */
13
+ ._wrapper_hrx0p_8 {
14
+ display: flex;
15
+ flex-direction: column;
16
+ width: 100%;
17
+ }
18
+
19
+ /* ── Label ───────────────────────────────────── */
20
+ ._label_hrx0p_15 {
21
+ display: block;
22
+ font-size: var(--axen-font-size-sm, 0.875rem);
23
+ font-weight: var(--axen-font-weight-medium, 500);
24
+ color: var(--axen-color-label, var(--axen-color-text, #1a1a1a));
25
+ margin-bottom: var(--axen-label-margin-bottom, 0.375rem);
26
+ line-height: var(--axen-line-height, 1.5);
27
+ letter-spacing: 0.01em;
28
+ }
29
+
30
+ ._labelRequired_hrx0p_25::after {
31
+ content: ' *';
32
+ color: var(--axen-color-error, #d32f2f);
33
+ font-weight: var(--axen-font-weight-bold, 600);
34
+ }
35
+
36
+ /* ── Input Base ──────────────────────────────── */
37
+ ._input_hrx0p_32 {
38
+ display: block;
39
+ width: 100%;
40
+ height: var(--axen-field-height, 2.75rem);
41
+ min-height: var(--axen-field-min-height, 44px);
42
+ padding: var(--axen-field-padding-y, 0.5rem) var(--axen-field-padding-x, 0.75rem);
43
+ font-family: var(
44
+ --axen-font-family,
45
+ -apple-system,
46
+ BlinkMacSystemFont,
47
+ 'Segoe UI',
48
+ Roboto,
49
+ 'Helvetica Neue',
50
+ Arial,
51
+ sans-serif
52
+ );
53
+ font-size: var(--axen-font-size-md, 1rem);
54
+ line-height: var(--axen-line-height, 1.5);
55
+ color: var(--axen-color-text, #1a1a1a);
56
+ background-color: var(--axen-color-input-bg, var(--axen-color-bg, #ffffff));
57
+ border: var(--axen-border-width, 1px) solid var(--axen-color-border, #d0d5dd);
58
+ border-radius: var(--axen-border-radius, 8px);
59
+ transition:
60
+ border-color var(--axen-transition-fast, 150ms ease),
61
+ box-shadow var(--axen-transition-fast, 150ms ease);
62
+ outline: none;
63
+ }
64
+
65
+ ._input_hrx0p_32::placeholder {
66
+ color: var(--axen-color-text-disabled, #9e9e9e);
67
+ }
68
+
69
+ ._input_hrx0p_32:hover:not(:disabled):not(:read-only) {
70
+ border-color: var(--axen-color-border-hover, #888888);
71
+ }
72
+
73
+ /* Focus ring (keyboard + mouse) */
74
+ ._input_hrx0p_32:focus {
75
+ border-color: var(--axen-color-border-focus, var(--axen-color-primary, #1976d2));
76
+ box-shadow: var(--axen-shadow-focus, 0 0 0 2px rgba(25, 118, 210, 0.5));
77
+ outline: none;
78
+ }
79
+
80
+ ._input_hrx0p_32:disabled {
81
+ background-color: var(--axen-color-bg-disabled, #f5f5f5);
82
+ color: var(--axen-color-text-disabled, #9e9e9e);
83
+ cursor: not-allowed;
84
+ }
85
+
86
+ /* Scope :read-only to actual text inputs only.
87
+ <select> and <div> match :read-only by default in modern browsers,
88
+ which would incorrectly apply the disabled-looking background. */
89
+ input._input_hrx0p_32:read-only,
90
+ textarea._input_hrx0p_32:read-only {
91
+ background-color: var(--axen-color-bg-disabled, #f5f5f5);
92
+ }
93
+
94
+ /* Hide native browser picker icons for date/time/datetime inputs.
95
+ We provide our own custom pickers via DatePicker / TimePicker components,
96
+ so the native chromium dark-themed popups must not appear. */
97
+ ._input_hrx0p_32::-webkit-calendar-picker-indicator {
98
+ display: none;
99
+ -webkit-appearance: none;
100
+ }
101
+
102
+ ._input_hrx0p_32::-webkit-date-and-time-value {
103
+ text-align: left;
104
+ }
105
+
106
+ /* ── Error State ─────────────────────────────── */
107
+ ._inputError_hrx0p_102 {
108
+ border-color: var(--axen-color-error, #d32f2f);
109
+ }
110
+
111
+ ._inputError_hrx0p_102:focus {
112
+ border-color: var(--axen-color-error, #d32f2f);
113
+ box-shadow: 0 0 0 var(--axen-focus-ring-width, 2px) rgba(211, 47, 47, 0.3);
114
+ }
115
+
116
+ /* ── Helper Text ─────────────────────────────── */
117
+ ._helperText_hrx0p_112 {
118
+ display: block;
119
+ font-size: var(--axen-font-size-xs, 0.75rem);
120
+ color: var(--axen-color-text-secondary, #666666);
121
+ margin-top: var(--axen-helper-margin-top, 0.25rem);
122
+ line-height: var(--axen-line-height, 1.5);
123
+ }
124
+
125
+ ._helperTextError_hrx0p_120 {
126
+ color: var(--axen-color-error, #d32f2f);
127
+ }
128
+
129
+ /* ── Textarea ────────────────────────────────── */
130
+ ._textarea_hrx0p_125 {
131
+ height: auto;
132
+ min-height: calc(var(--axen-field-min-height, 44px) * 2);
133
+ resize: vertical;
134
+ padding-top: var(--axen-field-padding-x, 0.75rem);
135
+ }
136
+
137
+ /* ── Select ──────────────────────────────────── */
138
+ ._select_hrx0p_133 {
139
+ appearance: none;
140
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23666' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
141
+ background-repeat: no-repeat;
142
+ background-position: right 0.75rem center;
143
+ padding-right: 2rem;
144
+ cursor: pointer;
145
+ }
146
+
147
+ ._selectPlaceholder_hrx0p_142 {
148
+ color: var(--axen-color-text-disabled, #9e9e9e);
149
+ }
150
+
151
+ /* Dark-mode override removed — axen-form is now light-only by default.
152
+ See tokens.css for color-scheme: light declaration. */
153
+
154
+ /* ── Input with prefix/suffix ────────────────── */
155
+ ._inputGroup_hrx0p_150 {
156
+ display: flex;
157
+ align-items: stretch;
158
+ }
159
+
160
+ /* ── Icon inside input ───────────────────────── */
161
+ ._inputIconWrapper_hrx0p_156 {
162
+ position: relative;
163
+ }
164
+
165
+ ._fieldIcon_hrx0p_160 {
166
+ position: absolute;
167
+ left: 0.75rem;
168
+ top: 50%;
169
+ transform: translateY(-50%);
170
+ width: 1.125rem;
171
+ height: 1.125rem;
172
+ color: var(--axen-color-text-secondary, #666666);
173
+ pointer-events: none;
174
+ display: flex;
175
+ align-items: center;
176
+ justify-content: center;
177
+ z-index: 1;
178
+ }
179
+
180
+ /* Clickable variant for icons that open pickers */
181
+ ._fieldIconClickable_hrx0p_176 {
182
+ position: absolute;
183
+ left: 0.75rem;
184
+ top: 50%;
185
+ transform: translateY(-50%);
186
+ width: 1.125rem;
187
+ height: 1.125rem;
188
+ color: var(--axen-color-text-secondary, #666666);
189
+ display: flex;
190
+ align-items: center;
191
+ justify-content: center;
192
+ z-index: 1;
193
+ cursor: pointer;
194
+ border: none;
195
+ background: transparent;
196
+ padding: 0;
197
+ margin: 0;
198
+ }
199
+
200
+ ._fieldIconClickable_hrx0p_176:hover {
201
+ color: var(--axen-color-primary, #1976d2);
202
+ }
203
+
204
+ ._fieldIconClickable_hrx0p_176:focus-visible {
205
+ outline: var(--axen-focus-ring-width, 2px) solid var(--axen-focus-ring-color);
206
+ outline-offset: 2px;
207
+ border-radius: var(--axen-border-radius-sm, 4px);
208
+ }
209
+
210
+ /* Right-side picker trigger button inside inputIconWrapper */
211
+ ._pickerTrigger_hrx0p_206 {
212
+ position: absolute;
213
+ right: 0.5rem;
214
+ top: 50%;
215
+ transform: translateY(-50%);
216
+ width: 1.5rem;
217
+ height: 1.5rem;
218
+ color: var(--axen-color-text-secondary, #666666);
219
+ display: flex;
220
+ align-items: center;
221
+ justify-content: center;
222
+ cursor: pointer;
223
+ border: none;
224
+ border-radius: var(--axen-border-radius-sm, 4px);
225
+ background: transparent;
226
+ padding: 0;
227
+ z-index: 1;
228
+ }
229
+
230
+ ._pickerTrigger_hrx0p_206:hover {
231
+ color: var(--axen-color-primary, #1976d2);
232
+ background-color: var(--axen-color-bg-hover, #fafafa);
233
+ }
234
+
235
+ ._pickerTrigger_hrx0p_206:focus-visible {
236
+ outline: var(--axen-focus-ring-width, 2px) solid var(--axen-focus-ring-color);
237
+ outline-offset: 2px;
238
+ }
239
+
240
+ /* Input with right-side picker trigger needs extra right padding */
241
+ ._inputWithPicker_hrx0p_236 {
242
+ padding-right: 2.25rem;
243
+ }
244
+
245
+ ._inputWithIcon_hrx0p_240 {
246
+ padding-left: 2.5rem;
247
+ }
248
+
249
+ ._inputPrefix_hrx0p_244,
250
+ ._inputSuffix_hrx0p_245 {
251
+ display: flex;
252
+ align-items: center;
253
+ padding: 0 var(--axen-space-sm, 0.5rem);
254
+ font-size: var(--axen-font-size-sm, 0.875rem);
255
+ color: var(--axen-color-text-secondary, #666666);
256
+ background-color: var(--axen-color-input-bg, #ffffff);
257
+ border: var(--axen-border-width, 1px) solid var(--axen-color-border, #d0d5dd);
258
+ white-space: nowrap;
259
+ }
260
+
261
+ ._inputPrefix_hrx0p_244 {
262
+ border-right: none;
263
+ border-radius: var(--axen-border-radius, 6px) 0 0 var(--axen-border-radius, 6px);
264
+ }
265
+
266
+ ._inputSuffix_hrx0p_245 {
267
+ border-left: none;
268
+ border-radius: 0 var(--axen-border-radius, 6px) var(--axen-border-radius, 6px) 0;
269
+ }
270
+
271
+ ._inputGroupInput_hrx0p_266 {
272
+ border-radius: 0;
273
+ }
274
+
275
+ ._inputPrefix_hrx0p_244 + ._inputGroupInput_hrx0p_266 {
276
+ border-top-left-radius: 0;
277
+ border-bottom-left-radius: 0;
278
+ }
279
+
280
+ ._inputGroupInput_hrx0p_266:has(+ ._inputSuffix_hrx0p_245) {
281
+ border-top-right-radius: 0;
282
+ border-bottom-right-radius: 0;
283
+ }
284
+
285
+ /* ── Checkbox/Radio/Switch shared ────────────── */
286
+ ._inlineWrapper_hrx0p_281 {
287
+ display: flex;
288
+ align-items: center;
289
+ gap: var(--axen-space-sm, 0.5rem);
290
+ min-height: var(--axen-field-min-height, 44px);
291
+ cursor: pointer;
292
+ }
293
+
294
+ ._inlineWrapper_hrx0p_281:has(:disabled) {
295
+ cursor: not-allowed;
296
+ opacity: 0.6;
297
+ }
298
+
299
+ ._inlineLabel_hrx0p_294 {
300
+ font-size: var(--axen-font-size-md, 1rem);
301
+ color: var(--axen-color-text, #1a1a1a);
302
+ user-select: none;
303
+ }
304
+
305
+ /* ── Responsive: mobile touch targets ────────── */
306
+ @media (max-width: 599px) {
307
+ ._input_hrx0p_32 {
308
+ font-size: 16px; /* Prevent iOS zoom on focus */
309
+ min-height: 48px;
310
+ }
311
+
312
+ ._inlineWrapper_hrx0p_281 {
313
+ min-height: 48px;
314
+ }
315
+ }
316
+
317
+ /* ── High Contrast Mode ──────────────────────── */
318
+ @media (prefers-contrast: more) {
319
+ ._input_hrx0p_32 {
320
+ border-width: 2px;
321
+ }
322
+
323
+ ._inputError_hrx0p_102 {
324
+ border-width: 3px;
325
+ }
326
+
327
+ ._helperTextError_hrx0p_120 {
328
+ font-weight: var(--axen-font-weight-bold, 600);
329
+ }
330
+ }
331
+
332
+ /* ── Reduced Motion (A11y: WCAG 2.3.3) ──────── */
333
+ @media (prefers-reduced-motion: reduce) {
334
+ ._input_hrx0p_32 {
335
+ transition: none;
336
+ }
337
+ }`));
338
+ document.head.appendChild(elementStyle);
339
+ }
340
+ } catch (e) {
341
+ console.error("vite-plugin-css-injected-by-js", e);
342
+ }
343
+ })();
1
344
  "use strict";
2
345
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
- const wrapper = "_wrapper_1w6h8_8";
4
- const label = "_label_1w6h8_15";
5
- const labelRequired = "_labelRequired_1w6h8_25";
6
- const input = "_input_1w6h8_32";
7
- const inputError = "_inputError_1w6h8_86";
8
- const helperText = "_helperText_1w6h8_96";
9
- const helperTextError = "_helperTextError_1w6h8_104";
10
- const textarea = "_textarea_1w6h8_109";
11
- const select = "_select_1w6h8_117";
12
- const inputGroup = "_inputGroup_1w6h8_127";
13
- const inputIconWrapper = "_inputIconWrapper_1w6h8_133";
14
- const fieldIcon = "_fieldIcon_1w6h8_137";
15
- const inputWithIcon = "_inputWithIcon_1w6h8_152";
16
- const inputPrefix = "_inputPrefix_1w6h8_156";
17
- const inputSuffix = "_inputSuffix_1w6h8_157";
18
- const inputGroupInput = "_inputGroupInput_1w6h8_178";
19
- const inlineWrapper = "_inlineWrapper_1w6h8_193";
20
- const inlineLabel = "_inlineLabel_1w6h8_206";
346
+ const wrapper = "_wrapper_hrx0p_8";
347
+ const label = "_label_hrx0p_15";
348
+ const labelRequired = "_labelRequired_hrx0p_25";
349
+ const input = "_input_hrx0p_32";
350
+ const inputError = "_inputError_hrx0p_102";
351
+ const helperText = "_helperText_hrx0p_112";
352
+ const helperTextError = "_helperTextError_hrx0p_120";
353
+ const textarea = "_textarea_hrx0p_125";
354
+ const select = "_select_hrx0p_133";
355
+ const selectPlaceholder = "_selectPlaceholder_hrx0p_142";
356
+ const inputGroup = "_inputGroup_hrx0p_150";
357
+ const inputIconWrapper = "_inputIconWrapper_hrx0p_156";
358
+ const fieldIcon = "_fieldIcon_hrx0p_160";
359
+ const fieldIconClickable = "_fieldIconClickable_hrx0p_176";
360
+ const pickerTrigger = "_pickerTrigger_hrx0p_206";
361
+ const inputWithPicker = "_inputWithPicker_hrx0p_236";
362
+ const inputWithIcon = "_inputWithIcon_hrx0p_240";
363
+ const inputPrefix = "_inputPrefix_hrx0p_244";
364
+ const inputSuffix = "_inputSuffix_hrx0p_245";
365
+ const inputGroupInput = "_inputGroupInput_hrx0p_266";
366
+ const inlineWrapper = "_inlineWrapper_hrx0p_281";
367
+ const inlineLabel = "_inlineLabel_hrx0p_294";
21
368
  const styles = {
22
369
  wrapper,
23
370
  label,
@@ -28,9 +375,13 @@ const styles = {
28
375
  helperTextError,
29
376
  textarea,
30
377
  select,
378
+ selectPlaceholder,
31
379
  inputGroup,
32
380
  inputIconWrapper,
33
381
  fieldIcon,
382
+ fieldIconClickable,
383
+ pickerTrigger,
384
+ inputWithPicker,
34
385
  inputWithIcon,
35
386
  inputPrefix,
36
387
  inputSuffix,
@@ -40,6 +391,7 @@ const styles = {
40
391
  };
41
392
  exports.default = styles;
42
393
  exports.fieldIcon = fieldIcon;
394
+ exports.fieldIconClickable = fieldIconClickable;
43
395
  exports.helperText = helperText;
44
396
  exports.helperTextError = helperTextError;
45
397
  exports.inlineLabel = inlineLabel;
@@ -52,8 +404,11 @@ exports.inputIconWrapper = inputIconWrapper;
52
404
  exports.inputPrefix = inputPrefix;
53
405
  exports.inputSuffix = inputSuffix;
54
406
  exports.inputWithIcon = inputWithIcon;
407
+ exports.inputWithPicker = inputWithPicker;
55
408
  exports.label = label;
56
409
  exports.labelRequired = labelRequired;
410
+ exports.pickerTrigger = pickerTrigger;
57
411
  exports.select = select;
412
+ exports.selectPlaceholder = selectPlaceholder;
58
413
  exports.textarea = textarea;
59
414
  exports.wrapper = wrapper;