@react-md/core 6.3.2 → 6.3.4
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.
- package/dist/_utils.scss +0 -2
- package/dist/box/_box.scss +20 -1
- package/dist/box/styles.d.ts +39 -0
- package/dist/box/styles.js +39 -0
- package/dist/box/styles.js.map +1 -1
- package/dist/form/useTextField.d.ts +1 -1
- package/dist/form/useTextField.js.map +1 -1
- package/dist/test-utils/queries/slider.d.ts +3 -3
- package/dist/test-utils/queries/slider.js.map +1 -1
- package/package.json +14 -17
- package/src/box/styles.ts +39 -0
- package/src/form/useTextField.ts +1 -1
- package/src/test-utils/queries/slider.ts +3 -3
package/dist/_utils.scss
CHANGED
|
@@ -272,7 +272,6 @@ $_swappable-properties: text-align;
|
|
|
272
272
|
@mixin _sr-only-focusable {
|
|
273
273
|
&:active,
|
|
274
274
|
&:focus {
|
|
275
|
-
clip: auto;
|
|
276
275
|
clip-path: none;
|
|
277
276
|
height: auto;
|
|
278
277
|
margin: auto;
|
|
@@ -294,7 +293,6 @@ $_swappable-properties: text-align;
|
|
|
294
293
|
/// the element has been focused
|
|
295
294
|
@mixin sr-only($focusable: false, $focus-selector: "&--focusable") {
|
|
296
295
|
border: 0;
|
|
297
|
-
clip: rect(1px, 1px, 1px, 1px);
|
|
298
296
|
clip-path: inset(50%);
|
|
299
297
|
height: 1px;
|
|
300
298
|
margin: -1px;
|
package/dist/box/_box.scss
CHANGED
|
@@ -153,6 +153,11 @@ $_breakpoints: (
|
|
|
153
153
|
),
|
|
154
154
|
);
|
|
155
155
|
|
|
156
|
+
/// @type Boolean
|
|
157
|
+
/// @access private
|
|
158
|
+
$_disable-media-queries: $disable-phone-columns and $disable-tablet-columns and
|
|
159
|
+
$disable-desktop-columns and $disable-large-desktop-columns;
|
|
160
|
+
|
|
156
161
|
/// The available configurable css variables and mostly used internally for the
|
|
157
162
|
/// `get-var`, `set-var`, and `use-var` utils.
|
|
158
163
|
/// @type List
|
|
@@ -168,6 +173,7 @@ $variables: (
|
|
|
168
173
|
columns,
|
|
169
174
|
row-max-height,
|
|
170
175
|
auto-rows-height,
|
|
176
|
+
fallback-media-columns,
|
|
171
177
|
phone-columns,
|
|
172
178
|
phone-item-min-size,
|
|
173
179
|
phone-item-min-height,
|
|
@@ -404,6 +410,10 @@ $variables: (
|
|
|
404
410
|
|
|
405
411
|
@if not $disable-grid {
|
|
406
412
|
&--grid {
|
|
413
|
+
@if not $_disable-media-queries {
|
|
414
|
+
@include set-var(fallback-media-columns, auto-fit);
|
|
415
|
+
}
|
|
416
|
+
|
|
407
417
|
display: grid;
|
|
408
418
|
grid-template-columns: repeat(
|
|
409
419
|
get-var(columns),
|
|
@@ -412,6 +422,9 @@ $variables: (
|
|
|
412
422
|
}
|
|
413
423
|
|
|
414
424
|
&--grid-fill {
|
|
425
|
+
@if not $_disable-media-queries {
|
|
426
|
+
@include set-var(fallback-media-columns, auto-fill);
|
|
427
|
+
}
|
|
415
428
|
@include set-var(columns, auto-fill);
|
|
416
429
|
}
|
|
417
430
|
|
|
@@ -427,7 +440,13 @@ $variables: (
|
|
|
427
440
|
$disable-size: map.get($feature-flags, size);
|
|
428
441
|
@if not $disable-columns {
|
|
429
442
|
&--grid-#{$media} {
|
|
430
|
-
@include set-var(
|
|
443
|
+
@include set-var(
|
|
444
|
+
columns,
|
|
445
|
+
get-var(
|
|
446
|
+
$media + "-columns",
|
|
447
|
+
get-var(fallback-media-columns)
|
|
448
|
+
)
|
|
449
|
+
);
|
|
431
450
|
}
|
|
432
451
|
}
|
|
433
452
|
|
package/dist/box/styles.d.ts
CHANGED
|
@@ -217,6 +217,20 @@ export interface BoxOptions {
|
|
|
217
217
|
reversed?: boolean;
|
|
218
218
|
}
|
|
219
219
|
/**
|
|
220
|
+
* Applies the `className` from the `Box` component to any element.
|
|
221
|
+
*
|
|
222
|
+
* @example Simple Example
|
|
223
|
+
* ```tsx
|
|
224
|
+
* return (
|
|
225
|
+
* <form className={box({ stacked: true})}>
|
|
226
|
+
* {children}
|
|
227
|
+
* </form>
|
|
228
|
+
* );
|
|
229
|
+
* ```
|
|
230
|
+
*
|
|
231
|
+
* If custom media query breakpoints are required, use the {@link boxStyles}
|
|
232
|
+
* function instead which will also set the CSS variables.
|
|
233
|
+
*
|
|
220
234
|
* @see {@link boxStyles}
|
|
221
235
|
* @since 6.0.0
|
|
222
236
|
*/
|
|
@@ -235,6 +249,31 @@ export interface BoxStylesOptions extends BoxOptions {
|
|
|
235
249
|
style?: CSSProperties;
|
|
236
250
|
}
|
|
237
251
|
/**
|
|
252
|
+
* Used to apply the styles from the `Box` component to any element by
|
|
253
|
+
* providing a `style` object with CSS variables and a `className`.
|
|
254
|
+
*
|
|
255
|
+
* If you do not need to use
|
|
256
|
+
*
|
|
257
|
+
* @example Simple Example
|
|
258
|
+
* ```tsx
|
|
259
|
+
* return (
|
|
260
|
+
* <form
|
|
261
|
+
* {...boxStyles({
|
|
262
|
+
* grid: true,
|
|
263
|
+
* gridColumns: {
|
|
264
|
+
* phone: 1,
|
|
265
|
+
* desktop: 3,
|
|
266
|
+
* },
|
|
267
|
+
* gridItemSize: {
|
|
268
|
+
* tablet: "12rem",
|
|
269
|
+
* },
|
|
270
|
+
* })}
|
|
271
|
+
* >
|
|
272
|
+
* {children}
|
|
273
|
+
* </form>
|
|
274
|
+
* );
|
|
275
|
+
* ```
|
|
276
|
+
*
|
|
238
277
|
* @see {@link box}
|
|
239
278
|
* @since 6.0.0
|
|
240
279
|
*/
|
package/dist/box/styles.js
CHANGED
|
@@ -2,6 +2,20 @@ import { cnb } from "cnbuilder";
|
|
|
2
2
|
import { bem } from "../utils/bem.js";
|
|
3
3
|
const styles = bem("rmd-box");
|
|
4
4
|
/**
|
|
5
|
+
* Applies the `className` from the `Box` component to any element.
|
|
6
|
+
*
|
|
7
|
+
* @example Simple Example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* return (
|
|
10
|
+
* <form className={box({ stacked: true})}>
|
|
11
|
+
* {children}
|
|
12
|
+
* </form>
|
|
13
|
+
* );
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* If custom media query breakpoints are required, use the {@link boxStyles}
|
|
17
|
+
* function instead which will also set the CSS variables.
|
|
18
|
+
*
|
|
5
19
|
* @see {@link boxStyles}
|
|
6
20
|
* @since 6.0.0
|
|
7
21
|
*/ export function box(options = {}) {
|
|
@@ -112,6 +126,31 @@ const styles = bem("rmd-box");
|
|
|
112
126
|
return style;
|
|
113
127
|
}
|
|
114
128
|
/**
|
|
129
|
+
* Used to apply the styles from the `Box` component to any element by
|
|
130
|
+
* providing a `style` object with CSS variables and a `className`.
|
|
131
|
+
*
|
|
132
|
+
* If you do not need to use
|
|
133
|
+
*
|
|
134
|
+
* @example Simple Example
|
|
135
|
+
* ```tsx
|
|
136
|
+
* return (
|
|
137
|
+
* <form
|
|
138
|
+
* {...boxStyles({
|
|
139
|
+
* grid: true,
|
|
140
|
+
* gridColumns: {
|
|
141
|
+
* phone: 1,
|
|
142
|
+
* desktop: 3,
|
|
143
|
+
* },
|
|
144
|
+
* gridItemSize: {
|
|
145
|
+
* tablet: "12rem",
|
|
146
|
+
* },
|
|
147
|
+
* })}
|
|
148
|
+
* >
|
|
149
|
+
* {children}
|
|
150
|
+
* </form>
|
|
151
|
+
* );
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
115
154
|
* @see {@link box}
|
|
116
155
|
* @since 6.0.0
|
|
117
156
|
*/ export function boxStyles(options) {
|
package/dist/box/styles.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/box/styles.ts"],"sourcesContent":["import { cnb } from \"cnbuilder\";\nimport { type CSSProperties } from \"react\";\n\nimport { type DefinedCSSVariableName } from \"../theme/types.js\";\nimport { type IsEmptyObject, type OverridableStringUnion } from \"../types.js\";\nimport { bem } from \"../utils/bem.js\";\n\nconst styles = bem(\"rmd-box\");\n\ndeclare module \"react\" {\n interface CSSProperties {\n \"--rmd-box-gap\"?: string | number;\n \"--rmd-box-padding\"?: string | number;\n \"--rmd-box-padding-h\"?: string | number;\n \"--rmd-box-padding-v\"?: string | number;\n \"--rmd-box-item-min-size\"?: string | number;\n \"--rmd-box-item-min-height\"?: string | number;\n \"--rmd-box-columns\"?: string | number;\n \"--rmd-box-row-max-height\"?: string;\n \"--rmd-box-auto-rows-height\"?: string;\n \"--rmd-box-phone-columns\"?: number | string;\n \"--rmd-box-phone-item-min-height\"?: number | string;\n \"--rmd-box-phone-item-min-size\"?: number | string;\n \"--rmd-box-tablet-columns\"?: number | string;\n \"--rmd-box-tablet-item-min-size\"?: number | string;\n \"--rmd-box-tablet-item-min-height\"?: number | string;\n \"--rmd-box-desktop-columns\"?: number | string;\n \"--rmd-box-desktop-item-min-size\"?: number | string;\n \"--rmd-box-desktop-item-min-height\"?: number | string;\n \"--rmd-box-large-desktop-columns\"?: number | string;\n \"--rmd-box-large-desktop-item-min-size\"?: number | string;\n \"--rmd-box-large-desktop-item-min-height\"?: number | string;\n }\n}\n\n/** @since 6.2.0 */\nexport interface BoxAlignItemsOverrides {}\n/** @since 6.2.0 */\nexport interface BoxJustifyContentOverrides {}\n/** @since 6.2.0 */\nexport interface BoxGridNameOverrides {}\n\n/**\n * @since 6.0.0\n */\nexport type BoxAlignItems = OverridableStringUnion<\n \"start\" | \"flex-start\" | \"center\" | \"end\" | \"flex-end\" | \"stretch\",\n BoxAlignItemsOverrides\n>;\n\n/**\n * @since 6.0.0\n */\nexport type BoxJustifyContent = OverridableStringUnion<\n BoxAlignItems | \"space-around\" | \"space-between\" | \"space-evenly\",\n BoxJustifyContentOverrides\n>;\n\n/**\n * @since 6.0.0\n */\nexport type BoxFlexDirection = \"row\" | \"column\";\n\n/**\n * @since 6.0.0\n */\nexport type BoxGridColumns = \"fit\" | \"fill\" | number;\n\n/**\n * @since 6.2.0\n */\nexport type BoxGridName =\n IsEmptyObject<BoxGridNameOverrides> extends true\n ? string\n : OverridableStringUnion<never, BoxGridNameOverrides>;\n\n/**\n * @since 6.0.0\n */\nexport type BoxBreakpoints = \"phone\" | \"tablet\" | \"desktop\" | \"largeDesktop\";\n\n/**\n * @since 6.0.0\n */\nexport type BoxGridBreakpointColumns = {\n [key in BoxBreakpoints]?: BoxGridColumns;\n};\n\n/**\n * @since 6.0.0\n */\nexport type BoxGridBreakpointItemSize = {\n [key in BoxBreakpoints]?: string;\n};\n\n/**\n * @since 6.0.0\n */\nexport interface BoxOptions {\n className?: string;\n\n /**\n * Set this to `true` to use `display: grid` instead of `display: flex`.\n *\n * @defaultValue `false`\n */\n grid?: boolean;\n\n /**\n * Set this to `true` to apply `width: 100%`. This can be useful when using\n * nested box layouts.\n *\n * @defaultValue `false`\n */\n fullWidth?: boolean;\n\n /**\n * Set this to `true` to prevent gap between items.\n *\n * @defaultValue `false`\n */\n disableGap?: boolean | \"row\" | \"column\";\n\n /**\n * Set this to `true` to set `flex-wrap: nowrap`.\n *\n * @defaultValue `false`\n */\n disableWrap?: boolean;\n\n /**\n * Set this to `true` to disable the default padding.\n *\n * @defaultValue `false`\n */\n disablePadding?: boolean;\n\n /**\n * This should match one of the names in the `$box-grids` map. So for example:\n *\n * ```scss\n * @use \"react-md\" with (\n * $box-grids: (\n * small: (\n * min: 5rem\n * ),\n * medium: (\n * min: 7rem,\n * gap: 0.5rem,\n * padding: 2rem,\n * ),\n * ),\n * );\n * ```\n *\n * ```ts\n * declare module \"@react-md/core/box/styles\" {\n * interface BoxGridNameOverrides {\n * small: true;\n * medium: true;\n * }\n * }\n * ```\n *\n * The `gridName` should be `\"small\" | \"medium\"`.\n *\n * @defaultValue `\"\"`\n */\n gridName?: BoxGridName;\n\n /**\n * The default behavior for a grid is to automatically determine the number\n * of columns to render by placing as many items as possible with a specific\n * item min-width and the defined gap between items. Once the number of\n * columns has been determined, place the items in the grid and expand to the\n * full column width.\n *\n * This prop is a convenience prop to control how to display items when there\n * are not enough to span all columns without a separate SCSS file.\n *\n * i.e. 3 items were provided but 4 can be rendered.\n *\n * - `\"fit\"` - fill the entire width with columns and stretch columns to fill\n * the remaining space\n * - `\"fill\"` - fill the entire width with columns and add empty columns to\n * fill the remaining space\n * - `number` - ignore the auto layout behavior and specify the number of\n * columns to use instead.\n *\n * If additional responsive behavior is required, an object can be provided\n * to define the column behavior for: phone, tablet, desktop, or\n * largeDesktop.\n *\n * @see {@link gridItemSize} for how the number of columns are determined.\n * @defaultValue `\"fit\"`\n */\n gridColumns?: BoxGridColumns | BoxGridBreakpointColumns;\n\n /**\n * This prop can be used to override the default `--rmd-box-item-min-size`\n * for the grid without a separate SCSS file. Since this is set as a CSS variable,\n * the value must be a number with a unit to work correctly. i.e. `10rem`\n * instead of `160`.\n *\n * If additional responsive behavior is required, an object can be provided\n * to define the column behavior for: phone, tablet, desktop, or\n * largeDesktop.\n *\n * @see {@link https://react-md.dev/sassdoc/box#variables-box-item-min-size}\n */\n gridItemSize?: string | BoxGridBreakpointItemSize;\n\n /**\n * Set this to `true` to enable equal height rows within the grid based\n * on the current `max-height`. This requires the `max-height` to be set\n * on the `Box` either by:\n *\n * - a custom class name that sets `max-height`\n * - `core.box-set-var(auto-rows-height, VALUE)` on the box or a parent element\n *\n * @defaultValue `false`\n */\n gridAutoRows?: boolean;\n\n /**\n * @defaultValue `\"\"`\n */\n align?: BoxAlignItems;\n\n /**\n * The default value is really `center` or whatever the\n * `$box-default-align-items` is set to.\n *\n * @defaultValue `\"\"`\n */\n justify?: BoxJustifyContent;\n\n /**\n * Set this to `true` to set `flex-direction: column` which will stack all\n * items in the box.\n *\n * @defaultValue `false`\n */\n stacked?: boolean;\n\n /**\n * Set this to `true` to reverse the `flex-direction`. i.e.\n * - `flex-direction: row-reverse`\n * - `flex-direction: column-reverse`\n *\n * @defaultValue `false`\n */\n reversed?: boolean;\n}\n\n/**\n * @see {@link boxStyles}\n * @since 6.0.0\n */\nexport function box(options: BoxOptions = {}): string {\n const {\n className,\n align = \"\",\n grid,\n gridName = \"\",\n gridColumns = \"fit\",\n gridItemSize,\n gridAutoRows,\n justify = \"\",\n stacked,\n reversed,\n fullWidth,\n disableGap,\n disableWrap,\n disablePadding,\n } = options;\n let phoneColumns: BoxGridColumns | undefined;\n let phoneItemSize: string | number | undefined;\n let tabletColumns: BoxGridColumns | undefined;\n let tabletItemSize: string | number | undefined;\n let desktopColumns: BoxGridColumns | undefined;\n let desktopItemSize: string | number | undefined;\n let largeDesktopColumns: BoxGridColumns | undefined;\n let largeDesktopItemSize: string | number | undefined;\n if (gridColumns && typeof gridColumns === \"object\") {\n ({\n phone: phoneColumns,\n tablet: tabletColumns,\n desktop: desktopColumns,\n largeDesktop: largeDesktopColumns,\n } = gridColumns);\n }\n if (gridItemSize && typeof gridItemSize === \"object\") {\n ({\n phone: phoneItemSize,\n tablet: tabletItemSize,\n desktop: desktopItemSize,\n largeDesktop: largeDesktopItemSize,\n } = gridItemSize);\n }\n\n const isItemSizeEnabled = (\n value: string | number | undefined\n ): boolean | undefined => grid && (!!value || value === 0);\n const isColumnsEnabled = (\n value: BoxGridColumns | BoxGridBreakpointColumns | undefined\n ): boolean | undefined => grid && typeof value === \"number\";\n\n return cnb(\n styles({\n gap: !disableGap,\n \"gap-h\": disableGap === \"row\",\n \"gap-v\": disableGap === \"column\",\n wrap: !disableWrap,\n padded: !disablePadding,\n column: stacked && !reversed,\n reverse: !stacked && reversed,\n \"column-reverse\": stacked && reversed,\n \"full-width\": fullWidth,\n grid,\n \"grid-fill\": grid && gridColumns === \"fill\",\n \"grid-size\": isColumnsEnabled(gridColumns),\n \"grid-phone\": grid && (phoneColumns || phoneItemSize),\n \"grid-phone-size\":\n isColumnsEnabled(phoneColumns) || isItemSizeEnabled(phoneItemSize),\n \"grid-tablet\": grid && (tabletColumns || tabletItemSize),\n \"grid-tablet-size\":\n isColumnsEnabled(tabletColumns) || isItemSizeEnabled(tabletItemSize),\n \"grid-desktop\": grid && (desktopColumns || desktopItemSize),\n \"grid-desktop-size\":\n isColumnsEnabled(desktopColumns) || isItemSizeEnabled(desktopItemSize),\n \"grid-large-desktop\":\n grid && (largeDesktopColumns || largeDesktopItemSize),\n \"grid-large-desktop-size\":\n isColumnsEnabled(largeDesktopColumns) ||\n isItemSizeEnabled(largeDesktopItemSize),\n \"grid-auto-rows\": grid && gridAutoRows,\n [gridName]: grid && gridName,\n \"align-start\": align === \"start\" || align === \"flex-start\",\n \"align-center\": align === \"center\",\n \"align-end\": align === \"end\" || align === \"flex-end\",\n \"align-stretch\": align === \"stretch\",\n \"justify-center\": justify === \"center\",\n \"justify-start\": justify === \"start\" || justify === \"flex-start\",\n \"justify-end\": justify === \"end\" || justify === \"flex-end\",\n \"justify-stretch\": justify === \"stretch\",\n \"justify-around\": justify === \"space-around\",\n \"justify-between\": justify === \"space-between\",\n \"justify-evenly\": justify === \"space-evenly\",\n }),\n className\n );\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\ninterface ApplyOptions {\n media: keyof BoxGridBreakpointColumns | \"\";\n style: CSSProperties | undefined;\n type: \"columns\" | \"size\";\n value:\n | BoxGridColumns\n | BoxGridBreakpointColumns\n | string\n | BoxGridBreakpointItemSize\n | undefined;\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nfunction applyBoxVar(options: ApplyOptions): CSSProperties | undefined {\n const { type, media, style, value } = options;\n const expectedType = type === \"columns\" ? \"number\" : \"string\";\n if (typeof value !== expectedType) {\n return style;\n }\n\n const suffix = type === \"columns\" ? type : \"item-min-size\";\n let varName: DefinedCSSVariableName = `--rmd-box-${suffix}`;\n if (media === \"largeDesktop\") {\n varName = `--rmd-box-large-desktop-${suffix}`;\n } else if (media) {\n varName = `--rmd-box-${media}-${suffix}`;\n }\n\n return {\n ...style,\n [varName]: value,\n };\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nconst BREAKPOINTS = [\"phone\", \"tablet\", \"desktop\", \"largeDesktop\"] as const;\n\n/**\n * @since 6.0.0\n * @internal\n */\nfunction applyBoxVarGroup(\n options: Pick<ApplyOptions, \"value\" | \"style\" | \"type\">\n): CSSProperties | undefined {\n const { style: propStyle, value, type } = options;\n let style = applyBoxVar({\n type,\n style: propStyle,\n media: \"\",\n value,\n });\n if (value && typeof value === \"object\") {\n BREAKPOINTS.forEach((media) => {\n style = applyBoxVar({\n type,\n style,\n media,\n value: value[media],\n });\n });\n }\n return style;\n}\n\n/**\n * @since 6.0.0\n */\nexport interface BoxStyles {\n style?: CSSProperties;\n className: string;\n}\n\n/**\n * @since 6.0.0\n */\nexport interface BoxStylesOptions extends BoxOptions {\n style?: CSSProperties;\n}\n\n/**\n * @see {@link box}\n * @since 6.0.0\n */\nexport function boxStyles(options: BoxStylesOptions): BoxStyles {\n const { style: propStyle, gridColumns, gridItemSize } = options;\n let style = applyBoxVarGroup({\n type: \"columns\",\n style: propStyle,\n value: gridColumns,\n });\n style = applyBoxVarGroup({\n type: \"size\",\n style,\n value: gridItemSize,\n });\n\n return {\n style,\n className: box(options),\n };\n}\n"],"names":["cnb","bem","styles","box","options","className","align","grid","gridName","gridColumns","gridItemSize","gridAutoRows","justify","stacked","reversed","fullWidth","disableGap","disableWrap","disablePadding","phoneColumns","phoneItemSize","tabletColumns","tabletItemSize","desktopColumns","desktopItemSize","largeDesktopColumns","largeDesktopItemSize","phone","tablet","desktop","largeDesktop","isItemSizeEnabled","value","isColumnsEnabled","gap","wrap","padded","column","reverse","applyBoxVar","type","media","style","expectedType","suffix","varName","BREAKPOINTS","applyBoxVarGroup","propStyle","forEach","boxStyles"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/box/styles.ts"],"sourcesContent":["import { cnb } from \"cnbuilder\";\nimport { type CSSProperties } from \"react\";\n\nimport { type DefinedCSSVariableName } from \"../theme/types.js\";\nimport { type IsEmptyObject, type OverridableStringUnion } from \"../types.js\";\nimport { bem } from \"../utils/bem.js\";\n\nconst styles = bem(\"rmd-box\");\n\ndeclare module \"react\" {\n interface CSSProperties {\n \"--rmd-box-gap\"?: string | number;\n \"--rmd-box-padding\"?: string | number;\n \"--rmd-box-padding-h\"?: string | number;\n \"--rmd-box-padding-v\"?: string | number;\n \"--rmd-box-item-min-size\"?: string | number;\n \"--rmd-box-item-min-height\"?: string | number;\n \"--rmd-box-columns\"?: string | number;\n \"--rmd-box-row-max-height\"?: string;\n \"--rmd-box-auto-rows-height\"?: string;\n \"--rmd-box-phone-columns\"?: number | string;\n \"--rmd-box-phone-item-min-height\"?: number | string;\n \"--rmd-box-phone-item-min-size\"?: number | string;\n \"--rmd-box-tablet-columns\"?: number | string;\n \"--rmd-box-tablet-item-min-size\"?: number | string;\n \"--rmd-box-tablet-item-min-height\"?: number | string;\n \"--rmd-box-desktop-columns\"?: number | string;\n \"--rmd-box-desktop-item-min-size\"?: number | string;\n \"--rmd-box-desktop-item-min-height\"?: number | string;\n \"--rmd-box-large-desktop-columns\"?: number | string;\n \"--rmd-box-large-desktop-item-min-size\"?: number | string;\n \"--rmd-box-large-desktop-item-min-height\"?: number | string;\n }\n}\n\n/** @since 6.2.0 */\nexport interface BoxAlignItemsOverrides {}\n/** @since 6.2.0 */\nexport interface BoxJustifyContentOverrides {}\n/** @since 6.2.0 */\nexport interface BoxGridNameOverrides {}\n\n/**\n * @since 6.0.0\n */\nexport type BoxAlignItems = OverridableStringUnion<\n \"start\" | \"flex-start\" | \"center\" | \"end\" | \"flex-end\" | \"stretch\",\n BoxAlignItemsOverrides\n>;\n\n/**\n * @since 6.0.0\n */\nexport type BoxJustifyContent = OverridableStringUnion<\n BoxAlignItems | \"space-around\" | \"space-between\" | \"space-evenly\",\n BoxJustifyContentOverrides\n>;\n\n/**\n * @since 6.0.0\n */\nexport type BoxFlexDirection = \"row\" | \"column\";\n\n/**\n * @since 6.0.0\n */\nexport type BoxGridColumns = \"fit\" | \"fill\" | number;\n\n/**\n * @since 6.2.0\n */\nexport type BoxGridName =\n IsEmptyObject<BoxGridNameOverrides> extends true\n ? string\n : OverridableStringUnion<never, BoxGridNameOverrides>;\n\n/**\n * @since 6.0.0\n */\nexport type BoxBreakpoints = \"phone\" | \"tablet\" | \"desktop\" | \"largeDesktop\";\n\n/**\n * @since 6.0.0\n */\nexport type BoxGridBreakpointColumns = {\n [key in BoxBreakpoints]?: BoxGridColumns;\n};\n\n/**\n * @since 6.0.0\n */\nexport type BoxGridBreakpointItemSize = {\n [key in BoxBreakpoints]?: string;\n};\n\n/**\n * @since 6.0.0\n */\nexport interface BoxOptions {\n className?: string;\n\n /**\n * Set this to `true` to use `display: grid` instead of `display: flex`.\n *\n * @defaultValue `false`\n */\n grid?: boolean;\n\n /**\n * Set this to `true` to apply `width: 100%`. This can be useful when using\n * nested box layouts.\n *\n * @defaultValue `false`\n */\n fullWidth?: boolean;\n\n /**\n * Set this to `true` to prevent gap between items.\n *\n * @defaultValue `false`\n */\n disableGap?: boolean | \"row\" | \"column\";\n\n /**\n * Set this to `true` to set `flex-wrap: nowrap`.\n *\n * @defaultValue `false`\n */\n disableWrap?: boolean;\n\n /**\n * Set this to `true` to disable the default padding.\n *\n * @defaultValue `false`\n */\n disablePadding?: boolean;\n\n /**\n * This should match one of the names in the `$box-grids` map. So for example:\n *\n * ```scss\n * @use \"react-md\" with (\n * $box-grids: (\n * small: (\n * min: 5rem\n * ),\n * medium: (\n * min: 7rem,\n * gap: 0.5rem,\n * padding: 2rem,\n * ),\n * ),\n * );\n * ```\n *\n * ```ts\n * declare module \"@react-md/core/box/styles\" {\n * interface BoxGridNameOverrides {\n * small: true;\n * medium: true;\n * }\n * }\n * ```\n *\n * The `gridName` should be `\"small\" | \"medium\"`.\n *\n * @defaultValue `\"\"`\n */\n gridName?: BoxGridName;\n\n /**\n * The default behavior for a grid is to automatically determine the number\n * of columns to render by placing as many items as possible with a specific\n * item min-width and the defined gap between items. Once the number of\n * columns has been determined, place the items in the grid and expand to the\n * full column width.\n *\n * This prop is a convenience prop to control how to display items when there\n * are not enough to span all columns without a separate SCSS file.\n *\n * i.e. 3 items were provided but 4 can be rendered.\n *\n * - `\"fit\"` - fill the entire width with columns and stretch columns to fill\n * the remaining space\n * - `\"fill\"` - fill the entire width with columns and add empty columns to\n * fill the remaining space\n * - `number` - ignore the auto layout behavior and specify the number of\n * columns to use instead.\n *\n * If additional responsive behavior is required, an object can be provided\n * to define the column behavior for: phone, tablet, desktop, or\n * largeDesktop.\n *\n * @see {@link gridItemSize} for how the number of columns are determined.\n * @defaultValue `\"fit\"`\n */\n gridColumns?: BoxGridColumns | BoxGridBreakpointColumns;\n\n /**\n * This prop can be used to override the default `--rmd-box-item-min-size`\n * for the grid without a separate SCSS file. Since this is set as a CSS variable,\n * the value must be a number with a unit to work correctly. i.e. `10rem`\n * instead of `160`.\n *\n * If additional responsive behavior is required, an object can be provided\n * to define the column behavior for: phone, tablet, desktop, or\n * largeDesktop.\n *\n * @see {@link https://react-md.dev/sassdoc/box#variables-box-item-min-size}\n */\n gridItemSize?: string | BoxGridBreakpointItemSize;\n\n /**\n * Set this to `true` to enable equal height rows within the grid based\n * on the current `max-height`. This requires the `max-height` to be set\n * on the `Box` either by:\n *\n * - a custom class name that sets `max-height`\n * - `core.box-set-var(auto-rows-height, VALUE)` on the box or a parent element\n *\n * @defaultValue `false`\n */\n gridAutoRows?: boolean;\n\n /**\n * @defaultValue `\"\"`\n */\n align?: BoxAlignItems;\n\n /**\n * The default value is really `center` or whatever the\n * `$box-default-align-items` is set to.\n *\n * @defaultValue `\"\"`\n */\n justify?: BoxJustifyContent;\n\n /**\n * Set this to `true` to set `flex-direction: column` which will stack all\n * items in the box.\n *\n * @defaultValue `false`\n */\n stacked?: boolean;\n\n /**\n * Set this to `true` to reverse the `flex-direction`. i.e.\n * - `flex-direction: row-reverse`\n * - `flex-direction: column-reverse`\n *\n * @defaultValue `false`\n */\n reversed?: boolean;\n}\n\n/**\n * Applies the `className` from the `Box` component to any element.\n *\n * @example Simple Example\n * ```tsx\n * return (\n * <form className={box({ stacked: true})}>\n * {children}\n * </form>\n * );\n * ```\n *\n * If custom media query breakpoints are required, use the {@link boxStyles}\n * function instead which will also set the CSS variables.\n *\n * @see {@link boxStyles}\n * @since 6.0.0\n */\nexport function box(options: BoxOptions = {}): string {\n const {\n className,\n align = \"\",\n grid,\n gridName = \"\",\n gridColumns = \"fit\",\n gridItemSize,\n gridAutoRows,\n justify = \"\",\n stacked,\n reversed,\n fullWidth,\n disableGap,\n disableWrap,\n disablePadding,\n } = options;\n let phoneColumns: BoxGridColumns | undefined;\n let phoneItemSize: string | number | undefined;\n let tabletColumns: BoxGridColumns | undefined;\n let tabletItemSize: string | number | undefined;\n let desktopColumns: BoxGridColumns | undefined;\n let desktopItemSize: string | number | undefined;\n let largeDesktopColumns: BoxGridColumns | undefined;\n let largeDesktopItemSize: string | number | undefined;\n if (gridColumns && typeof gridColumns === \"object\") {\n ({\n phone: phoneColumns,\n tablet: tabletColumns,\n desktop: desktopColumns,\n largeDesktop: largeDesktopColumns,\n } = gridColumns);\n }\n if (gridItemSize && typeof gridItemSize === \"object\") {\n ({\n phone: phoneItemSize,\n tablet: tabletItemSize,\n desktop: desktopItemSize,\n largeDesktop: largeDesktopItemSize,\n } = gridItemSize);\n }\n\n const isItemSizeEnabled = (\n value: string | number | undefined\n ): boolean | undefined => grid && (!!value || value === 0);\n const isColumnsEnabled = (\n value: BoxGridColumns | BoxGridBreakpointColumns | undefined\n ): boolean | undefined => grid && typeof value === \"number\";\n\n return cnb(\n styles({\n gap: !disableGap,\n \"gap-h\": disableGap === \"row\",\n \"gap-v\": disableGap === \"column\",\n wrap: !disableWrap,\n padded: !disablePadding,\n column: stacked && !reversed,\n reverse: !stacked && reversed,\n \"column-reverse\": stacked && reversed,\n \"full-width\": fullWidth,\n grid,\n \"grid-fill\": grid && gridColumns === \"fill\",\n \"grid-size\": isColumnsEnabled(gridColumns),\n \"grid-phone\": grid && (phoneColumns || phoneItemSize),\n \"grid-phone-size\":\n isColumnsEnabled(phoneColumns) || isItemSizeEnabled(phoneItemSize),\n \"grid-tablet\": grid && (tabletColumns || tabletItemSize),\n \"grid-tablet-size\":\n isColumnsEnabled(tabletColumns) || isItemSizeEnabled(tabletItemSize),\n \"grid-desktop\": grid && (desktopColumns || desktopItemSize),\n \"grid-desktop-size\":\n isColumnsEnabled(desktopColumns) || isItemSizeEnabled(desktopItemSize),\n \"grid-large-desktop\":\n grid && (largeDesktopColumns || largeDesktopItemSize),\n \"grid-large-desktop-size\":\n isColumnsEnabled(largeDesktopColumns) ||\n isItemSizeEnabled(largeDesktopItemSize),\n \"grid-auto-rows\": grid && gridAutoRows,\n [gridName]: grid && gridName,\n \"align-start\": align === \"start\" || align === \"flex-start\",\n \"align-center\": align === \"center\",\n \"align-end\": align === \"end\" || align === \"flex-end\",\n \"align-stretch\": align === \"stretch\",\n \"justify-center\": justify === \"center\",\n \"justify-start\": justify === \"start\" || justify === \"flex-start\",\n \"justify-end\": justify === \"end\" || justify === \"flex-end\",\n \"justify-stretch\": justify === \"stretch\",\n \"justify-around\": justify === \"space-around\",\n \"justify-between\": justify === \"space-between\",\n \"justify-evenly\": justify === \"space-evenly\",\n }),\n className\n );\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\ninterface ApplyOptions {\n media: keyof BoxGridBreakpointColumns | \"\";\n style: CSSProperties | undefined;\n type: \"columns\" | \"size\";\n value:\n | BoxGridColumns\n | BoxGridBreakpointColumns\n | string\n | BoxGridBreakpointItemSize\n | undefined;\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nfunction applyBoxVar(options: ApplyOptions): CSSProperties | undefined {\n const { type, media, style, value } = options;\n const expectedType = type === \"columns\" ? \"number\" : \"string\";\n if (typeof value !== expectedType) {\n return style;\n }\n\n const suffix = type === \"columns\" ? type : \"item-min-size\";\n let varName: DefinedCSSVariableName = `--rmd-box-${suffix}`;\n if (media === \"largeDesktop\") {\n varName = `--rmd-box-large-desktop-${suffix}`;\n } else if (media) {\n varName = `--rmd-box-${media}-${suffix}`;\n }\n\n return {\n ...style,\n [varName]: value,\n };\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nconst BREAKPOINTS = [\"phone\", \"tablet\", \"desktop\", \"largeDesktop\"] as const;\n\n/**\n * @since 6.0.0\n * @internal\n */\nfunction applyBoxVarGroup(\n options: Pick<ApplyOptions, \"value\" | \"style\" | \"type\">\n): CSSProperties | undefined {\n const { style: propStyle, value, type } = options;\n let style = applyBoxVar({\n type,\n style: propStyle,\n media: \"\",\n value,\n });\n if (value && typeof value === \"object\") {\n BREAKPOINTS.forEach((media) => {\n style = applyBoxVar({\n type,\n style,\n media,\n value: value[media],\n });\n });\n }\n return style;\n}\n\n/**\n * @since 6.0.0\n */\nexport interface BoxStyles {\n style?: CSSProperties;\n className: string;\n}\n\n/**\n * @since 6.0.0\n */\nexport interface BoxStylesOptions extends BoxOptions {\n style?: CSSProperties;\n}\n\n/**\n * Used to apply the styles from the `Box` component to any element by\n * providing a `style` object with CSS variables and a `className`.\n *\n * If you do not need to use\n *\n * @example Simple Example\n * ```tsx\n * return (\n * <form\n * {...boxStyles({\n * grid: true,\n * gridColumns: {\n * phone: 1,\n * desktop: 3,\n * },\n * gridItemSize: {\n * tablet: \"12rem\",\n * },\n * })}\n * >\n * {children}\n * </form>\n * );\n * ```\n *\n * @see {@link box}\n * @since 6.0.0\n */\nexport function boxStyles(options: BoxStylesOptions): BoxStyles {\n const { style: propStyle, gridColumns, gridItemSize } = options;\n let style = applyBoxVarGroup({\n type: \"columns\",\n style: propStyle,\n value: gridColumns,\n });\n style = applyBoxVarGroup({\n type: \"size\",\n style,\n value: gridItemSize,\n });\n\n return {\n style,\n className: box(options),\n };\n}\n"],"names":["cnb","bem","styles","box","options","className","align","grid","gridName","gridColumns","gridItemSize","gridAutoRows","justify","stacked","reversed","fullWidth","disableGap","disableWrap","disablePadding","phoneColumns","phoneItemSize","tabletColumns","tabletItemSize","desktopColumns","desktopItemSize","largeDesktopColumns","largeDesktopItemSize","phone","tablet","desktop","largeDesktop","isItemSizeEnabled","value","isColumnsEnabled","gap","wrap","padded","column","reverse","applyBoxVar","type","media","style","expectedType","suffix","varName","BREAKPOINTS","applyBoxVarGroup","propStyle","forEach","boxStyles"],"mappings":"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"}
|
|
@@ -83,7 +83,7 @@ export interface ProvidedFormMessageProps extends Pick<FormMessageProps, "id" |
|
|
|
83
83
|
*
|
|
84
84
|
* @since 2.5.0
|
|
85
85
|
*/
|
|
86
|
-
export interface ProvidedTextFieldProps<E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement> extends TextFieldValidationOptions, TextFieldChangeHandlers<E
|
|
86
|
+
export interface ProvidedTextFieldProps<E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement> extends TextFieldValidationOptions, Required<TextFieldChangeHandlers<E>>, Required<Pick<TextFieldProps, "id" | "name" | "value" | "error">>, Pick<TextFieldProps, "aria-describedby" | "rightAddon"> {
|
|
87
87
|
/**
|
|
88
88
|
* A ref that must be passed to the `TextField`/`TextArea` so that the custom
|
|
89
89
|
* validity behavior can work.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/form/useTextField.ts"],"sourcesContent":["\"use client\";\n\nimport {\n type HTMLAttributes,\n type ReactNode,\n type Ref,\n type RefCallback,\n type RefObject,\n useCallback,\n useRef,\n useState,\n} from \"react\";\n\nimport { getIcon } from \"../icon/config.js\";\nimport { type UseStateInitializer, type UseStateSetter } from \"../types.js\";\nimport { useEnsuredId } from \"../useEnsuredId.js\";\nimport { useEnsuredRef } from \"../useEnsuredRef.js\";\nimport { type TextFieldProps } from \"./TextField.js\";\nimport {\n type FormMessageInputLengthCounterProps,\n type FormMessageProps,\n} from \"./types.js\";\nimport { useFormReset } from \"./useFormReset.js\";\nimport {\n type ErrorMessageOptions,\n type GetErrorIcon,\n type GetErrorMessage,\n type IsErrored,\n type TextFieldValidationOptions,\n type TextFieldValidationType,\n defaultGetErrorIcon,\n defaultGetErrorMessage,\n defaultIsErrored,\n} from \"./validation.js\";\n\nconst noop = (): void => {\n // do nothing\n};\n\n/**\n * @since 2.5.0\n * @since 6.0.0 Added the `onInvalid` handler\n */\nexport type TextFieldChangeHandlers<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> = Pick<HTMLAttributes<E>, \"onBlur\" | \"onChange\" | \"onInvalid\">;\n\n/** @since 6.0.0 */\nexport interface ErrorChangeHandlerOptions<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> {\n /**\n * A ref containing the `TextField` or `TextArea` if you need access to that\n * DOM node for error reporting.\n */\n ref: RefObject<E>;\n\n /**\n * The current name for the `TextField` or `TextArea`.\n */\n name: string;\n\n /**\n * This will be `true` when the `TextField`/`TextArea` has an error.\n */\n error: boolean;\n\n /**\n * The error message returned by {@link GetErrorMessage}/the browser's\n * validation message. This is normally an empty string when the {@link error}\n * state is `false`.\n */\n errorMessage: string;\n}\n\n/**\n * A function that reports the error state changing. A good use-case for this is\n * to keep track of all the errors within your form and keep a submit button\n * disabled until they have been resolved.\n *\n * Example:\n *\n * ```ts\n * const [errors, setErrors] = useState<Record<string, boolean | undefined>>({});\n * const onErrorChange: ErrorChangeHandler = ({ name, error }) =>\n * setErrors((prevErrors) => ({ ...prevErrors, [name]: error }));\n *\n * const invalid = Object.values(errors).some(Boolean);\n *\n * // form implementation is left as an exercise for the reader\n * <Button type=\"submit\" disabled={invalid} onClick={submitForm}>Submit</Button>\n * ```\n *\n * @since 2.5.0\n * @since 6.0.0 Changed to object argument.\n */\nexport type ErrorChangeHandler<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> = (options: ErrorChangeHandlerOptions<E>) => void;\n\n/** @since 2.5.6 */\nexport interface TextFieldHookState {\n /**\n * The current value for the `TextField` or `TextArea`.\n */\n value: string;\n\n /**\n * This will be `true` when the `TextField`/`TextArea` has an error.\n */\n error: boolean;\n\n /**\n * The error message returned by {@link GetErrorMessage}/the browser's\n * validation message. This is normally an empty string when the {@link error}\n * state is `false`.\n */\n errorMessage: string;\n}\n\n/**\n * All the props that will be generated and return from the `useTextField` hook\n * that should be passed to a `FormMessage` component.\n *\n * @since 2.5.0\n */\nexport interface ProvidedFormMessageProps\n extends Pick<FormMessageProps, \"id\" | \"theme\" | \"children\">,\n Required<Pick<TextFieldProps, \"error\">>,\n Partial<Pick<FormMessageInputLengthCounterProps, \"length\" | \"maxLength\">> {}\n\n/**\n * All the props that will be generated and returned by the `useTextField` hook\n * that should be passed to a `TextField` component.\n *\n * @since 2.5.0\n */\nexport interface ProvidedTextFieldProps<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> extends TextFieldValidationOptions,\n TextFieldChangeHandlers<E>,\n Required<Pick<TextFieldProps, \"id\" | \"name\" | \"value\" | \"error\">>,\n Pick<TextFieldProps, \"aria-describedby\" | \"rightAddon\"> {\n /**\n * A ref that must be passed to the `TextField`/`TextArea` so that the custom\n * validity behavior can work.\n *\n * @since 6.0.0\n */\n ref: RefCallback<E>;\n}\n\n/**\n * @since 2.5.0\n */\nexport interface ProvidedTextFieldMessageProps<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> extends ProvidedTextFieldProps<E> {\n /**\n * These props will be defined as long as the `disableMessage` prop is not\n * `true` from the `useTextField` hook.\n */\n messageProps: ProvidedFormMessageProps;\n}\n\n/**\n * @since 6.3.0\n */\nexport interface TextFieldHookComponentOptions<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> {\n /**\n * An optional id to use for the `TextField`, `Password`, or `TextArea` that\n * is also used to create an id for the inline help/error messages.\n *\n * @defaultValue `\"text-field-\" + useId()`\n */\n id?: string;\n\n /**\n * A unique name to attach to the `TextField`, `TextArea`, or `Password`\n * component.\n */\n name: string;\n\n /**\n * @since 6.3.0\n */\n form?: string;\n\n /**\n * Boolean if the `FormMessage` should also display a counter for the\n * remaining letters allowed based on the `maxLength`.\n *\n * This will still be considered false if the `maxLength` value is not\n * provided.\n *\n * @defaultValue `false`\n */\n counter?: boolean;\n\n /**\n * An optional help text to display in the `FormMessage` component when there\n * is not an error.\n */\n helpText?: ReactNode;\n\n /**\n * A function used to determine if the `TextField` or `TextArea` is an in\n * errored state.\n *\n * @see {@link defaultIsErrored}\n * @defaultValue `defaultIsErrored`\n */\n isErrored?: IsErrored;\n\n /**\n * An optional error icon used in the {@link getErrorIcon} option.\n *\n * @defaultValue `getIcon(\"error\")`\n */\n errorIcon?: ReactNode;\n\n /**\n * A function used to get the error icon to display at the right of the\n * `TextField` or `TextArea`. The default behavior will only show an icon when\n * the `error` state is `true` and an `errorIcon` option has been provided.\n *\n * @see {@link defaultGetErrorIcon}\n * @defaultValue `defaultGetErrorIcon`\n */\n getErrorIcon?: GetErrorIcon;\n\n /**\n * A function to get and display an error message based on the `TextField` or\n * `TextArea` validity.\n *\n * @see {@link defaultGetErrorMessage}\n * @defaultValue `defaultGetErrorMessage`\n */\n getErrorMessage?: GetErrorMessage;\n\n /**\n * An optional function that will be called whenever the `error` state is\n * changed. This can be used for more complex forms to `disable` the Submit\n * button or anything else if any field has an error.\n *\n * @defaultValue `() => {}`\n */\n onErrorChange?: ErrorChangeHandler<E>;\n\n /**\n * Set to `true` to prevent the state from automatically resetting with a\n * form's `reset` event.\n *\n * @defaultValue `false`\n * @since 6.3.0\n */\n disableReset?: boolean;\n\n /**\n * Set this to `true` to prevent the `errorMessage` from being\n * rendered inline below the `TextField`.\n *\n * @defaultValue `false`\n * @since 6.0.0 Only disables the `errorMessage` behavior so\n * that counters and help text can still be rendered easily.\n */\n disableMessage?: boolean;\n\n /**\n * Boolean if the `maxLength` prop should not be passed to the `TextField`\n * component since it will prevent any additional characters from being\n * entered in the text field which might feel like weird behavior to some\n * users. This should really only be used when the `counter` option is also\n * enabled and rendering along with a `FormMessage` component.\n *\n * @defaultValue `false`\n */\n disableMaxLength?: boolean;\n\n /**\n * @defaultValue `\"recommended\"`\n */\n validationType?: TextFieldValidationType;\n}\n\n/** @since 2.5.6 */\nexport interface TextFieldHookOptions<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> extends TextFieldValidationOptions,\n TextFieldHookComponentOptions<E>,\n TextFieldChangeHandlers<E> {\n /**\n * An optional ref that should be merged with the ref returned by this hook.\n * This should really only be used if you are making a custom component using\n * this hook and forwarding refs. If you need a ref to access the `<input>` or\n * `<textarea>` DOM node, you can use the `fieldRef` returned by this hook\n * instead.\n *\n * @example Accessing TextField DOM Node\n * ```tsx\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useTextField } from \"@react-md/core/form/useTextField\";\n * import { useEffect } from \"react\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const { fieldRef, fieldProps } = useTextField({ name: \"example\" });\n *\n * useEffect(() => {\n * fieldRef.current;\n * // ^ HTMLInputElement | null\n * }, [fieldRef]);\n *\n * return <TextField {...fieldProps} label=\"Example\" />;\n * }\n * ```\n */\n ref?: Ref<E>;\n\n /**\n * This is used internally for the `useNumberField` hook and probably\n * shouldn't be used otherwise. This is just passed into the\n * {@link getErrorMessage} options and is used for additional validation.\n *\n * @defaultValue `false`\n */\n isNumber?: boolean;\n\n /**\n * The default value to use for the `TextField` or `TextArea` one initial\n * render. If you want to manually change the value to something else after\n * the initial render, either change the `key` for the component containing\n * this hook, or use the `setState` function returned from this hook.\n *\n * @defaultValue `\"\"`\n */\n defaultValue?: UseStateInitializer<string>;\n}\n\n/** @since 6.0.0 */\nexport interface TextFieldImplementation<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> extends TextFieldHookState {\n fieldRef: RefObject<E>;\n reset: () => void;\n setState: UseStateSetter<Readonly<TextFieldHookState>>;\n fieldProps: ProvidedTextFieldProps<E>;\n}\n\n/** @since 6.0.0 */\nexport interface TextFieldWithMessageImplementation<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> extends TextFieldImplementation<E> {\n fieldProps: ProvidedTextFieldMessageProps<E>;\n}\n\n/** @since 6.0.0 */\nexport interface ValidatedTextFieldImplementation<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> extends TextFieldImplementation<E> {\n fieldProps: ProvidedTextFieldProps<E> | ProvidedTextFieldMessageProps<E>;\n}\n\n/**\n * If you do not want to display the error messages below the `TextField` and\n * handle error messages separately, set the `disableMessage` option to `true`.\n *\n * @example No Inline Error Messages\n * ```tsx\n * import { type ReactElement } from \"react\";\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useTextField } from \"@react-md/core/form/useTextField\";\n *\n * function Example(): ReactElement {\n * const { fieldProps } = useTextField({\n * name: \"example\",\n * disableMessage: true,\n * });\n *\n * return <TextField {...fieldProps} label=\"Example\" />;\n * }\n * ```\n *\n * Look at the other {@link useTextField} override for additional examples.\n *\n * @see {@link https://react-md.dev/components/text-field | TextField Demos}\n * @see {@link https://react-md.dev/hooks/use-text-field | useTextField Demos}\n */\nexport function useTextField<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n>(\n options: TextFieldHookOptions<E> & { disableMessage: true }\n): TextFieldImplementation<E>;\n\n/**\n * @example Simple Example\n * ```tsx\n * import { type ReactElement } from \"react\";\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useTextField } from \"@react-md/core/form/useTextField\";\n *\n * function Example(): ReactElement {\n * const { fieldProps } = useTextField({\n * name: \"example\",\n * });\n *\n * return <TextField {...fieldProps} label=\"Example\" />;\n * }\n * ```\n *\n * @example Inline Counter\n * ```tsx\n * import { type ReactElement } from \"react\";\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useTextField } from \"@react-md/core/form/useTextField\";\n *\n * function Example(): ReactElement {\n * const { fieldProps } = useTextField({\n * name: \"example\",\n * counter: true,\n * required: true,\n * maxLength: 20,\n * // this allows the user to type beyond the max length limit and display\n * // an error message. omit or set to `false` to enforce the max length instead\n * disableMaxLength: true,\n * });\n *\n * return <TextField {...fieldProps} label=\"Example\" />;\n * }\n * ```\n *\n * @example Adding Constraints\n * ```tsx\n * import { type ReactElement } from \"react\";\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useTextField } from \"@react-md/core/form/useTextField\";\n *\n * function Example(): ReactElement {\n * const { fieldProps } = useTextField({\n * name: \"example\",\n * required: true,\n * pattern: \"^[A-Za-z]+$\",\n * minLength: 4,\n * maxLength: 20,\n * });\n *\n * return <TextField {...fieldProps} label=\"Example\" />;\n * }\n * ```\n *\n * @example Custom Validation\n * ```tsx\n * import { type ReactElement } from \"react\";\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useTextField } from \"@react-md/core/form/useTextField\";\n *\n * function Example(): ReactElement {\n * const { fieldProps } = useTextField({\n * name: \"example\",\n * required: true,\n * getErrorMessage(options) {\n * const {\n * value,\n * pattern,\n * required,\n * minLength,\n * maxLength,\n * validity,\n * validationMessage,\n * isNumber,\n * isBlurEvent,\n * validationType,\n * } = options;\n *\n * if (validity.tooLong) {\n * return `No more than ${maxLength} characters.`;\n * }\n *\n * if (validity.tooShort) {\n * return `No more than ${minLength} characters.`;\n * }\n *\n * if (validity.valueMissing) {\n * return \"This value is required!\";\n * }\n *\n * if (value === \"bad value\") {\n * return \"Value cannot be bad value\";\n * }\n *\n * return defaultGetErrorMessage(options);\n * }\n * });\n *\n * return <TextField {...fieldProps} label=\"Example\" />;\n * }\n * ```\n *\n * @see {@link https://react-md.dev/components/text-field | TextField Demos}\n * @see {@link https://react-md.dev/hooks/use-text-field | useTextField Demos}\n * @see https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation\n * @since 2.5.6\n * @since 6.0.0 This hook returns an object instead of an ordered list. Also\n * added the ability to display an inline counter and help text while disabling\n * the error messaging.\n */\nexport function useTextField<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n>(options: TextFieldHookOptions<E>): TextFieldWithMessageImplementation<E>;\n/**\n * @see {@link https://react-md.dev/components/text-field | TextField Demos}\n * @see {@link https://react-md.dev/hooks/use-text-field | useTextField Demos}\n * @since 6.0.0\n */\nexport function useTextField<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n>(options: TextFieldHookOptions<E>): ValidatedTextFieldImplementation<E> {\n const {\n id: propId,\n ref: propRef,\n name,\n form,\n defaultValue = \"\",\n isNumber = false,\n required,\n pattern,\n minLength,\n maxLength,\n onBlur = noop,\n onChange = noop,\n onInvalid = noop,\n counter = false,\n helpText,\n disableReset,\n validationType = \"recommended\",\n disableMessage = false,\n disableMaxLength = false,\n errorIcon: propErrorIcon,\n isErrored = defaultIsErrored,\n onErrorChange = noop,\n getErrorIcon = defaultGetErrorIcon,\n getErrorMessage = defaultGetErrorMessage,\n } = options;\n\n const id = useEnsuredId(propId, \"text-field\");\n const messageId = `${id}-message`;\n const [fieldRef, ref] = useEnsuredRef(propRef);\n const [state, setState] = useState<TextFieldHookState>(() => {\n const value =\n typeof defaultValue === \"function\" ? defaultValue() : defaultValue;\n\n return {\n value,\n error: false,\n errorMessage: \"\",\n };\n });\n const { value, error, errorMessage } = state;\n\n // using a `ref` instead of a `useCallback` makes it so the `defaultValue`\n // will always be used once reset.\n const reset = useRef(() => {\n fieldRef.current?.setCustomValidity(\"\");\n setState({ value, error: false, errorMessage: \"\" });\n }).current;\n\n const errored = useRef(error);\n const checkValidity = useCallback(\n (isBlurEvent: boolean) => {\n const field = fieldRef.current;\n if (!field) {\n throw new Error(\"Unable to check validity due to missing ref\");\n }\n\n // need to temporarily set the `maxLength` back so it can be \"verified\"\n // through the validity api\n /* istanbul ignore next */\n if (isBlurEvent && disableMaxLength && typeof maxLength === \"number\") {\n field.maxLength = maxLength;\n }\n\n const { value } = field;\n field.setCustomValidity(\"\");\n field.checkValidity();\n\n // remove the temporarily set `maxLength` attribute after checking the\n // validity\n /* istanbul ignore next */\n if (disableMaxLength && typeof maxLength === \"number\") {\n field.removeAttribute(\"maxLength\");\n }\n\n const options: ErrorMessageOptions = {\n value,\n pattern,\n required,\n minLength,\n maxLength,\n isBlurEvent,\n isNumber,\n validationType,\n validity: field.validity,\n validationMessage: field.validationMessage,\n };\n const errorMessage = getErrorMessage(options);\n const error = isErrored({ ...options, errorMessage });\n\n if (errored.current !== error) {\n errored.current = error;\n onErrorChange({\n ref: fieldRef,\n name,\n error,\n errorMessage,\n });\n }\n\n /* istanbul ignore next */\n if (errorMessage !== field.validationMessage) {\n field.setCustomValidity(errorMessage);\n }\n\n setState((prevState) => {\n if (\n prevState.value === value &&\n prevState.error === error &&\n prevState.errorMessage === errorMessage\n ) {\n return prevState;\n }\n\n return {\n value,\n error,\n errorMessage,\n };\n });\n },\n [\n disableMaxLength,\n fieldRef,\n getErrorMessage,\n isErrored,\n isNumber,\n maxLength,\n minLength,\n name,\n onErrorChange,\n pattern,\n required,\n validationType,\n ]\n );\n\n const errorIcon = getIcon(\"error\", propErrorIcon);\n const fieldProps: ProvidedTextFieldProps<E> & {\n messageProps?: ProvidedFormMessageProps;\n } = {\n id,\n ref,\n name,\n value,\n error,\n required,\n pattern,\n minLength,\n maxLength: disableMaxLength ? undefined : maxLength,\n rightAddon: getErrorIcon({\n error,\n errorIcon,\n errorMessage,\n }),\n onBlur(event) {\n onBlur(event);\n if (event.isPropagationStopped()) {\n return;\n }\n\n checkValidity(true);\n },\n onChange(event) {\n onChange(event);\n if (event.isPropagationStopped()) {\n return;\n }\n\n if (validationType === \"blur\") {\n const { value } = event.currentTarget;\n setState((prevState) => ({\n ...prevState,\n value,\n }));\n return;\n }\n\n checkValidity(false);\n },\n onInvalid(event) {\n onInvalid(event);\n if (\n event.isPropagationStopped() ||\n event.currentTarget === document.activeElement\n ) {\n return;\n }\n\n // this makes it so that if a submit button is clicked in a form, all\n // textfields will gain the error state immediately\n // also need to extract the validationMessage immediately because of the\n // SyntheticEvent behavior in React. By the time the `setState` is called,\n // the event might've been deleted\n const { validationMessage } = event.currentTarget;\n\n setState((prevState) => {\n if (prevState.error) {\n return prevState;\n }\n\n return {\n ...prevState,\n error: true,\n errorMessage: validationMessage,\n };\n });\n },\n };\n\n const isCounter = counter && typeof maxLength === \"number\";\n if (isCounter || helpText || !disableMessage) {\n fieldProps[\"aria-describedby\"] = messageId;\n fieldProps.messageProps = {\n id: messageId,\n error,\n length: counter ? value.length : undefined,\n maxLength: (counter && maxLength) || undefined,\n children: (!disableMessage && errorMessage) || helpText,\n };\n }\n\n useFormReset({\n form,\n onReset: disableReset ? undefined : reset,\n elementRef: fieldRef,\n });\n\n return {\n ...state,\n reset,\n setState,\n fieldRef,\n fieldProps,\n };\n}\n"],"names":["useCallback","useRef","useState","getIcon","useEnsuredId","useEnsuredRef","useFormReset","defaultGetErrorIcon","defaultGetErrorMessage","defaultIsErrored","noop","useTextField","options","id","propId","ref","propRef","name","form","defaultValue","isNumber","required","pattern","minLength","maxLength","onBlur","onChange","onInvalid","counter","helpText","disableReset","validationType","disableMessage","disableMaxLength","errorIcon","propErrorIcon","isErrored","onErrorChange","getErrorIcon","getErrorMessage","messageId","fieldRef","state","setState","value","error","errorMessage","reset","current","setCustomValidity","errored","checkValidity","isBlurEvent","field","Error","removeAttribute","validity","validationMessage","prevState","fieldProps","undefined","rightAddon","event","isPropagationStopped","currentTarget","document","activeElement","isCounter","messageProps","length","children","onReset","elementRef"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/form/useTextField.ts"],"sourcesContent":["\"use client\";\n\nimport {\n type HTMLAttributes,\n type ReactNode,\n type Ref,\n type RefCallback,\n type RefObject,\n useCallback,\n useRef,\n useState,\n} from \"react\";\n\nimport { getIcon } from \"../icon/config.js\";\nimport { type UseStateInitializer, type UseStateSetter } from \"../types.js\";\nimport { useEnsuredId } from \"../useEnsuredId.js\";\nimport { useEnsuredRef } from \"../useEnsuredRef.js\";\nimport { type TextFieldProps } from \"./TextField.js\";\nimport {\n type FormMessageInputLengthCounterProps,\n type FormMessageProps,\n} from \"./types.js\";\nimport { useFormReset } from \"./useFormReset.js\";\nimport {\n type ErrorMessageOptions,\n type GetErrorIcon,\n type GetErrorMessage,\n type IsErrored,\n type TextFieldValidationOptions,\n type TextFieldValidationType,\n defaultGetErrorIcon,\n defaultGetErrorMessage,\n defaultIsErrored,\n} from \"./validation.js\";\n\nconst noop = (): void => {\n // do nothing\n};\n\n/**\n * @since 2.5.0\n * @since 6.0.0 Added the `onInvalid` handler\n */\nexport type TextFieldChangeHandlers<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> = Pick<HTMLAttributes<E>, \"onBlur\" | \"onChange\" | \"onInvalid\">;\n\n/** @since 6.0.0 */\nexport interface ErrorChangeHandlerOptions<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> {\n /**\n * A ref containing the `TextField` or `TextArea` if you need access to that\n * DOM node for error reporting.\n */\n ref: RefObject<E>;\n\n /**\n * The current name for the `TextField` or `TextArea`.\n */\n name: string;\n\n /**\n * This will be `true` when the `TextField`/`TextArea` has an error.\n */\n error: boolean;\n\n /**\n * The error message returned by {@link GetErrorMessage}/the browser's\n * validation message. This is normally an empty string when the {@link error}\n * state is `false`.\n */\n errorMessage: string;\n}\n\n/**\n * A function that reports the error state changing. A good use-case for this is\n * to keep track of all the errors within your form and keep a submit button\n * disabled until they have been resolved.\n *\n * Example:\n *\n * ```ts\n * const [errors, setErrors] = useState<Record<string, boolean | undefined>>({});\n * const onErrorChange: ErrorChangeHandler = ({ name, error }) =>\n * setErrors((prevErrors) => ({ ...prevErrors, [name]: error }));\n *\n * const invalid = Object.values(errors).some(Boolean);\n *\n * // form implementation is left as an exercise for the reader\n * <Button type=\"submit\" disabled={invalid} onClick={submitForm}>Submit</Button>\n * ```\n *\n * @since 2.5.0\n * @since 6.0.0 Changed to object argument.\n */\nexport type ErrorChangeHandler<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> = (options: ErrorChangeHandlerOptions<E>) => void;\n\n/** @since 2.5.6 */\nexport interface TextFieldHookState {\n /**\n * The current value for the `TextField` or `TextArea`.\n */\n value: string;\n\n /**\n * This will be `true` when the `TextField`/`TextArea` has an error.\n */\n error: boolean;\n\n /**\n * The error message returned by {@link GetErrorMessage}/the browser's\n * validation message. This is normally an empty string when the {@link error}\n * state is `false`.\n */\n errorMessage: string;\n}\n\n/**\n * All the props that will be generated and return from the `useTextField` hook\n * that should be passed to a `FormMessage` component.\n *\n * @since 2.5.0\n */\nexport interface ProvidedFormMessageProps\n extends Pick<FormMessageProps, \"id\" | \"theme\" | \"children\">,\n Required<Pick<TextFieldProps, \"error\">>,\n Partial<Pick<FormMessageInputLengthCounterProps, \"length\" | \"maxLength\">> {}\n\n/**\n * All the props that will be generated and returned by the `useTextField` hook\n * that should be passed to a `TextField` component.\n *\n * @since 2.5.0\n */\nexport interface ProvidedTextFieldProps<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> extends TextFieldValidationOptions,\n Required<TextFieldChangeHandlers<E>>,\n Required<Pick<TextFieldProps, \"id\" | \"name\" | \"value\" | \"error\">>,\n Pick<TextFieldProps, \"aria-describedby\" | \"rightAddon\"> {\n /**\n * A ref that must be passed to the `TextField`/`TextArea` so that the custom\n * validity behavior can work.\n *\n * @since 6.0.0\n */\n ref: RefCallback<E>;\n}\n\n/**\n * @since 2.5.0\n */\nexport interface ProvidedTextFieldMessageProps<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> extends ProvidedTextFieldProps<E> {\n /**\n * These props will be defined as long as the `disableMessage` prop is not\n * `true` from the `useTextField` hook.\n */\n messageProps: ProvidedFormMessageProps;\n}\n\n/**\n * @since 6.3.0\n */\nexport interface TextFieldHookComponentOptions<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> {\n /**\n * An optional id to use for the `TextField`, `Password`, or `TextArea` that\n * is also used to create an id for the inline help/error messages.\n *\n * @defaultValue `\"text-field-\" + useId()`\n */\n id?: string;\n\n /**\n * A unique name to attach to the `TextField`, `TextArea`, or `Password`\n * component.\n */\n name: string;\n\n /**\n * @since 6.3.0\n */\n form?: string;\n\n /**\n * Boolean if the `FormMessage` should also display a counter for the\n * remaining letters allowed based on the `maxLength`.\n *\n * This will still be considered false if the `maxLength` value is not\n * provided.\n *\n * @defaultValue `false`\n */\n counter?: boolean;\n\n /**\n * An optional help text to display in the `FormMessage` component when there\n * is not an error.\n */\n helpText?: ReactNode;\n\n /**\n * A function used to determine if the `TextField` or `TextArea` is an in\n * errored state.\n *\n * @see {@link defaultIsErrored}\n * @defaultValue `defaultIsErrored`\n */\n isErrored?: IsErrored;\n\n /**\n * An optional error icon used in the {@link getErrorIcon} option.\n *\n * @defaultValue `getIcon(\"error\")`\n */\n errorIcon?: ReactNode;\n\n /**\n * A function used to get the error icon to display at the right of the\n * `TextField` or `TextArea`. The default behavior will only show an icon when\n * the `error` state is `true` and an `errorIcon` option has been provided.\n *\n * @see {@link defaultGetErrorIcon}\n * @defaultValue `defaultGetErrorIcon`\n */\n getErrorIcon?: GetErrorIcon;\n\n /**\n * A function to get and display an error message based on the `TextField` or\n * `TextArea` validity.\n *\n * @see {@link defaultGetErrorMessage}\n * @defaultValue `defaultGetErrorMessage`\n */\n getErrorMessage?: GetErrorMessage;\n\n /**\n * An optional function that will be called whenever the `error` state is\n * changed. This can be used for more complex forms to `disable` the Submit\n * button or anything else if any field has an error.\n *\n * @defaultValue `() => {}`\n */\n onErrorChange?: ErrorChangeHandler<E>;\n\n /**\n * Set to `true` to prevent the state from automatically resetting with a\n * form's `reset` event.\n *\n * @defaultValue `false`\n * @since 6.3.0\n */\n disableReset?: boolean;\n\n /**\n * Set this to `true` to prevent the `errorMessage` from being\n * rendered inline below the `TextField`.\n *\n * @defaultValue `false`\n * @since 6.0.0 Only disables the `errorMessage` behavior so\n * that counters and help text can still be rendered easily.\n */\n disableMessage?: boolean;\n\n /**\n * Boolean if the `maxLength` prop should not be passed to the `TextField`\n * component since it will prevent any additional characters from being\n * entered in the text field which might feel like weird behavior to some\n * users. This should really only be used when the `counter` option is also\n * enabled and rendering along with a `FormMessage` component.\n *\n * @defaultValue `false`\n */\n disableMaxLength?: boolean;\n\n /**\n * @defaultValue `\"recommended\"`\n */\n validationType?: TextFieldValidationType;\n}\n\n/** @since 2.5.6 */\nexport interface TextFieldHookOptions<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> extends TextFieldValidationOptions,\n TextFieldHookComponentOptions<E>,\n TextFieldChangeHandlers<E> {\n /**\n * An optional ref that should be merged with the ref returned by this hook.\n * This should really only be used if you are making a custom component using\n * this hook and forwarding refs. If you need a ref to access the `<input>` or\n * `<textarea>` DOM node, you can use the `fieldRef` returned by this hook\n * instead.\n *\n * @example Accessing TextField DOM Node\n * ```tsx\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useTextField } from \"@react-md/core/form/useTextField\";\n * import { useEffect } from \"react\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const { fieldRef, fieldProps } = useTextField({ name: \"example\" });\n *\n * useEffect(() => {\n * fieldRef.current;\n * // ^ HTMLInputElement | null\n * }, [fieldRef]);\n *\n * return <TextField {...fieldProps} label=\"Example\" />;\n * }\n * ```\n */\n ref?: Ref<E>;\n\n /**\n * This is used internally for the `useNumberField` hook and probably\n * shouldn't be used otherwise. This is just passed into the\n * {@link getErrorMessage} options and is used for additional validation.\n *\n * @defaultValue `false`\n */\n isNumber?: boolean;\n\n /**\n * The default value to use for the `TextField` or `TextArea` one initial\n * render. If you want to manually change the value to something else after\n * the initial render, either change the `key` for the component containing\n * this hook, or use the `setState` function returned from this hook.\n *\n * @defaultValue `\"\"`\n */\n defaultValue?: UseStateInitializer<string>;\n}\n\n/** @since 6.0.0 */\nexport interface TextFieldImplementation<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> extends TextFieldHookState {\n fieldRef: RefObject<E>;\n reset: () => void;\n setState: UseStateSetter<Readonly<TextFieldHookState>>;\n fieldProps: ProvidedTextFieldProps<E>;\n}\n\n/** @since 6.0.0 */\nexport interface TextFieldWithMessageImplementation<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> extends TextFieldImplementation<E> {\n fieldProps: ProvidedTextFieldMessageProps<E>;\n}\n\n/** @since 6.0.0 */\nexport interface ValidatedTextFieldImplementation<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n> extends TextFieldImplementation<E> {\n fieldProps: ProvidedTextFieldProps<E> | ProvidedTextFieldMessageProps<E>;\n}\n\n/**\n * If you do not want to display the error messages below the `TextField` and\n * handle error messages separately, set the `disableMessage` option to `true`.\n *\n * @example No Inline Error Messages\n * ```tsx\n * import { type ReactElement } from \"react\";\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useTextField } from \"@react-md/core/form/useTextField\";\n *\n * function Example(): ReactElement {\n * const { fieldProps } = useTextField({\n * name: \"example\",\n * disableMessage: true,\n * });\n *\n * return <TextField {...fieldProps} label=\"Example\" />;\n * }\n * ```\n *\n * Look at the other {@link useTextField} override for additional examples.\n *\n * @see {@link https://react-md.dev/components/text-field | TextField Demos}\n * @see {@link https://react-md.dev/hooks/use-text-field | useTextField Demos}\n */\nexport function useTextField<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n>(\n options: TextFieldHookOptions<E> & { disableMessage: true }\n): TextFieldImplementation<E>;\n\n/**\n * @example Simple Example\n * ```tsx\n * import { type ReactElement } from \"react\";\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useTextField } from \"@react-md/core/form/useTextField\";\n *\n * function Example(): ReactElement {\n * const { fieldProps } = useTextField({\n * name: \"example\",\n * });\n *\n * return <TextField {...fieldProps} label=\"Example\" />;\n * }\n * ```\n *\n * @example Inline Counter\n * ```tsx\n * import { type ReactElement } from \"react\";\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useTextField } from \"@react-md/core/form/useTextField\";\n *\n * function Example(): ReactElement {\n * const { fieldProps } = useTextField({\n * name: \"example\",\n * counter: true,\n * required: true,\n * maxLength: 20,\n * // this allows the user to type beyond the max length limit and display\n * // an error message. omit or set to `false` to enforce the max length instead\n * disableMaxLength: true,\n * });\n *\n * return <TextField {...fieldProps} label=\"Example\" />;\n * }\n * ```\n *\n * @example Adding Constraints\n * ```tsx\n * import { type ReactElement } from \"react\";\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useTextField } from \"@react-md/core/form/useTextField\";\n *\n * function Example(): ReactElement {\n * const { fieldProps } = useTextField({\n * name: \"example\",\n * required: true,\n * pattern: \"^[A-Za-z]+$\",\n * minLength: 4,\n * maxLength: 20,\n * });\n *\n * return <TextField {...fieldProps} label=\"Example\" />;\n * }\n * ```\n *\n * @example Custom Validation\n * ```tsx\n * import { type ReactElement } from \"react\";\n * import { TextField } from \"@react-md/core/form/TextField\";\n * import { useTextField } from \"@react-md/core/form/useTextField\";\n *\n * function Example(): ReactElement {\n * const { fieldProps } = useTextField({\n * name: \"example\",\n * required: true,\n * getErrorMessage(options) {\n * const {\n * value,\n * pattern,\n * required,\n * minLength,\n * maxLength,\n * validity,\n * validationMessage,\n * isNumber,\n * isBlurEvent,\n * validationType,\n * } = options;\n *\n * if (validity.tooLong) {\n * return `No more than ${maxLength} characters.`;\n * }\n *\n * if (validity.tooShort) {\n * return `No more than ${minLength} characters.`;\n * }\n *\n * if (validity.valueMissing) {\n * return \"This value is required!\";\n * }\n *\n * if (value === \"bad value\") {\n * return \"Value cannot be bad value\";\n * }\n *\n * return defaultGetErrorMessage(options);\n * }\n * });\n *\n * return <TextField {...fieldProps} label=\"Example\" />;\n * }\n * ```\n *\n * @see {@link https://react-md.dev/components/text-field | TextField Demos}\n * @see {@link https://react-md.dev/hooks/use-text-field | useTextField Demos}\n * @see https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation\n * @since 2.5.6\n * @since 6.0.0 This hook returns an object instead of an ordered list. Also\n * added the ability to display an inline counter and help text while disabling\n * the error messaging.\n */\nexport function useTextField<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n>(options: TextFieldHookOptions<E>): TextFieldWithMessageImplementation<E>;\n/**\n * @see {@link https://react-md.dev/components/text-field | TextField Demos}\n * @see {@link https://react-md.dev/hooks/use-text-field | useTextField Demos}\n * @since 6.0.0\n */\nexport function useTextField<\n E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,\n>(options: TextFieldHookOptions<E>): ValidatedTextFieldImplementation<E> {\n const {\n id: propId,\n ref: propRef,\n name,\n form,\n defaultValue = \"\",\n isNumber = false,\n required,\n pattern,\n minLength,\n maxLength,\n onBlur = noop,\n onChange = noop,\n onInvalid = noop,\n counter = false,\n helpText,\n disableReset,\n validationType = \"recommended\",\n disableMessage = false,\n disableMaxLength = false,\n errorIcon: propErrorIcon,\n isErrored = defaultIsErrored,\n onErrorChange = noop,\n getErrorIcon = defaultGetErrorIcon,\n getErrorMessage = defaultGetErrorMessage,\n } = options;\n\n const id = useEnsuredId(propId, \"text-field\");\n const messageId = `${id}-message`;\n const [fieldRef, ref] = useEnsuredRef(propRef);\n const [state, setState] = useState<TextFieldHookState>(() => {\n const value =\n typeof defaultValue === \"function\" ? defaultValue() : defaultValue;\n\n return {\n value,\n error: false,\n errorMessage: \"\",\n };\n });\n const { value, error, errorMessage } = state;\n\n // using a `ref` instead of a `useCallback` makes it so the `defaultValue`\n // will always be used once reset.\n const reset = useRef(() => {\n fieldRef.current?.setCustomValidity(\"\");\n setState({ value, error: false, errorMessage: \"\" });\n }).current;\n\n const errored = useRef(error);\n const checkValidity = useCallback(\n (isBlurEvent: boolean) => {\n const field = fieldRef.current;\n if (!field) {\n throw new Error(\"Unable to check validity due to missing ref\");\n }\n\n // need to temporarily set the `maxLength` back so it can be \"verified\"\n // through the validity api\n /* istanbul ignore next */\n if (isBlurEvent && disableMaxLength && typeof maxLength === \"number\") {\n field.maxLength = maxLength;\n }\n\n const { value } = field;\n field.setCustomValidity(\"\");\n field.checkValidity();\n\n // remove the temporarily set `maxLength` attribute after checking the\n // validity\n /* istanbul ignore next */\n if (disableMaxLength && typeof maxLength === \"number\") {\n field.removeAttribute(\"maxLength\");\n }\n\n const options: ErrorMessageOptions = {\n value,\n pattern,\n required,\n minLength,\n maxLength,\n isBlurEvent,\n isNumber,\n validationType,\n validity: field.validity,\n validationMessage: field.validationMessage,\n };\n const errorMessage = getErrorMessage(options);\n const error = isErrored({ ...options, errorMessage });\n\n if (errored.current !== error) {\n errored.current = error;\n onErrorChange({\n ref: fieldRef,\n name,\n error,\n errorMessage,\n });\n }\n\n /* istanbul ignore next */\n if (errorMessage !== field.validationMessage) {\n field.setCustomValidity(errorMessage);\n }\n\n setState((prevState) => {\n if (\n prevState.value === value &&\n prevState.error === error &&\n prevState.errorMessage === errorMessage\n ) {\n return prevState;\n }\n\n return {\n value,\n error,\n errorMessage,\n };\n });\n },\n [\n disableMaxLength,\n fieldRef,\n getErrorMessage,\n isErrored,\n isNumber,\n maxLength,\n minLength,\n name,\n onErrorChange,\n pattern,\n required,\n validationType,\n ]\n );\n\n const errorIcon = getIcon(\"error\", propErrorIcon);\n const fieldProps: ProvidedTextFieldProps<E> & {\n messageProps?: ProvidedFormMessageProps;\n } = {\n id,\n ref,\n name,\n value,\n error,\n required,\n pattern,\n minLength,\n maxLength: disableMaxLength ? undefined : maxLength,\n rightAddon: getErrorIcon({\n error,\n errorIcon,\n errorMessage,\n }),\n onBlur(event) {\n onBlur(event);\n if (event.isPropagationStopped()) {\n return;\n }\n\n checkValidity(true);\n },\n onChange(event) {\n onChange(event);\n if (event.isPropagationStopped()) {\n return;\n }\n\n if (validationType === \"blur\") {\n const { value } = event.currentTarget;\n setState((prevState) => ({\n ...prevState,\n value,\n }));\n return;\n }\n\n checkValidity(false);\n },\n onInvalid(event) {\n onInvalid(event);\n if (\n event.isPropagationStopped() ||\n event.currentTarget === document.activeElement\n ) {\n return;\n }\n\n // this makes it so that if a submit button is clicked in a form, all\n // textfields will gain the error state immediately\n // also need to extract the validationMessage immediately because of the\n // SyntheticEvent behavior in React. By the time the `setState` is called,\n // the event might've been deleted\n const { validationMessage } = event.currentTarget;\n\n setState((prevState) => {\n if (prevState.error) {\n return prevState;\n }\n\n return {\n ...prevState,\n error: true,\n errorMessage: validationMessage,\n };\n });\n },\n };\n\n const isCounter = counter && typeof maxLength === \"number\";\n if (isCounter || helpText || !disableMessage) {\n fieldProps[\"aria-describedby\"] = messageId;\n fieldProps.messageProps = {\n id: messageId,\n error,\n length: counter ? value.length : undefined,\n maxLength: (counter && maxLength) || undefined,\n children: (!disableMessage && errorMessage) || helpText,\n };\n }\n\n useFormReset({\n form,\n onReset: disableReset ? undefined : reset,\n elementRef: fieldRef,\n });\n\n return {\n ...state,\n reset,\n setState,\n fieldRef,\n fieldProps,\n };\n}\n"],"names":["useCallback","useRef","useState","getIcon","useEnsuredId","useEnsuredRef","useFormReset","defaultGetErrorIcon","defaultGetErrorMessage","defaultIsErrored","noop","useTextField","options","id","propId","ref","propRef","name","form","defaultValue","isNumber","required","pattern","minLength","maxLength","onBlur","onChange","onInvalid","counter","helpText","disableReset","validationType","disableMessage","disableMaxLength","errorIcon","propErrorIcon","isErrored","onErrorChange","getErrorIcon","getErrorMessage","messageId","fieldRef","state","setState","value","error","errorMessage","reset","current","setCustomValidity","errored","checkValidity","isBlurEvent","field","Error","removeAttribute","validity","validationMessage","prevState","fieldProps","undefined","rightAddon","event","isPropagationStopped","currentTarget","document","activeElement","isCounter","messageProps","length","children","onReset","elementRef"],"mappings":"AAAA;AAEA,SAMEA,WAAW,EACXC,MAAM,EACNC,QAAQ,QACH,QAAQ;AAEf,SAASC,OAAO,QAAQ,oBAAoB;AAE5C,SAASC,YAAY,QAAQ,qBAAqB;AAClD,SAASC,aAAa,QAAQ,sBAAsB;AAMpD,SAASC,YAAY,QAAQ,oBAAoB;AACjD,SAOEC,mBAAmB,EACnBC,sBAAsB,EACtBC,gBAAgB,QACX,kBAAkB;AAEzB,MAAMC,OAAO;AACX,aAAa;AACf;AA0dA;;;;CAIC,GACD,OAAO,SAASC,aAEdC,OAAgC;IAChC,MAAM,EACJC,IAAIC,MAAM,EACVC,KAAKC,OAAO,EACZC,IAAI,EACJC,IAAI,EACJC,eAAe,EAAE,EACjBC,WAAW,KAAK,EAChBC,QAAQ,EACRC,OAAO,EACPC,SAAS,EACTC,SAAS,EACTC,SAASf,IAAI,EACbgB,WAAWhB,IAAI,EACfiB,YAAYjB,IAAI,EAChBkB,UAAU,KAAK,EACfC,QAAQ,EACRC,YAAY,EACZC,iBAAiB,aAAa,EAC9BC,iBAAiB,KAAK,EACtBC,mBAAmB,KAAK,EACxBC,WAAWC,aAAa,EACxBC,YAAY3B,gBAAgB,EAC5B4B,gBAAgB3B,IAAI,EACpB4B,eAAe/B,mBAAmB,EAClCgC,kBAAkB/B,sBAAsB,EACzC,GAAGI;IAEJ,MAAMC,KAAKT,aAAaU,QAAQ;IAChC,MAAM0B,YAAY,GAAG3B,GAAG,QAAQ,CAAC;IACjC,MAAM,CAAC4B,UAAU1B,IAAI,GAAGV,cAAcW;IACtC,MAAM,CAAC0B,OAAOC,SAAS,GAAGzC,SAA6B;QACrD,MAAM0C,QACJ,OAAOzB,iBAAiB,aAAaA,iBAAiBA;QAExD,OAAO;YACLyB;YACAC,OAAO;YACPC,cAAc;QAChB;IACF;IACA,MAAM,EAAEF,KAAK,EAAEC,KAAK,EAAEC,YAAY,EAAE,GAAGJ;IAEvC,0EAA0E;IAC1E,kCAAkC;IAClC,MAAMK,QAAQ9C,OAAO;QACnBwC,SAASO,OAAO,EAAEC,kBAAkB;QACpCN,SAAS;YAAEC;YAAOC,OAAO;YAAOC,cAAc;QAAG;IACnD,GAAGE,OAAO;IAEV,MAAME,UAAUjD,OAAO4C;IACvB,MAAMM,gBAAgBnD,YACpB,CAACoD;QACC,MAAMC,QAAQZ,SAASO,OAAO;QAC9B,IAAI,CAACK,OAAO;YACV,MAAM,IAAIC,MAAM;QAClB;QAEA,uEAAuE;QACvE,2BAA2B;QAC3B,wBAAwB,GACxB,IAAIF,eAAenB,oBAAoB,OAAOT,cAAc,UAAU;YACpE6B,MAAM7B,SAAS,GAAGA;QACpB;QAEA,MAAM,EAAEoB,KAAK,EAAE,GAAGS;QAClBA,MAAMJ,iBAAiB,CAAC;QACxBI,MAAMF,aAAa;QAEnB,sEAAsE;QACtE,WAAW;QACX,wBAAwB,GACxB,IAAIlB,oBAAoB,OAAOT,cAAc,UAAU;YACrD6B,MAAME,eAAe,CAAC;QACxB;QAEA,MAAM3C,UAA+B;YACnCgC;YACAtB;YACAD;YACAE;YACAC;YACA4B;YACAhC;YACAW;YACAyB,UAAUH,MAAMG,QAAQ;YACxBC,mBAAmBJ,MAAMI,iBAAiB;QAC5C;QACA,MAAMX,eAAeP,gBAAgB3B;QACrC,MAAMiC,QAAQT,UAAU;YAAE,GAAGxB,OAAO;YAAEkC;QAAa;QAEnD,IAAII,QAAQF,OAAO,KAAKH,OAAO;YAC7BK,QAAQF,OAAO,GAAGH;YAClBR,cAAc;gBACZtB,KAAK0B;gBACLxB;gBACA4B;gBACAC;YACF;QACF;QAEA,wBAAwB,GACxB,IAAIA,iBAAiBO,MAAMI,iBAAiB,EAAE;YAC5CJ,MAAMJ,iBAAiB,CAACH;QAC1B;QAEAH,SAAS,CAACe;YACR,IACEA,UAAUd,KAAK,KAAKA,SACpBc,UAAUb,KAAK,KAAKA,SACpBa,UAAUZ,YAAY,KAAKA,cAC3B;gBACA,OAAOY;YACT;YAEA,OAAO;gBACLd;gBACAC;gBACAC;YACF;QACF;IACF,GACA;QACEb;QACAQ;QACAF;QACAH;QACAhB;QACAI;QACAD;QACAN;QACAoB;QACAf;QACAD;QACAU;KACD;IAGH,MAAMG,YAAY/B,QAAQ,SAASgC;IACnC,MAAMwB,aAEF;QACF9C;QACAE;QACAE;QACA2B;QACAC;QACAxB;QACAC;QACAC;QACAC,WAAWS,mBAAmB2B,YAAYpC;QAC1CqC,YAAYvB,aAAa;YACvBO;YACAX;YACAY;QACF;QACArB,QAAOqC,KAAK;YACVrC,OAAOqC;YACP,IAAIA,MAAMC,oBAAoB,IAAI;gBAChC;YACF;YAEAZ,cAAc;QAChB;QACAzB,UAASoC,KAAK;YACZpC,SAASoC;YACT,IAAIA,MAAMC,oBAAoB,IAAI;gBAChC;YACF;YAEA,IAAIhC,mBAAmB,QAAQ;gBAC7B,MAAM,EAAEa,KAAK,EAAE,GAAGkB,MAAME,aAAa;gBACrCrB,SAAS,CAACe,YAAe,CAAA;wBACvB,GAAGA,SAAS;wBACZd;oBACF,CAAA;gBACA;YACF;YAEAO,cAAc;QAChB;QACAxB,WAAUmC,KAAK;YACbnC,UAAUmC;YACV,IACEA,MAAMC,oBAAoB,MAC1BD,MAAME,aAAa,KAAKC,SAASC,aAAa,EAC9C;gBACA;YACF;YAEA,qEAAqE;YACrE,mDAAmD;YACnD,wEAAwE;YACxE,0EAA0E;YAC1E,kCAAkC;YAClC,MAAM,EAAET,iBAAiB,EAAE,GAAGK,MAAME,aAAa;YAEjDrB,SAAS,CAACe;gBACR,IAAIA,UAAUb,KAAK,EAAE;oBACnB,OAAOa;gBACT;gBAEA,OAAO;oBACL,GAAGA,SAAS;oBACZb,OAAO;oBACPC,cAAcW;gBAChB;YACF;QACF;IACF;IAEA,MAAMU,YAAYvC,WAAW,OAAOJ,cAAc;IAClD,IAAI2C,aAAatC,YAAY,CAACG,gBAAgB;QAC5C2B,UAAU,CAAC,mBAAmB,GAAGnB;QACjCmB,WAAWS,YAAY,GAAG;YACxBvD,IAAI2B;YACJK;YACAwB,QAAQzC,UAAUgB,MAAMyB,MAAM,GAAGT;YACjCpC,WAAW,AAACI,WAAWJ,aAAcoC;YACrCU,UAAU,AAAC,CAACtC,kBAAkBc,gBAAiBjB;QACjD;IACF;IAEAvB,aAAa;QACXY;QACAqD,SAASzC,eAAe8B,YAAYb;QACpCyB,YAAY/B;IACd;IAEA,OAAO;QACL,GAAGC,KAAK;QACRK;QACAJ;QACAF;QACAkB;IACF;AACF"}
|
|
@@ -59,7 +59,7 @@ export declare function findSliderTestElements(options: GetPartsByRoleOptions):
|
|
|
59
59
|
/**
|
|
60
60
|
* @since 6.0.0
|
|
61
61
|
*/
|
|
62
|
-
export interface
|
|
62
|
+
export interface GetRangeSliderTestElementsOptions {
|
|
63
63
|
/** @defaultValue `screen` */
|
|
64
64
|
container?: BoundFunctions<typeof queries>;
|
|
65
65
|
/** @defaultValue `{ name: "Min" }` */
|
|
@@ -118,9 +118,9 @@ export interface RangeSliderTestElements {
|
|
|
118
118
|
*
|
|
119
119
|
* @since 6.0.0
|
|
120
120
|
*/
|
|
121
|
-
export declare function getRangeSliderTestElements(options?:
|
|
121
|
+
export declare function getRangeSliderTestElements(options?: GetRangeSliderTestElementsOptions): RangeSliderTestElements;
|
|
122
122
|
/**
|
|
123
123
|
* @see {@link getRangeSliderTestElements}
|
|
124
124
|
* @since 6.0.0
|
|
125
125
|
*/
|
|
126
|
-
export declare function findRangeSliderTestElements(options?:
|
|
126
|
+
export declare function findRangeSliderTestElements(options?: GetRangeSliderTestElementsOptions): Promise<RangeSliderTestElements>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/test-utils/queries/slider.ts"],"sourcesContent":["import {\n type BoundFunctions,\n type ByRoleOptions,\n type queries,\n screen,\n} from \"@testing-library/dom\";\n\nimport { type GetPartsByRoleOptions } from \"./types.js\";\n\ntype Elements = Omit<SliderTestElements, \"slider\">;\n\nfunction getElements(slider: HTMLSpanElement): Elements {\n const sliderInput = slider.nextElementSibling;\n const sliderTrack = slider.parentElement;\n const sliderContainer = sliderTrack?.parentElement;\n if (!(sliderInput instanceof HTMLInputElement)) {\n throw new Error(\"Unable to find the `Slider` input element\");\n }\n if (\n !(sliderTrack instanceof HTMLSpanElement) ||\n !sliderTrack.classList.contains(\"rmd-slider-track\")\n ) {\n throw new Error(\"Unable to find the `Slider` track element\");\n }\n if (\n !(sliderContainer instanceof HTMLDivElement) ||\n !sliderContainer.classList.contains(\"rmd-slider-container\")\n ) {\n throw new Error(\"Unable to find the `Slider` container element\");\n }\n\n return { sliderInput, sliderTrack, sliderContainer };\n}\n\n/**\n * @since 6.0.0\n */\nexport interface SliderTestElements {\n /**\n * The element that is visible to screen readers and can be updated using\n * drag, touch, or keyboard events. Since this is usually annoying for tests,\n * the `sliderInput` should normally be used instead.\n */\n slider: HTMLSpanElement;\n\n /**\n * The element that is hidden to screen readers but stores the current value\n * and can be updated using\n * `fireEvent.change(sliderInput, { target: { value: \"100\" }})`.\n */\n sliderInput: HTMLInputElement;\n\n /**\n * This is only useful when needing to verify that clicking somewhere on the\n * track updates the value. Most test cases can be solved through the\n * {@link slider} or {@link sliderInput} instead.\n */\n sliderTrack: HTMLSpanElement;\n\n /**\n * Returns the slider container element if it is needed for testing. It's\n * useful for simple snapshot tests.\n */\n sliderContainer: HTMLDivElement;\n}\n\n/**\n * @example Simple Example\n * ```tsx\n * function Test(): ReactElement {\n * const slider = useSlider({ defaultValue: 30 });\n * return <Slider {...slider} aria-label=\"Example\" />;\n * }\n *\n * rmdRender(<Test />);\n *\n * const { slider, sliderInput } = getSliderTestElements({ name: \"Example\" });\n *\n * expect(slider).toHaveValue(30);\n * expect(sliderInput).toHaveValue(\"30\");\n *\n * fireEvent.change(sliderInput, { target: { value: \"55\" }});\n * expect(slider).toHaveValue(55);\n * expect(sliderInput).toHaveValue(\"55\");\n * ```\n *\n * @since 6.0.0\n */\nexport function getSliderTestElements(\n options: GetPartsByRoleOptions\n): SliderTestElements {\n const { container = screen, ...byRoleOptions } = options;\n const slider = container.getByRole<HTMLSpanElement>(\"slider\", byRoleOptions);\n\n return {\n slider,\n ...getElements(slider),\n };\n}\n\n/**\n * @see {@link getSliderTestElements}\n * @since 6.0.0\n */\nexport async function findSliderTestElements(\n options: GetPartsByRoleOptions\n): Promise<SliderTestElements> {\n const { container = screen, ...byRoleOptions } = options;\n const slider = await container.findByRole<HTMLSpanElement>(\n \"slider\",\n byRoleOptions\n );\n\n return {\n slider,\n ...getElements(slider),\n };\n}\n\n/**\n * @since 6.0.0\n */\nexport interface
|
|
1
|
+
{"version":3,"sources":["../../../src/test-utils/queries/slider.ts"],"sourcesContent":["import {\n type BoundFunctions,\n type ByRoleOptions,\n type queries,\n screen,\n} from \"@testing-library/dom\";\n\nimport { type GetPartsByRoleOptions } from \"./types.js\";\n\ntype Elements = Omit<SliderTestElements, \"slider\">;\n\nfunction getElements(slider: HTMLSpanElement): Elements {\n const sliderInput = slider.nextElementSibling;\n const sliderTrack = slider.parentElement;\n const sliderContainer = sliderTrack?.parentElement;\n if (!(sliderInput instanceof HTMLInputElement)) {\n throw new Error(\"Unable to find the `Slider` input element\");\n }\n if (\n !(sliderTrack instanceof HTMLSpanElement) ||\n !sliderTrack.classList.contains(\"rmd-slider-track\")\n ) {\n throw new Error(\"Unable to find the `Slider` track element\");\n }\n if (\n !(sliderContainer instanceof HTMLDivElement) ||\n !sliderContainer.classList.contains(\"rmd-slider-container\")\n ) {\n throw new Error(\"Unable to find the `Slider` container element\");\n }\n\n return { sliderInput, sliderTrack, sliderContainer };\n}\n\n/**\n * @since 6.0.0\n */\nexport interface SliderTestElements {\n /**\n * The element that is visible to screen readers and can be updated using\n * drag, touch, or keyboard events. Since this is usually annoying for tests,\n * the `sliderInput` should normally be used instead.\n */\n slider: HTMLSpanElement;\n\n /**\n * The element that is hidden to screen readers but stores the current value\n * and can be updated using\n * `fireEvent.change(sliderInput, { target: { value: \"100\" }})`.\n */\n sliderInput: HTMLInputElement;\n\n /**\n * This is only useful when needing to verify that clicking somewhere on the\n * track updates the value. Most test cases can be solved through the\n * {@link slider} or {@link sliderInput} instead.\n */\n sliderTrack: HTMLSpanElement;\n\n /**\n * Returns the slider container element if it is needed for testing. It's\n * useful for simple snapshot tests.\n */\n sliderContainer: HTMLDivElement;\n}\n\n/**\n * @example Simple Example\n * ```tsx\n * function Test(): ReactElement {\n * const slider = useSlider({ defaultValue: 30 });\n * return <Slider {...slider} aria-label=\"Example\" />;\n * }\n *\n * rmdRender(<Test />);\n *\n * const { slider, sliderInput } = getSliderTestElements({ name: \"Example\" });\n *\n * expect(slider).toHaveValue(30);\n * expect(sliderInput).toHaveValue(\"30\");\n *\n * fireEvent.change(sliderInput, { target: { value: \"55\" }});\n * expect(slider).toHaveValue(55);\n * expect(sliderInput).toHaveValue(\"55\");\n * ```\n *\n * @since 6.0.0\n */\nexport function getSliderTestElements(\n options: GetPartsByRoleOptions\n): SliderTestElements {\n const { container = screen, ...byRoleOptions } = options;\n const slider = container.getByRole<HTMLSpanElement>(\"slider\", byRoleOptions);\n\n return {\n slider,\n ...getElements(slider),\n };\n}\n\n/**\n * @see {@link getSliderTestElements}\n * @since 6.0.0\n */\nexport async function findSliderTestElements(\n options: GetPartsByRoleOptions\n): Promise<SliderTestElements> {\n const { container = screen, ...byRoleOptions } = options;\n const slider = await container.findByRole<HTMLSpanElement>(\n \"slider\",\n byRoleOptions\n );\n\n return {\n slider,\n ...getElements(slider),\n };\n}\n\n/**\n * @since 6.0.0\n */\nexport interface GetRangeSliderTestElementsOptions {\n /** @defaultValue `screen` */\n container?: BoundFunctions<typeof queries>;\n\n /** @defaultValue `{ name: \"Min\" }` */\n min?: ByRoleOptions;\n\n /** @defaultValue `{ name: \"Max\" }` */\n max?: ByRoleOptions;\n}\n\n/**\n * @since 6.0.0\n */\nexport interface RangeSliderTestElements {\n /** @see {@link SliderTestElements.slider} */\n minSlider: HTMLSpanElement;\n /** @see {@link SliderTestElements.sliderInput} */\n minSliderInput: HTMLInputElement;\n /** @see {@link SliderTestElements.slider} */\n maxSlider: HTMLSpanElement;\n /** @see {@link SliderTestElements.sliderInput} */\n maxSliderInput: HTMLInputElement;\n /** @see {@link SliderTestElements.sliderTrack} */\n sliderTrack: HTMLSpanElement;\n /** @see {@link SliderTestElements.sliderContainer} */\n sliderContainer: HTMLDivElement;\n}\n\n/**\n * @example Simple Example\n * ```tsx\n * function Test(): ReactElement {\n * const slider = useRangeSlider({ defaultValue: [30, 60] });\n * return <Slider {...slider} aria-label=\"Example\" />;\n * }\n *\n * rmdRender(<Test />);\n *\n * const { slider, sliderInput } = getSliderTestElements();\n *\n * const { minSlider, minSliderInput, maxSlider, maxSliderInput } =\n * getRangeSliderTestElements();\n * expect(minSlider).toHaveValue(30);\n * expect(minSliderInput).toHaveValue(\"30\");\n * expect(maxSlider).toHaveAttribute(60);\n * expect(maxSliderInput).toHaveValue(\"60\");\n *\n * fireEvent.change(minSliderInput, { target: { value: \"55\" }});\n * expect(minSlider).toHaveValue(55);\n * expect(minSliderInput).toHaveValue(\"55\");\n * expect(maxSlider).toHaveAttribute(60);\n * expect(maxSliderInput).toHaveValue(\"60\");\n *\n * fireEvent.change(maxSliderInput, { target: { value: \"88\" }});\n * expect(minSlider).toHaveValue(55);\n * expect(minSliderInput).toHaveValue(\"55\");\n * expect(maxSlider).toHaveAttribute(88);\n * expect(maxSliderInput).toHaveValue(\"88\");\n * ```\n *\n * @since 6.0.0\n */\nexport function getRangeSliderTestElements(\n options: GetRangeSliderTestElementsOptions = {}\n): RangeSliderTestElements {\n const { container = screen, min, max } = options;\n const minSlider = container.getByRole<HTMLSpanElement>(\n \"slider\",\n min ?? { name: \"Min\" }\n );\n const maxSlider = container.getByRole<HTMLSpanElement>(\n \"slider\",\n max ?? { name: \"Max\" }\n );\n\n const {\n sliderInput: minSliderInput,\n sliderTrack,\n sliderContainer,\n } = getElements(minSlider);\n const { sliderInput: maxSliderInput } = getElements(maxSlider);\n\n return {\n minSlider,\n minSliderInput,\n maxSlider,\n maxSliderInput,\n sliderTrack,\n sliderContainer,\n };\n}\n\n/**\n * @see {@link getRangeSliderTestElements}\n * @since 6.0.0\n */\nexport async function findRangeSliderTestElements(\n options: GetRangeSliderTestElementsOptions = {}\n): Promise<RangeSliderTestElements> {\n const { container = screen, min, max } = options;\n const minSlider = await container.findByRole<HTMLSpanElement>(\n \"slider\",\n min ?? { name: \"Min\" }\n );\n const maxSlider = await container.findByRole<HTMLSpanElement>(\n \"slider\",\n max ?? { name: \"Max\" }\n );\n const {\n sliderInput: minSliderInput,\n sliderTrack,\n sliderContainer,\n } = getElements(minSlider);\n const { sliderInput: maxSliderInput } = getElements(maxSlider);\n\n return {\n minSlider,\n minSliderInput,\n maxSlider,\n maxSliderInput,\n sliderTrack,\n sliderContainer,\n };\n}\n"],"names":["screen","getElements","slider","sliderInput","nextElementSibling","sliderTrack","parentElement","sliderContainer","HTMLInputElement","Error","HTMLSpanElement","classList","contains","HTMLDivElement","getSliderTestElements","options","container","byRoleOptions","getByRole","findSliderTestElements","findByRole","getRangeSliderTestElements","min","max","minSlider","name","maxSlider","minSliderInput","maxSliderInput","findRangeSliderTestElements"],"mappings":"AAAA,SAIEA,MAAM,QACD,uBAAuB;AAM9B,SAASC,YAAYC,MAAuB;IAC1C,MAAMC,cAAcD,OAAOE,kBAAkB;IAC7C,MAAMC,cAAcH,OAAOI,aAAa;IACxC,MAAMC,kBAAkBF,aAAaC;IACrC,IAAI,CAAEH,CAAAA,uBAAuBK,gBAAe,GAAI;QAC9C,MAAM,IAAIC,MAAM;IAClB;IACA,IACE,CAAEJ,CAAAA,uBAAuBK,eAAc,KACvC,CAACL,YAAYM,SAAS,CAACC,QAAQ,CAAC,qBAChC;QACA,MAAM,IAAIH,MAAM;IAClB;IACA,IACE,CAAEF,CAAAA,2BAA2BM,cAAa,KAC1C,CAACN,gBAAgBI,SAAS,CAACC,QAAQ,CAAC,yBACpC;QACA,MAAM,IAAIH,MAAM;IAClB;IAEA,OAAO;QAAEN;QAAaE;QAAaE;IAAgB;AACrD;AAkCA;;;;;;;;;;;;;;;;;;;;;CAqBC,GACD,OAAO,SAASO,sBACdC,OAA8B;IAE9B,MAAM,EAAEC,YAAYhB,MAAM,EAAE,GAAGiB,eAAe,GAAGF;IACjD,MAAMb,SAASc,UAAUE,SAAS,CAAkB,UAAUD;IAE9D,OAAO;QACLf;QACA,GAAGD,YAAYC,OAAO;IACxB;AACF;AAEA;;;CAGC,GACD,OAAO,eAAeiB,uBACpBJ,OAA8B;IAE9B,MAAM,EAAEC,YAAYhB,MAAM,EAAE,GAAGiB,eAAe,GAAGF;IACjD,MAAMb,SAAS,MAAMc,UAAUI,UAAU,CACvC,UACAH;IAGF,OAAO;QACLf;QACA,GAAGD,YAAYC,OAAO;IACxB;AACF;AAkCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCC,GACD,OAAO,SAASmB,2BACdN,UAA6C,CAAC,CAAC;IAE/C,MAAM,EAAEC,YAAYhB,MAAM,EAAEsB,GAAG,EAAEC,GAAG,EAAE,GAAGR;IACzC,MAAMS,YAAYR,UAAUE,SAAS,CACnC,UACAI,OAAO;QAAEG,MAAM;IAAM;IAEvB,MAAMC,YAAYV,UAAUE,SAAS,CACnC,UACAK,OAAO;QAAEE,MAAM;IAAM;IAGvB,MAAM,EACJtB,aAAawB,cAAc,EAC3BtB,WAAW,EACXE,eAAe,EAChB,GAAGN,YAAYuB;IAChB,MAAM,EAAErB,aAAayB,cAAc,EAAE,GAAG3B,YAAYyB;IAEpD,OAAO;QACLF;QACAG;QACAD;QACAE;QACAvB;QACAE;IACF;AACF;AAEA;;;CAGC,GACD,OAAO,eAAesB,4BACpBd,UAA6C,CAAC,CAAC;IAE/C,MAAM,EAAEC,YAAYhB,MAAM,EAAEsB,GAAG,EAAEC,GAAG,EAAE,GAAGR;IACzC,MAAMS,YAAY,MAAMR,UAAUI,UAAU,CAC1C,UACAE,OAAO;QAAEG,MAAM;IAAM;IAEvB,MAAMC,YAAY,MAAMV,UAAUI,UAAU,CAC1C,UACAG,OAAO;QAAEE,MAAM;IAAM;IAEvB,MAAM,EACJtB,aAAawB,cAAc,EAC3BtB,WAAW,EACXE,eAAe,EAChB,GAAGN,YAAYuB;IAChB,MAAM,EAAErB,aAAayB,cAAc,EAAE,GAAG3B,YAAYyB;IAEpD,OAAO;QACLF;QACAG;QACAD;QACAE;QACAvB;QACAE;IACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-md/core",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.4",
|
|
4
4
|
"description": "The core components and functionality for react-md.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sass": "./dist/_core.scss",
|
|
@@ -74,18 +74,18 @@
|
|
|
74
74
|
"license": "MIT",
|
|
75
75
|
"dependencies": {
|
|
76
76
|
"cnbuilder": "^3.1.0",
|
|
77
|
-
"nanoid": "^5.1.
|
|
77
|
+
"nanoid": "^5.1.6",
|
|
78
78
|
"remove-accents": "^0.5.0"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"@jest/globals": "^29.7.0",
|
|
82
82
|
"@jest/types": "^29.6.3",
|
|
83
|
-
"@microsoft/api-extractor": "^7.
|
|
83
|
+
"@microsoft/api-extractor": "^7.53.0",
|
|
84
84
|
"@swc/cli": "^0.6.0",
|
|
85
|
-
"@swc/core": "^1.13.
|
|
85
|
+
"@swc/core": "^1.13.20",
|
|
86
86
|
"@swc/jest": "^0.2.39",
|
|
87
87
|
"@testing-library/dom": "^10.4.1",
|
|
88
|
-
"@testing-library/jest-dom": "^6.
|
|
88
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
89
89
|
"@testing-library/react": "^16.3.0",
|
|
90
90
|
"@testing-library/user-event": "^14.6.1",
|
|
91
91
|
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
|
@@ -94,8 +94,8 @@
|
|
|
94
94
|
"@types/react": "^18.3.12",
|
|
95
95
|
"@types/react-dom": "^18.3.1",
|
|
96
96
|
"chokidar": "^4.0.3",
|
|
97
|
-
"eslint": "^9.
|
|
98
|
-
"filesize": "^11.0.
|
|
97
|
+
"eslint": "^9.37.0",
|
|
98
|
+
"filesize": "^11.0.13",
|
|
99
99
|
"glob": "11.0.3",
|
|
100
100
|
"jest": "^29.7.0",
|
|
101
101
|
"jest-environment-jsdom": "^29.7.0",
|
|
@@ -104,21 +104,18 @@
|
|
|
104
104
|
"lz-string": "^1.5.0",
|
|
105
105
|
"npm-run-all": "^4.1.5",
|
|
106
106
|
"prettier": "^3.6.2",
|
|
107
|
-
"stylelint": "^16.
|
|
108
|
-
"
|
|
109
|
-
"stylelint-config-recommended-scss": "^15.0.1",
|
|
110
|
-
"stylelint-order": "^7.0.0",
|
|
111
|
-
"stylelint-scss": "^6.12.1",
|
|
112
|
-
"ts-morph": "^26.0.0",
|
|
107
|
+
"stylelint": "^16.24.0",
|
|
108
|
+
"ts-morph": "^27.0.0",
|
|
113
109
|
"ts-node": "^10.9.2",
|
|
114
|
-
"tsx": "^4.20.
|
|
110
|
+
"tsx": "^4.20.6",
|
|
115
111
|
"typescript": "^5.8.3",
|
|
116
112
|
"vitest": "^3.2.4",
|
|
117
|
-
"@react-md/eslint-config": "1.0.0"
|
|
113
|
+
"@react-md/eslint-config": "1.0.0",
|
|
114
|
+
"@react-md/stylelint-config": "1.0.0"
|
|
118
115
|
},
|
|
119
116
|
"peerDependencies": {
|
|
120
|
-
"@jest/globals": "
|
|
121
|
-
"@jest/types": "
|
|
117
|
+
"@jest/globals": ">= 29 <=30",
|
|
118
|
+
"@jest/types": ">= 29 <=30",
|
|
122
119
|
"@testing-library/dom": ">= 9",
|
|
123
120
|
"@testing-library/jest-dom": ">= 6",
|
|
124
121
|
"@testing-library/react": ">= 14",
|
package/src/box/styles.ts
CHANGED
|
@@ -254,6 +254,20 @@ export interface BoxOptions {
|
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
/**
|
|
257
|
+
* Applies the `className` from the `Box` component to any element.
|
|
258
|
+
*
|
|
259
|
+
* @example Simple Example
|
|
260
|
+
* ```tsx
|
|
261
|
+
* return (
|
|
262
|
+
* <form className={box({ stacked: true})}>
|
|
263
|
+
* {children}
|
|
264
|
+
* </form>
|
|
265
|
+
* );
|
|
266
|
+
* ```
|
|
267
|
+
*
|
|
268
|
+
* If custom media query breakpoints are required, use the {@link boxStyles}
|
|
269
|
+
* function instead which will also set the CSS variables.
|
|
270
|
+
*
|
|
257
271
|
* @see {@link boxStyles}
|
|
258
272
|
* @since 6.0.0
|
|
259
273
|
*/
|
|
@@ -442,6 +456,31 @@ export interface BoxStylesOptions extends BoxOptions {
|
|
|
442
456
|
}
|
|
443
457
|
|
|
444
458
|
/**
|
|
459
|
+
* Used to apply the styles from the `Box` component to any element by
|
|
460
|
+
* providing a `style` object with CSS variables and a `className`.
|
|
461
|
+
*
|
|
462
|
+
* If you do not need to use
|
|
463
|
+
*
|
|
464
|
+
* @example Simple Example
|
|
465
|
+
* ```tsx
|
|
466
|
+
* return (
|
|
467
|
+
* <form
|
|
468
|
+
* {...boxStyles({
|
|
469
|
+
* grid: true,
|
|
470
|
+
* gridColumns: {
|
|
471
|
+
* phone: 1,
|
|
472
|
+
* desktop: 3,
|
|
473
|
+
* },
|
|
474
|
+
* gridItemSize: {
|
|
475
|
+
* tablet: "12rem",
|
|
476
|
+
* },
|
|
477
|
+
* })}
|
|
478
|
+
* >
|
|
479
|
+
* {children}
|
|
480
|
+
* </form>
|
|
481
|
+
* );
|
|
482
|
+
* ```
|
|
483
|
+
*
|
|
445
484
|
* @see {@link box}
|
|
446
485
|
* @since 6.0.0
|
|
447
486
|
*/
|
package/src/form/useTextField.ts
CHANGED
|
@@ -138,7 +138,7 @@ export interface ProvidedFormMessageProps
|
|
|
138
138
|
export interface ProvidedTextFieldProps<
|
|
139
139
|
E extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement,
|
|
140
140
|
> extends TextFieldValidationOptions,
|
|
141
|
-
TextFieldChangeHandlers<E
|
|
141
|
+
Required<TextFieldChangeHandlers<E>>,
|
|
142
142
|
Required<Pick<TextFieldProps, "id" | "name" | "value" | "error">>,
|
|
143
143
|
Pick<TextFieldProps, "aria-describedby" | "rightAddon"> {
|
|
144
144
|
/**
|
|
@@ -120,7 +120,7 @@ export async function findSliderTestElements(
|
|
|
120
120
|
/**
|
|
121
121
|
* @since 6.0.0
|
|
122
122
|
*/
|
|
123
|
-
export interface
|
|
123
|
+
export interface GetRangeSliderTestElementsOptions {
|
|
124
124
|
/** @defaultValue `screen` */
|
|
125
125
|
container?: BoundFunctions<typeof queries>;
|
|
126
126
|
|
|
@@ -184,7 +184,7 @@ export interface RangeSliderTestElements {
|
|
|
184
184
|
* @since 6.0.0
|
|
185
185
|
*/
|
|
186
186
|
export function getRangeSliderTestElements(
|
|
187
|
-
options:
|
|
187
|
+
options: GetRangeSliderTestElementsOptions = {}
|
|
188
188
|
): RangeSliderTestElements {
|
|
189
189
|
const { container = screen, min, max } = options;
|
|
190
190
|
const minSlider = container.getByRole<HTMLSpanElement>(
|
|
@@ -218,7 +218,7 @@ export function getRangeSliderTestElements(
|
|
|
218
218
|
* @since 6.0.0
|
|
219
219
|
*/
|
|
220
220
|
export async function findRangeSliderTestElements(
|
|
221
|
-
options:
|
|
221
|
+
options: GetRangeSliderTestElementsOptions = {}
|
|
222
222
|
): Promise<RangeSliderTestElements> {
|
|
223
223
|
const { container = screen, min, max } = options;
|
|
224
224
|
const minSlider = await container.findByRole<HTMLSpanElement>(
|