@react-md/core 1.0.0-next.17 → 1.0.0-next.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/chip/_chip.scss +3 -2
  2. package/dist/files/validation.d.ts +2 -2
  3. package/dist/files/validation.js +1 -1
  4. package/dist/files/validation.js.map +1 -1
  5. package/dist/form/SelectedOption.js +2 -1
  6. package/dist/form/SelectedOption.js.map +1 -1
  7. package/dist/form/_form.scss +9 -5
  8. package/dist/icon/FontIcon.d.ts +4 -2
  9. package/dist/icon/FontIcon.js.map +1 -1
  10. package/dist/icon/TextIconSpacing.d.ts +3 -1
  11. package/dist/icon/TextIconSpacing.js.map +1 -1
  12. package/dist/test-utils/ResizeObserver.d.ts +11 -12
  13. package/dist/test-utils/ResizeObserver.js +11 -12
  14. package/dist/test-utils/ResizeObserver.js.map +1 -1
  15. package/dist/test-utils/matchMedia.d.ts +2 -2
  16. package/dist/test-utils/matchMedia.js +2 -2
  17. package/dist/test-utils/matchMedia.js.map +1 -1
  18. package/dist/theme/_a11y.scss +12 -3
  19. package/dist/useResizeObserver.d.ts +19 -0
  20. package/dist/useResizeObserver.js +19 -0
  21. package/dist/useResizeObserver.js.map +1 -1
  22. package/dist/utils/RenderRecursively.d.ts +1 -1
  23. package/dist/utils/RenderRecursively.js.map +1 -1
  24. package/dist/utils/alphaNumericSort.d.ts +4 -4
  25. package/dist/utils/alphaNumericSort.js.map +1 -1
  26. package/dist/utils/bem.d.ts +1 -1
  27. package/dist/utils/bem.js +1 -1
  28. package/dist/utils/bem.js.map +1 -1
  29. package/package.json +16 -16
  30. package/src/files/validation.ts +2 -2
  31. package/src/form/SelectedOption.tsx +2 -0
  32. package/src/icon/FontIcon.tsx +4 -2
  33. package/src/icon/TextIconSpacing.tsx +1 -1
  34. package/src/test-utils/ResizeObserver.ts +11 -12
  35. package/src/test-utils/matchMedia.ts +2 -2
  36. package/src/useResizeObserver.ts +19 -0
  37. package/src/utils/RenderRecursively.tsx +1 -1
  38. package/src/utils/alphaNumericSort.ts +4 -4
  39. package/src/utils/bem.ts +1 -1
@@ -278,8 +278,9 @@ $variables: (
278
278
 
279
279
  @include box-shadows.box-shadow-transition(
280
280
  $start-shadow: $outline-box-shadow,
281
- $end-shadow:
282
- box-shadows.box-shadow($outline-raisable-box-shadow-z-value),
281
+ $end-shadow: box-shadows.box-shadow(
282
+ $outline-raisable-box-shadow-z-value
283
+ ),
283
284
  $duration: $outline-raisable-transition-duration,
284
285
  $active-selectors: "&.rmd-chip--pressed",
285
286
  $pseudo-before: false,
@@ -265,7 +265,7 @@ export interface ValidatedFilesResult<CustomError> {
265
265
  * };
266
266
  * ```
267
267
  *
268
- * @typeparam E - An optional custom file validation error.
268
+ * @typeParam E - An optional custom file validation error.
269
269
  * @param files - The list of files to check
270
270
  * @param options - The {@link FilesValidationOptions}
271
271
  * @returns the {@link ValidatedFilesResult}
@@ -278,7 +278,7 @@ export type FilesValidator<CustomError = never> = (files: readonly File[], optio
278
278
  * {@link useFileUpload} that ensures the {@link FilesValidationOptions} are
279
279
  * enforced before allowing a file to be uploaded.
280
280
  *
281
- * @typeparam E - An optional custom file validation error.
281
+ * @typeParam E - An optional custom file validation error.
282
282
  * @param files - The list of files to check
283
283
  * @param options - The {@link FilesValidationOptions}
284
284
  * @returns the {@link ValidatedFilesResult}
@@ -157,7 +157,7 @@ import { nanoid } from "nanoid";
157
157
  * {@link useFileUpload} that ensures the {@link FilesValidationOptions} are
158
158
  * enforced before allowing a file to be uploaded.
159
159
  *
160
- * @typeparam E - An optional custom file validation error.
160
+ * @typeParam E - An optional custom file validation error.
161
161
  * @param files - The list of files to check
162
162
  * @param options - The {@link FilesValidationOptions}
163
163
  * @returns the {@link ValidatedFilesResult}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/files/validation.ts"],"sourcesContent":["import { nanoid } from \"nanoid\";\n\n/**\n * An error that will be created if a user tries dragging and dropping files\n * from a shared directory that they do not have access to. This error will not\n * occur much.\n *\n * @since 2.9.0\n */\nexport class FileAccessError extends Error {\n /**\n * A unique key generated by `nanoid` that can be used as a `React` key\n */\n public key: string;\n\n /**\n *\n * @param message - An optional message for the error.\n */\n constructor(message?: string) {\n super(message);\n this.key = nanoid();\n this.name = \"FileAccessError\";\n }\n}\n\n/**\n * An error that just requires a `File` to be passed as the first argument.\n *\n * @since 2.9.0\n */\nexport class GenericFileError extends Error {\n /**\n * A unique key generated by `nanoid` that can be used as a `React` key\n */\n public key: string;\n\n /**\n *\n * @param files - A list of files that caused the error.\n * @param reason - An optional reason for the error\n */\n constructor(\n public files: readonly File[],\n public reason?: string\n ) {\n super(\"Invalid files\");\n this.key = nanoid();\n this.name = \"GenericFileError\";\n }\n}\n\n/**\n * An error that is created during the upload process if the number of files\n * exceeds the {@link FileUploadOptions.maxFiles} amount.\n *\n * @since 2.9.0\n */\nexport class TooManyFilesError extends GenericFileError {\n /**\n *\n * @param files - The list of files that could not be uploaded due to the file\n * limit defined.\n * @param limit - The max limit of files allowed.\n */\n constructor(\n files: readonly File[],\n public limit: number\n ) {\n super(files, \"file limit\");\n this.name = \"TooManyFilesError\";\n }\n}\n\n/**\n * An error that will be created if a user tries to upload a file that\n * is either:\n * - less than the {@link FileValidationOptions.minFileSize}\n * - greater than the {@link FileValidationOptions.maxFileSize}\n * - including the file would be greater than the {@link FileValidationOptions.totalFileSize}\n *\n * @since 2.9.0\n */\nexport class FileSizeError extends GenericFileError {\n /**\n *\n * @param files - The list of files that have the file size error\n * @param type - The file size error type\n * @param limit - The number of bytes allowed based on the type\n */\n constructor(\n files: readonly File[],\n public type: \"min\" | \"max\" | \"total\",\n public limit: number\n ) {\n super(files, \"file size\");\n this.name = \"FileSizeError\";\n }\n}\n\n/**\n * An error that will be created if a user tries to upload a file that does not\n * end with one of the {@link FileValidationOptions.extensions}.\n *\n * @since 2.9.0\n */\nexport class FileExtensionError extends GenericFileError {\n /**\n *\n * @param files - The file that caused the error\n * @param extensions - The allowed list of file extensions\n */\n constructor(\n files: readonly File[],\n public extensions: readonly string[]\n ) {\n super(files, \"extension\");\n this.name = \"FileExtensionError\";\n }\n}\n\n/**\n * Mostly an internal type that is used to allow custom validation errors\n *\n * @since 2.9.0\n */\nexport type FileValidationError<E = GenericFileError> =\n | FileAccessError\n | TooManyFilesError\n | FileSizeError\n | FileExtensionError\n | E;\n\n/**\n * A simple type-guard that can be used to check if the\n * {@link FileValidationError} is the {@link GenericFileError} which can be\n * useful when displaying the errors to the user.\n *\n * @param error - The error to check\n * @returns true if the error is a {@link FileAccessError}\n */\nexport function isGenericFileError<CustomError extends object>(\n error: FileValidationError<CustomError>\n): error is GenericFileError {\n return \"name\" in error && error.name === \"GenericFileError\";\n}\n\n/**\n * A simple type-guard that can be used to check if the\n * {@link FileValidationError} is the {@link FileAccessError} which can be\n * useful when displaying the errors to the user.\n *\n * @param error - The error to check\n * @returns true if the error is a {@link FileAccessError}\n */\nexport function isFileAccessError<CustomError extends object>(\n error: FileValidationError<CustomError>\n): error is FileAccessError {\n return \"name\" in error && error.name === \"FileAccessError\";\n}\n\n/**\n * A simple type-guard that can be used to check if the\n * {@link FileValidationError} is the {@link TooManyFilesError} which can be\n * useful when displaying the errors to the user.\n *\n * @param error - The error to check\n * @returns true if the error is a {@link TooManyFilesError}\n */\nexport function isTooManyFilesError<CustomError extends object>(\n error: FileValidationError<CustomError>\n): error is TooManyFilesError {\n return \"name\" in error && error.name === \"TooManyFilesError\";\n}\n\n/**\n * A simple type-guard that can be used to check if the\n * {@link FileValidationError} is the {@link FileSizeError} which can be\n * useful when displaying the errors to the user.\n *\n * @param error - The error to check\n * @returns true if the error is a {@link FileSizeError}\n */\nexport function isFileSizeError<CustomError extends object>(\n error: FileValidationError<CustomError>\n): error is FileSizeError {\n return \"name\" in error && error.name === \"FileSizeError\";\n}\n\n/**\n * A simple type-guard that can be used to check if the\n * {@link FileValidationError} is the {@link FileExtensionError} which can be\n * useful when displaying the errors to the user.\n *\n * @param error - The error to check\n * @returns true if the error is a {@link FileExtensionError}\n */\nexport function isFileExtensionError<CustomError extends object>(\n error: FileValidationError<CustomError>\n): error is FileExtensionError {\n return \"name\" in error && error.name === \"FileExtensionError\";\n}\n\n/**\n * This function is used to determine if a file should be added to the\n * {@link FileExtensionError}. The default implementation should work for most\n * use cases except when files that do not have extensions can be uploaded. i.e.\n * LICENSE files.\n *\n * @param file - The file being checked\n * @param extensionRegExp - A regex that will only be defined if the\n * `extensions` list had at least one value.\n * @param extensions - The list of extensions allowed\n * @returns true if the file has a valid name.\n * @since 3.1.0\n */\nexport type IsValidFileName = (\n file: File,\n extensionRegExp: RegExp | undefined,\n extensions: readonly string[]\n) => boolean;\n\n/**\n *\n * @defaultValue `matcher?.test(file.name) ?? true`\n * @since 3.1.0\n */\nexport const isValidFileName: IsValidFileName = (file, matcher) =>\n matcher?.test(file.name) ?? true;\n\n/** @since 2.9.0 */\nexport interface FileValidationOptions {\n /**\n * If the number of files should be limited, set this value to a number\n * greater than `0`.\n *\n * Note: This still allows \"infinite\" files when set to `0` since the\n * `<input>` element should normally be set to `disabled` if files should not\n * be able to be uploaded.\n *\n * @defaultValue `-1`\n */\n maxFiles?: number;\n\n /**\n * An optional minimum file size to enforce for each file. This will only be\n * used when it is greater than `0`.\n *\n * @defaultValue `-1`\n */\n minFileSize?: number;\n\n /**\n * An optional maximum file size to enforce for each file. This will only be\n * used when it is greater than `0`.\n *\n * @defaultValue `-1`\n */\n maxFileSize?: number;\n\n /**\n * An optional list of extensions to enforce when uploading files.\n *\n * Note: The extensions and file names will be compared ignoring case.\n *\n * @example Only Allow Images\n * ```ts\n * const extensions = [\"png\", \"jpeg\", \"jpg\", \"gif\"];\n * ```\n */\n extensions?: readonly string[];\n\n /** {@inheritDoc IsValidFileName} */\n isValidFileName?: IsValidFileName;\n\n /**\n * An optional total file size to enforce when the {@link maxFiles} option is\n * not set to `1`.\n *\n * @defaultValue `-1`\n */\n totalFileSize?: number;\n}\n\n/** @since 2.9.0 */\nexport interface FilesValidationOptions\n extends Required<FileValidationOptions> {\n /**\n * The total number of bytes in the {@link FileUploadHookReturnValue.stats}\n * list. This is really just:\n *\n * ```ts\n * const totalBytes = stats.reduce((total, stat) => total + stat.file.size, 0);\n * ```\n */\n totalBytes: number;\n\n /**\n * The total number of files in the {@link FileUploadHookReturnValue.stats}.\n */\n totalFiles: number;\n}\n\n/**\n * @since 2.9.0\n */\nexport interface ValidatedFilesResult<CustomError> {\n /**\n * A filtered list of files that have been validated and can be queued for the\n * upload process.\n */\n pending: readonly File[];\n\n /**\n * A list of {@link FileValidationError} that occurred during the validation\n * step.\n *\n * Note: If an error has occurred, the file **should not** be added to the\n * {@link pending} list of files.\n */\n errors: readonly FileValidationError<CustomError>[];\n}\n\n/**\n * This function will be called whenever a file has been uploaded by the user\n * either through an `<input type=\"file\">` or drag and drop behavior.\n *\n * @example Simple Example\n * ```ts\n * const validateFiles: FilesValidator = (files, options) => {\n * const invalid: File[] = [];\n * const pending: File[] = [];\n * for (const file of files) {\n * if (!/\\.(jpe?g|svg|png)$/i.test(name)) {\n * invalid.push(file);\n * } else {\n * pending.push(file);\n * }\n * }\n *\n * const errors: FileValidationError[] = [];\n * if (invalid.length) {\n * errors.push(new GenericFileError(invalid))\n * }\n *\n * return { pending, errors };\n * };\n * ```\n *\n * @typeparam E - An optional custom file validation error.\n * @param files - The list of files to check\n * @param options - The {@link FilesValidationOptions}\n * @returns the {@link ValidatedFilesResult}\n * @see {@link validateFiles} for the default implementation\n * @since 2.9.0\n */\nexport type FilesValidator<CustomError = never> = (\n files: readonly File[],\n options: FilesValidationOptions\n) => ValidatedFilesResult<CustomError>;\n\n/**\n * A pretty decent default implementation for validating files with the\n * {@link useFileUpload} that ensures the {@link FilesValidationOptions} are\n * enforced before allowing a file to be uploaded.\n *\n * @typeparam E - An optional custom file validation error.\n * @param files - The list of files to check\n * @param options - The {@link FilesValidationOptions}\n * @returns the {@link ValidatedFilesResult}\n * @since 2.9.0\n */\nexport function validateFiles<CustomError>(\n files: readonly File[],\n options: FilesValidationOptions\n): ValidatedFilesResult<CustomError> {\n const {\n maxFiles,\n extensions,\n minFileSize,\n maxFileSize,\n totalBytes,\n totalFiles,\n totalFileSize,\n isValidFileName,\n } = options;\n\n const errors: FileValidationError<CustomError>[] = [];\n const pending: File[] = [];\n const extraFiles: File[] = [];\n const extensionRegExp =\n extensions.length > 0\n ? new RegExp(`\\\\.(${extensions.join(\"|\")})$`, \"i\")\n : undefined;\n\n let maxFilesReached = maxFiles > 0 && totalFiles >= maxFiles;\n let remainingBytes = totalFileSize - totalBytes;\n const extensionErrors: File[] = [];\n const minErrors: File[] = [];\n const maxErrors: File[] = [];\n const totalSizeErrors: File[] = [];\n for (let i = 0; i < files.length; i += 1) {\n const file = files[i];\n\n let valid = true;\n const { size } = file;\n if (!isValidFileName(file, extensionRegExp, extensions)) {\n valid = false;\n extensionErrors.push(file);\n }\n\n if (minFileSize > 0 && size < minFileSize) {\n valid = false;\n minErrors.push(file);\n }\n\n if (maxFileSize > 0 && size > maxFileSize) {\n valid = false;\n maxErrors.push(file);\n } else if (totalFileSize > 0 && remainingBytes - file.size < 0) {\n // don't want both errors displaying\n valid = false;\n totalSizeErrors.push(file);\n }\n\n if (maxFilesReached && valid) {\n extraFiles.push(file);\n } else if (!maxFilesReached && valid) {\n pending.push(file);\n remainingBytes -= file.size;\n maxFilesReached =\n maxFilesReached ||\n (maxFiles > 0 && totalFiles + pending.length >= maxFiles);\n }\n }\n\n if (extensionErrors.length) {\n errors.push(new FileExtensionError(extensionErrors, extensions));\n }\n\n if (minErrors.length) {\n errors.push(new FileSizeError(minErrors, \"min\", minFileSize));\n }\n\n if (maxErrors.length) {\n errors.push(new FileSizeError(maxErrors, \"max\", maxFileSize));\n }\n\n if (totalSizeErrors.length) {\n errors.push(new FileSizeError(totalSizeErrors, \"total\", totalFileSize));\n }\n\n if (extraFiles.length) {\n errors.push(new TooManyFilesError(extraFiles, maxFiles));\n }\n\n return { pending, errors };\n}\n"],"names":["nanoid","FileAccessError","Error","constructor","message","key","name","GenericFileError","files","reason","TooManyFilesError","limit","FileSizeError","type","FileExtensionError","extensions","isGenericFileError","error","isFileAccessError","isTooManyFilesError","isFileSizeError","isFileExtensionError","isValidFileName","file","matcher","test","validateFiles","options","maxFiles","minFileSize","maxFileSize","totalBytes","totalFiles","totalFileSize","errors","pending","extraFiles","extensionRegExp","length","RegExp","join","undefined","maxFilesReached","remainingBytes","extensionErrors","minErrors","maxErrors","totalSizeErrors","i","valid","size","push"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/files/validation.ts"],"sourcesContent":["import { nanoid } from \"nanoid\";\n\n/**\n * An error that will be created if a user tries dragging and dropping files\n * from a shared directory that they do not have access to. This error will not\n * occur much.\n *\n * @since 2.9.0\n */\nexport class FileAccessError extends Error {\n /**\n * A unique key generated by `nanoid` that can be used as a `React` key\n */\n public key: string;\n\n /**\n *\n * @param message - An optional message for the error.\n */\n constructor(message?: string) {\n super(message);\n this.key = nanoid();\n this.name = \"FileAccessError\";\n }\n}\n\n/**\n * An error that just requires a `File` to be passed as the first argument.\n *\n * @since 2.9.0\n */\nexport class GenericFileError extends Error {\n /**\n * A unique key generated by `nanoid` that can be used as a `React` key\n */\n public key: string;\n\n /**\n *\n * @param files - A list of files that caused the error.\n * @param reason - An optional reason for the error\n */\n constructor(\n public files: readonly File[],\n public reason?: string\n ) {\n super(\"Invalid files\");\n this.key = nanoid();\n this.name = \"GenericFileError\";\n }\n}\n\n/**\n * An error that is created during the upload process if the number of files\n * exceeds the {@link FileUploadOptions.maxFiles} amount.\n *\n * @since 2.9.0\n */\nexport class TooManyFilesError extends GenericFileError {\n /**\n *\n * @param files - The list of files that could not be uploaded due to the file\n * limit defined.\n * @param limit - The max limit of files allowed.\n */\n constructor(\n files: readonly File[],\n public limit: number\n ) {\n super(files, \"file limit\");\n this.name = \"TooManyFilesError\";\n }\n}\n\n/**\n * An error that will be created if a user tries to upload a file that\n * is either:\n * - less than the {@link FileValidationOptions.minFileSize}\n * - greater than the {@link FileValidationOptions.maxFileSize}\n * - including the file would be greater than the {@link FileValidationOptions.totalFileSize}\n *\n * @since 2.9.0\n */\nexport class FileSizeError extends GenericFileError {\n /**\n *\n * @param files - The list of files that have the file size error\n * @param type - The file size error type\n * @param limit - The number of bytes allowed based on the type\n */\n constructor(\n files: readonly File[],\n public type: \"min\" | \"max\" | \"total\",\n public limit: number\n ) {\n super(files, \"file size\");\n this.name = \"FileSizeError\";\n }\n}\n\n/**\n * An error that will be created if a user tries to upload a file that does not\n * end with one of the {@link FileValidationOptions.extensions}.\n *\n * @since 2.9.0\n */\nexport class FileExtensionError extends GenericFileError {\n /**\n *\n * @param files - The file that caused the error\n * @param extensions - The allowed list of file extensions\n */\n constructor(\n files: readonly File[],\n public extensions: readonly string[]\n ) {\n super(files, \"extension\");\n this.name = \"FileExtensionError\";\n }\n}\n\n/**\n * Mostly an internal type that is used to allow custom validation errors\n *\n * @since 2.9.0\n */\nexport type FileValidationError<E = GenericFileError> =\n | FileAccessError\n | TooManyFilesError\n | FileSizeError\n | FileExtensionError\n | E;\n\n/**\n * A simple type-guard that can be used to check if the\n * {@link FileValidationError} is the {@link GenericFileError} which can be\n * useful when displaying the errors to the user.\n *\n * @param error - The error to check\n * @returns true if the error is a {@link FileAccessError}\n */\nexport function isGenericFileError<CustomError extends object>(\n error: FileValidationError<CustomError>\n): error is GenericFileError {\n return \"name\" in error && error.name === \"GenericFileError\";\n}\n\n/**\n * A simple type-guard that can be used to check if the\n * {@link FileValidationError} is the {@link FileAccessError} which can be\n * useful when displaying the errors to the user.\n *\n * @param error - The error to check\n * @returns true if the error is a {@link FileAccessError}\n */\nexport function isFileAccessError<CustomError extends object>(\n error: FileValidationError<CustomError>\n): error is FileAccessError {\n return \"name\" in error && error.name === \"FileAccessError\";\n}\n\n/**\n * A simple type-guard that can be used to check if the\n * {@link FileValidationError} is the {@link TooManyFilesError} which can be\n * useful when displaying the errors to the user.\n *\n * @param error - The error to check\n * @returns true if the error is a {@link TooManyFilesError}\n */\nexport function isTooManyFilesError<CustomError extends object>(\n error: FileValidationError<CustomError>\n): error is TooManyFilesError {\n return \"name\" in error && error.name === \"TooManyFilesError\";\n}\n\n/**\n * A simple type-guard that can be used to check if the\n * {@link FileValidationError} is the {@link FileSizeError} which can be\n * useful when displaying the errors to the user.\n *\n * @param error - The error to check\n * @returns true if the error is a {@link FileSizeError}\n */\nexport function isFileSizeError<CustomError extends object>(\n error: FileValidationError<CustomError>\n): error is FileSizeError {\n return \"name\" in error && error.name === \"FileSizeError\";\n}\n\n/**\n * A simple type-guard that can be used to check if the\n * {@link FileValidationError} is the {@link FileExtensionError} which can be\n * useful when displaying the errors to the user.\n *\n * @param error - The error to check\n * @returns true if the error is a {@link FileExtensionError}\n */\nexport function isFileExtensionError<CustomError extends object>(\n error: FileValidationError<CustomError>\n): error is FileExtensionError {\n return \"name\" in error && error.name === \"FileExtensionError\";\n}\n\n/**\n * This function is used to determine if a file should be added to the\n * {@link FileExtensionError}. The default implementation should work for most\n * use cases except when files that do not have extensions can be uploaded. i.e.\n * LICENSE files.\n *\n * @param file - The file being checked\n * @param extensionRegExp - A regex that will only be defined if the\n * `extensions` list had at least one value.\n * @param extensions - The list of extensions allowed\n * @returns true if the file has a valid name.\n * @since 3.1.0\n */\nexport type IsValidFileName = (\n file: File,\n extensionRegExp: RegExp | undefined,\n extensions: readonly string[]\n) => boolean;\n\n/**\n *\n * @defaultValue `matcher?.test(file.name) ?? true`\n * @since 3.1.0\n */\nexport const isValidFileName: IsValidFileName = (file, matcher) =>\n matcher?.test(file.name) ?? true;\n\n/** @since 2.9.0 */\nexport interface FileValidationOptions {\n /**\n * If the number of files should be limited, set this value to a number\n * greater than `0`.\n *\n * Note: This still allows \"infinite\" files when set to `0` since the\n * `<input>` element should normally be set to `disabled` if files should not\n * be able to be uploaded.\n *\n * @defaultValue `-1`\n */\n maxFiles?: number;\n\n /**\n * An optional minimum file size to enforce for each file. This will only be\n * used when it is greater than `0`.\n *\n * @defaultValue `-1`\n */\n minFileSize?: number;\n\n /**\n * An optional maximum file size to enforce for each file. This will only be\n * used when it is greater than `0`.\n *\n * @defaultValue `-1`\n */\n maxFileSize?: number;\n\n /**\n * An optional list of extensions to enforce when uploading files.\n *\n * Note: The extensions and file names will be compared ignoring case.\n *\n * @example Only Allow Images\n * ```ts\n * const extensions = [\"png\", \"jpeg\", \"jpg\", \"gif\"];\n * ```\n */\n extensions?: readonly string[];\n\n /** {@inheritDoc IsValidFileName} */\n isValidFileName?: IsValidFileName;\n\n /**\n * An optional total file size to enforce when the {@link maxFiles} option is\n * not set to `1`.\n *\n * @defaultValue `-1`\n */\n totalFileSize?: number;\n}\n\n/** @since 2.9.0 */\nexport interface FilesValidationOptions\n extends Required<FileValidationOptions> {\n /**\n * The total number of bytes in the {@link FileUploadHookReturnValue.stats}\n * list. This is really just:\n *\n * ```ts\n * const totalBytes = stats.reduce((total, stat) => total + stat.file.size, 0);\n * ```\n */\n totalBytes: number;\n\n /**\n * The total number of files in the {@link FileUploadHookReturnValue.stats}.\n */\n totalFiles: number;\n}\n\n/**\n * @since 2.9.0\n */\nexport interface ValidatedFilesResult<CustomError> {\n /**\n * A filtered list of files that have been validated and can be queued for the\n * upload process.\n */\n pending: readonly File[];\n\n /**\n * A list of {@link FileValidationError} that occurred during the validation\n * step.\n *\n * Note: If an error has occurred, the file **should not** be added to the\n * {@link pending} list of files.\n */\n errors: readonly FileValidationError<CustomError>[];\n}\n\n/**\n * This function will be called whenever a file has been uploaded by the user\n * either through an `<input type=\"file\">` or drag and drop behavior.\n *\n * @example Simple Example\n * ```ts\n * const validateFiles: FilesValidator = (files, options) => {\n * const invalid: File[] = [];\n * const pending: File[] = [];\n * for (const file of files) {\n * if (!/\\.(jpe?g|svg|png)$/i.test(name)) {\n * invalid.push(file);\n * } else {\n * pending.push(file);\n * }\n * }\n *\n * const errors: FileValidationError[] = [];\n * if (invalid.length) {\n * errors.push(new GenericFileError(invalid))\n * }\n *\n * return { pending, errors };\n * };\n * ```\n *\n * @typeParam E - An optional custom file validation error.\n * @param files - The list of files to check\n * @param options - The {@link FilesValidationOptions}\n * @returns the {@link ValidatedFilesResult}\n * @see {@link validateFiles} for the default implementation\n * @since 2.9.0\n */\nexport type FilesValidator<CustomError = never> = (\n files: readonly File[],\n options: FilesValidationOptions\n) => ValidatedFilesResult<CustomError>;\n\n/**\n * A pretty decent default implementation for validating files with the\n * {@link useFileUpload} that ensures the {@link FilesValidationOptions} are\n * enforced before allowing a file to be uploaded.\n *\n * @typeParam E - An optional custom file validation error.\n * @param files - The list of files to check\n * @param options - The {@link FilesValidationOptions}\n * @returns the {@link ValidatedFilesResult}\n * @since 2.9.0\n */\nexport function validateFiles<CustomError>(\n files: readonly File[],\n options: FilesValidationOptions\n): ValidatedFilesResult<CustomError> {\n const {\n maxFiles,\n extensions,\n minFileSize,\n maxFileSize,\n totalBytes,\n totalFiles,\n totalFileSize,\n isValidFileName,\n } = options;\n\n const errors: FileValidationError<CustomError>[] = [];\n const pending: File[] = [];\n const extraFiles: File[] = [];\n const extensionRegExp =\n extensions.length > 0\n ? new RegExp(`\\\\.(${extensions.join(\"|\")})$`, \"i\")\n : undefined;\n\n let maxFilesReached = maxFiles > 0 && totalFiles >= maxFiles;\n let remainingBytes = totalFileSize - totalBytes;\n const extensionErrors: File[] = [];\n const minErrors: File[] = [];\n const maxErrors: File[] = [];\n const totalSizeErrors: File[] = [];\n for (let i = 0; i < files.length; i += 1) {\n const file = files[i];\n\n let valid = true;\n const { size } = file;\n if (!isValidFileName(file, extensionRegExp, extensions)) {\n valid = false;\n extensionErrors.push(file);\n }\n\n if (minFileSize > 0 && size < minFileSize) {\n valid = false;\n minErrors.push(file);\n }\n\n if (maxFileSize > 0 && size > maxFileSize) {\n valid = false;\n maxErrors.push(file);\n } else if (totalFileSize > 0 && remainingBytes - file.size < 0) {\n // don't want both errors displaying\n valid = false;\n totalSizeErrors.push(file);\n }\n\n if (maxFilesReached && valid) {\n extraFiles.push(file);\n } else if (!maxFilesReached && valid) {\n pending.push(file);\n remainingBytes -= file.size;\n maxFilesReached =\n maxFilesReached ||\n (maxFiles > 0 && totalFiles + pending.length >= maxFiles);\n }\n }\n\n if (extensionErrors.length) {\n errors.push(new FileExtensionError(extensionErrors, extensions));\n }\n\n if (minErrors.length) {\n errors.push(new FileSizeError(minErrors, \"min\", minFileSize));\n }\n\n if (maxErrors.length) {\n errors.push(new FileSizeError(maxErrors, \"max\", maxFileSize));\n }\n\n if (totalSizeErrors.length) {\n errors.push(new FileSizeError(totalSizeErrors, \"total\", totalFileSize));\n }\n\n if (extraFiles.length) {\n errors.push(new TooManyFilesError(extraFiles, maxFiles));\n }\n\n return { pending, errors };\n}\n"],"names":["nanoid","FileAccessError","Error","constructor","message","key","name","GenericFileError","files","reason","TooManyFilesError","limit","FileSizeError","type","FileExtensionError","extensions","isGenericFileError","error","isFileAccessError","isTooManyFilesError","isFileSizeError","isFileExtensionError","isValidFileName","file","matcher","test","validateFiles","options","maxFiles","minFileSize","maxFileSize","totalBytes","totalFiles","totalFileSize","errors","pending","extraFiles","extensionRegExp","length","RegExp","join","undefined","maxFilesReached","remainingBytes","extensionErrors","minErrors","maxErrors","totalSizeErrors","i","valid","size","push"],"mappings":"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"}
@@ -9,7 +9,7 @@ import { textField } from "./textFieldStyles.js";
9
9
  * @since 6.0.0
10
10
  * @internal
11
11
  */ export function SelectedOption(props) {
12
- const { disableAddon, option, className, disablePadding = true, placeholder, ...remaining } = props;
12
+ const { disableAddon, option, className, disableWrap = true, disablePadding = true, placeholder, ...remaining } = props;
13
13
  let children = option?.children || placeholder;
14
14
  // when the children are a string or number, wrap it in additional span so
15
15
  // that overflow can be ellipsis-ed
@@ -24,6 +24,7 @@ import { textField } from "./textFieldStyles.js";
24
24
  return /*#__PURE__*/ _jsxs(Box, {
25
25
  ...remaining,
26
26
  className: cnb("rmd-selected-option", textField(), className),
27
+ disableWrap: disableWrap,
27
28
  disablePadding: disablePadding,
28
29
  children: [
29
30
  !disableAddon && option?.leftAddon,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/form/SelectedOption.tsx"],"sourcesContent":["import { cnb } from \"cnbuilder\";\nimport { type ReactElement, type ReactNode } from \"react\";\nimport { Box, type BoxProps } from \"../box/Box.js\";\nimport { cssUtils } from \"../cssUtils.js\";\nimport { type OptionProps } from \"./Option.js\";\nimport { textField } from \"./textFieldStyles.js\";\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport interface SelectedOptionProps extends BoxProps {\n option: OptionProps | undefined;\n placeholder?: ReactNode;\n disableAddon: boolean;\n}\n\n/**\n * This component is used to render the current option.\n *\n * @since 6.0.0\n * @internal\n */\nexport function SelectedOption(props: SelectedOptionProps): ReactElement {\n const {\n disableAddon,\n option,\n className,\n disablePadding = true,\n placeholder,\n ...remaining\n } = props;\n\n let children = option?.children || placeholder;\n // when the children are a string or number, wrap it in additional span so\n // that overflow can be ellipsis-ed\n if (typeof children === \"string\" || typeof children === \"number\") {\n children = (\n <span className={cssUtils({ textOverflow: \"ellipsis\" })}>{children}</span>\n );\n }\n\n return (\n <Box\n {...remaining}\n className={cnb(\"rmd-selected-option\", textField(), className)}\n disablePadding={disablePadding}\n >\n {!disableAddon && option?.leftAddon}\n {children}\n </Box>\n );\n}\n"],"names":["cnb","Box","cssUtils","textField","SelectedOption","props","disableAddon","option","className","disablePadding","placeholder","remaining","children","span","textOverflow","leftAddon"],"mappings":";AAAA,SAASA,GAAG,QAAQ,YAAY;AAEhC,SAASC,GAAG,QAAuB,gBAAgB;AACnD,SAASC,QAAQ,QAAQ,iBAAiB;AAE1C,SAASC,SAAS,QAAQ,uBAAuB;AAYjD;;;;;CAKC,GACD,OAAO,SAASC,eAAeC,KAA0B;IACvD,MAAM,EACJC,YAAY,EACZC,MAAM,EACNC,SAAS,EACTC,iBAAiB,IAAI,EACrBC,WAAW,EACX,GAAGC,WACJ,GAAGN;IAEJ,IAAIO,WAAWL,QAAQK,YAAYF;IACnC,0EAA0E;IAC1E,mCAAmC;IACnC,IAAI,OAAOE,aAAa,YAAY,OAAOA,aAAa,UAAU;QAChEA,yBACE,KAACC;YAAKL,WAAWN,SAAS;gBAAEY,cAAc;YAAW;sBAAKF;;IAE9D;IAEA,qBACE,MAACX;QACE,GAAGU,SAAS;QACbH,WAAWR,IAAI,uBAAuBG,aAAaK;QACnDC,gBAAgBA;;YAEf,CAACH,gBAAgBC,QAAQQ;YACzBH;;;AAGP"}
1
+ {"version":3,"sources":["../../src/form/SelectedOption.tsx"],"sourcesContent":["import { cnb } from \"cnbuilder\";\nimport { type ReactElement, type ReactNode } from \"react\";\nimport { Box, type BoxProps } from \"../box/Box.js\";\nimport { cssUtils } from \"../cssUtils.js\";\nimport { type OptionProps } from \"./Option.js\";\nimport { textField } from \"./textFieldStyles.js\";\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport interface SelectedOptionProps extends BoxProps {\n option: OptionProps | undefined;\n placeholder?: ReactNode;\n disableAddon: boolean;\n}\n\n/**\n * This component is used to render the current option.\n *\n * @since 6.0.0\n * @internal\n */\nexport function SelectedOption(props: SelectedOptionProps): ReactElement {\n const {\n disableAddon,\n option,\n className,\n disableWrap = true,\n disablePadding = true,\n placeholder,\n ...remaining\n } = props;\n\n let children = option?.children || placeholder;\n // when the children are a string or number, wrap it in additional span so\n // that overflow can be ellipsis-ed\n if (typeof children === \"string\" || typeof children === \"number\") {\n children = (\n <span className={cssUtils({ textOverflow: \"ellipsis\" })}>{children}</span>\n );\n }\n\n return (\n <Box\n {...remaining}\n className={cnb(\"rmd-selected-option\", textField(), className)}\n disableWrap={disableWrap}\n disablePadding={disablePadding}\n >\n {!disableAddon && option?.leftAddon}\n {children}\n </Box>\n );\n}\n"],"names":["cnb","Box","cssUtils","textField","SelectedOption","props","disableAddon","option","className","disableWrap","disablePadding","placeholder","remaining","children","span","textOverflow","leftAddon"],"mappings":";AAAA,SAASA,GAAG,QAAQ,YAAY;AAEhC,SAASC,GAAG,QAAuB,gBAAgB;AACnD,SAASC,QAAQ,QAAQ,iBAAiB;AAE1C,SAASC,SAAS,QAAQ,uBAAuB;AAYjD;;;;;CAKC,GACD,OAAO,SAASC,eAAeC,KAA0B;IACvD,MAAM,EACJC,YAAY,EACZC,MAAM,EACNC,SAAS,EACTC,cAAc,IAAI,EAClBC,iBAAiB,IAAI,EACrBC,WAAW,EACX,GAAGC,WACJ,GAAGP;IAEJ,IAAIQ,WAAWN,QAAQM,YAAYF;IACnC,0EAA0E;IAC1E,mCAAmC;IACnC,IAAI,OAAOE,aAAa,YAAY,OAAOA,aAAa,UAAU;QAChEA,yBACE,KAACC;YAAKN,WAAWN,SAAS;gBAAEa,cAAc;YAAW;sBAAKF;;IAE9D;IAEA,qBACE,MAACZ;QACE,GAAGW,SAAS;QACbJ,WAAWR,IAAI,uBAAuBG,aAAaK;QACnDC,aAAaA;QACbC,gBAAgBA;;YAEf,CAACJ,gBAAgBC,QAAQS;YACzBH;;;AAGP"}
@@ -543,16 +543,21 @@ $variables: (
543
543
  @if not
544
544
  $disable-text-field or not
545
545
  $disable-password or not
546
- $disable-textarea
546
+ $disable-textarea or not
547
+ $disable-select
547
548
  {
549
+ $text-field-floating: ", .rmd-text-field:where(:not(:placeholder-shown))" +
550
+ if($disable-select, "", " ~ :where(:not(.rmd-select))") +
551
+ " ~ &--floating";
552
+
548
553
  $floating-active-selector: $floating-active-selector +
549
554
  ", .rmd-text-field-container:focus-within &--floating" +
550
- ", .rmd-text-field:not(:placeholder-shown) ~ &--floating";
555
+ $text-field-floating;
551
556
  }
552
557
  @if not $disable-native-select {
553
558
  $floating-active-selector: $floating-active-selector +
554
559
  ", .rmd-native-select[multiple] ~ &--floating" +
555
- ", .rmd-native-select:not(:invalid) ~ &--floating";
560
+ ", .rmd-native-select:where(:not(:invalid)) ~ &--floating";
556
561
  }
557
562
 
558
563
  #{$floating-active-selector} {
@@ -1279,8 +1284,7 @@ $variables: (
1279
1284
  .rmd-input-toggle {
1280
1285
  @include icon.set-var(color, currentcolor);
1281
1286
  @include interaction.surface(
1282
- $focus-selector:
1283
- if(
1287
+ $focus-selector: if(
1284
1288
  utils.$disable-has-selectors or utils.$disable-focus-visible,
1285
1289
  "&:focus-within",
1286
1290
  "&:has(:focus-visible)"
@@ -9,9 +9,11 @@ export interface FontIconProps extends HTMLAttributes<HTMLSpanElement>, FontIcon
9
9
  "aria-hidden"?: AriaAttributes["aria-hidden"];
10
10
  /**
11
11
  * Any children to render to create the font icon. This is required for
12
- * material-icons.
12
+ * material-icons. For example:
13
13
  *
14
- * @example `<FontIcon>clear</FontIcon>`
14
+ * ```tsx
15
+ * <FontIcon>clear</FontIcon>
16
+ * ```
15
17
  */
16
18
  children?: ReactNode;
17
19
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/icon/FontIcon.tsx"],"sourcesContent":["import {\n forwardRef,\n type AriaAttributes,\n type HTMLAttributes,\n type ReactNode,\n} from \"react\";\nimport { icon, type FontIconClassNameOptions } from \"./styles.js\";\n\n/**\n * @since 6.0.0 Removed the `forceSize`/`forceFontSize` props and added the\n * `inline` and `theme` props.\n */\nexport interface FontIconProps\n extends HTMLAttributes<HTMLSpanElement>,\n FontIconClassNameOptions {\n /** @defaultValue `true` */\n \"aria-hidden\"?: AriaAttributes[\"aria-hidden\"];\n\n /**\n * Any children to render to create the font icon. This is required for\n * material-icons.\n *\n * @example `<FontIcon>clear</FontIcon>`\n */\n children?: ReactNode;\n}\n\n/**\n * The `FontIcon` component is used for rendering a font-icon library's icon.\n * The default is to use the `material-icons` library, but others can be used as\n * well.\n *\n * @since 6.0.0 Switched from `<i>` to `<span>` element and removed\n * the `forceSize`/`forceFontSize` props.\n */\nexport const FontIcon = forwardRef<HTMLElement, FontIconProps>(\n function FontIcon(props, ref) {\n const {\n \"aria-hidden\": ariaHidden = true,\n iconClassName = \"material-icons\",\n dense = false,\n theme,\n className,\n children,\n ...remaining\n } = props;\n\n return (\n <span\n {...remaining}\n aria-hidden={ariaHidden}\n ref={ref}\n className={icon({\n type: \"font\",\n dense,\n theme,\n className,\n iconClassName,\n })}\n >\n {children}\n </span>\n );\n }\n);\n"],"names":["forwardRef","icon","FontIcon","props","ref","ariaHidden","iconClassName","dense","theme","className","children","remaining","span","aria-hidden","type"],"mappings":";AAAA,SACEA,UAAU,QAIL,QAAQ;AACf,SAASC,IAAI,QAAuC,cAAc;AAqBlE;;;;;;;CAOC,GACD,OAAO,MAAMC,yBAAWF,WACtB,SAASE,SAASC,KAAK,EAAEC,GAAG;IAC1B,MAAM,EACJ,eAAeC,aAAa,IAAI,EAChCC,gBAAgB,gBAAgB,EAChCC,QAAQ,KAAK,EACbC,KAAK,EACLC,SAAS,EACTC,QAAQ,EACR,GAAGC,WACJ,GAAGR;IAEJ,qBACE,KAACS;QACE,GAAGD,SAAS;QACbE,eAAaR;QACbD,KAAKA;QACLK,WAAWR,KAAK;YACda,MAAM;YACNP;YACAC;YACAC;YACAH;QACF;kBAECI;;AAGP,GACA"}
1
+ {"version":3,"sources":["../../src/icon/FontIcon.tsx"],"sourcesContent":["import {\n forwardRef,\n type AriaAttributes,\n type HTMLAttributes,\n type ReactNode,\n} from \"react\";\nimport { icon, type FontIconClassNameOptions } from \"./styles.js\";\n\n/**\n * @since 6.0.0 Removed the `forceSize`/`forceFontSize` props and added the\n * `inline` and `theme` props.\n */\nexport interface FontIconProps\n extends HTMLAttributes<HTMLSpanElement>,\n FontIconClassNameOptions {\n /** @defaultValue `true` */\n \"aria-hidden\"?: AriaAttributes[\"aria-hidden\"];\n\n /**\n * Any children to render to create the font icon. This is required for\n * material-icons. For example:\n *\n * ```tsx\n * <FontIcon>clear</FontIcon>\n * ```\n */\n children?: ReactNode;\n}\n\n/**\n * The `FontIcon` component is used for rendering a font-icon library's icon.\n * The default is to use the `material-icons` library, but others can be used as\n * well.\n *\n * @since 6.0.0 Switched from `<i>` to `<span>` element and removed\n * the `forceSize`/`forceFontSize` props.\n */\nexport const FontIcon = forwardRef<HTMLElement, FontIconProps>(\n function FontIcon(props, ref) {\n const {\n \"aria-hidden\": ariaHidden = true,\n iconClassName = \"material-icons\",\n dense = false,\n theme,\n className,\n children,\n ...remaining\n } = props;\n\n return (\n <span\n {...remaining}\n aria-hidden={ariaHidden}\n ref={ref}\n className={icon({\n type: \"font\",\n dense,\n theme,\n className,\n iconClassName,\n })}\n >\n {children}\n </span>\n );\n }\n);\n"],"names":["forwardRef","icon","FontIcon","props","ref","ariaHidden","iconClassName","dense","theme","className","children","remaining","span","aria-hidden","type"],"mappings":";AAAA,SACEA,UAAU,QAIL,QAAQ;AACf,SAASC,IAAI,QAAuC,cAAc;AAuBlE;;;;;;;CAOC,GACD,OAAO,MAAMC,yBAAWF,WACtB,SAASE,SAASC,KAAK,EAAEC,GAAG;IAC1B,MAAM,EACJ,eAAeC,aAAa,IAAI,EAChCC,gBAAgB,gBAAgB,EAChCC,QAAQ,KAAK,EACbC,KAAK,EACLC,SAAS,EACTC,QAAQ,EACR,GAAGC,WACJ,GAAGR;IAEJ,qBACE,KAACS;QACE,GAAGD,SAAS;QACbE,eAAaR;QACbD,KAAKA;QACLK,WAAWR,KAAK;YACda,MAAM;YACNP;YACAC;YACAC;YACAH;QACF;kBAECI;;AAGP,GACA"}
@@ -17,7 +17,9 @@ export interface TextIconSpacingProps {
17
17
  * If this is not a valid react element, the icon will be wrapped in a
18
18
  * `<span>` instead with the class names applied.
19
19
  */
20
- icon?: ReactElement | ReactNode;
20
+ icon?: ReactElement<{
21
+ className?: string;
22
+ }> | ReactNode;
21
23
  /**
22
24
  * Boolean if the icon should appear after the text instead of before.
23
25
  *
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/icon/TextIconSpacing.tsx"],"sourcesContent":["import { cnb } from \"cnbuilder\";\nimport {\n Children,\n cloneElement,\n isValidElement,\n type ReactElement,\n type ReactNode,\n} from \"react\";\n\nexport interface TextIconSpacingProps {\n /**\n * An optional className to apply to the surrounding `<span>` when the\n * `forceIconWrap` prop is enabled or the icon is not a valid React Element.\n * If the `forceIconWrap` prop is not enabled, it will be cloned into the icon\n * instead.\n */\n className?: string;\n\n /**\n * An optional icon to display with a text button. This is invalid for icon\n * buttons. If this is a single element, a new class name will be cloned into\n * the element to get correct spacing so if you have a custom icon element,\n * you **must** also pass that class name down. If you are using one of the\n * react-md icon component packages, this is handled automatically.\n *\n * If this is not a valid react element, the icon will be wrapped in a\n * `<span>` instead with the class names applied.\n */\n icon?: ReactElement | ReactNode;\n\n /**\n * Boolean if the icon should appear after the text instead of before.\n *\n * @defaultValue `false`\n */\n iconAfter?: boolean;\n\n /**\n * The children to render before or after the provided icon. This is defaulted\n * to `null` so that providing a `null` icon will correctly render without\n * React crashing.\n *\n * @defaultValue `null`\n */\n children?: ReactNode;\n\n /**\n * The class name to use for an icon that is placed before text.\n *\n * @defaultValue `\"rmd-icon--before\"`\n */\n beforeClassName?: string;\n\n /**\n * The class name to use for an icon that is placed after text.\n *\n * @defaultValue `\"rmd-icon--after\"`\n */\n afterClassName?: string;\n\n /**\n * The class name to use for an icon that is placed before above the text.\n * This is used when the `stacked` prop is enabled and the `iconAfter` prop is\n * disabled or omitted.\n *\n * @defaultValue `\"rmd-icon--above\"`\n */\n aboveClassName?: string;\n\n /**\n * The class name to use for an icon that is placed before above the text.\n * This is used when the `stacked` prop is enabled and the `iconAfter` prop is\n * enabled.\n *\n * @defaultValue `\"rmd-icon--below\"`\n */\n belowClassName?: string;\n\n /**\n * Boolean if the icon should be forced into a `<span>` with the class names\n * applied instead of attempting to clone into the provided icon.\n *\n * @defaultValue `false`\n */\n forceIconWrap?: boolean;\n\n /**\n * Boolean if the icon and text should be stacked instead of inline. Note:\n * You'll normally want to update the container element to have\n * `display: flex` and `flex-direction: column` for this to work.\n *\n * @defaultValue `false`\n */\n stacked?: boolean;\n\n /**\n * Boolean if the icon and content are in a `column-reverse` or `row-reverse`\n * `flex-direction`. This will swap the different classnames as needed.\n *\n * @since 2.5.0\n * @defaultValue `false`\n */\n flexReverse?: boolean;\n}\n\n/**\n * Note: This component **must** be rendered within a flex container unless the\n * {@link TextIconSpacingProps.forceIconWrap} is set to `true`.\n *\n * @example Simple Example\n * ```tsx\n * import { TextIconSpacing, FontIcon } from \"@react-md/core\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * return (\n * <TextIconSpacing icon={<FontIcon>favorite</FontIcon>}>\n * Favorite\n * </TextIconSpacing>\n * );\n * }\n * ```\n *\n * @example Stacked Example\n * ```tsx\n * import { TextIconSpacing, FontIcon } from \"@react-md/core\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * return (\n * <TextIconSpacing icon={<FontIcon>favorite</FontIcon>} stacked>\n * Favorite\n * </TextIconSpacing>\n * );\n * }\n * ```\n */\nexport function TextIconSpacing(props: TextIconSpacingProps): ReactElement {\n const {\n className,\n icon: propIcon,\n children = null,\n stacked = false,\n iconAfter = false,\n flexReverse = false,\n forceIconWrap = false,\n beforeClassName = \"rmd-icon--before\",\n afterClassName = \"rmd-icon--after\",\n aboveClassName = \"rmd-icon--above\",\n belowClassName = \"rmd-icon--below\",\n } = props;\n\n if (!propIcon) {\n return <>{children}</>;\n }\n\n const isAfter = flexReverse ? !iconAfter : iconAfter;\n const baseClassName = cnb(\n {\n [beforeClassName]: !stacked && !isAfter,\n [afterClassName]: !stacked && isAfter,\n [aboveClassName]: stacked && !isAfter,\n [belowClassName]: stacked && isAfter,\n },\n className\n );\n\n let iconEl = propIcon;\n let content = children;\n if (!forceIconWrap && isValidElement<{ className?: string }>(propIcon)) {\n const icon = Children.only(propIcon);\n iconEl = cloneElement(icon, {\n className: cnb(baseClassName, icon.props.className),\n });\n } else if (propIcon) {\n iconEl = (\n <span className={cnb(\"rmd-text-icon-spacing\", baseClassName)}>\n {propIcon}\n </span>\n );\n }\n\n if (iconEl) {\n content = (\n <>\n {!iconAfter && iconEl}\n {children}\n {iconAfter && iconEl}\n </>\n );\n }\n\n return <>{content}</>;\n}\n"],"names":["cnb","Children","cloneElement","isValidElement","TextIconSpacing","props","className","icon","propIcon","children","stacked","iconAfter","flexReverse","forceIconWrap","beforeClassName","afterClassName","aboveClassName","belowClassName","isAfter","baseClassName","iconEl","content","only","span"],"mappings":";AAAA,SAASA,GAAG,QAAQ,YAAY;AAChC,SACEC,QAAQ,EACRC,YAAY,EACZC,cAAc,QAGT,QAAQ;AAkGf;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BC,GACD,OAAO,SAASC,gBAAgBC,KAA2B;IACzD,MAAM,EACJC,SAAS,EACTC,MAAMC,QAAQ,EACdC,WAAW,IAAI,EACfC,UAAU,KAAK,EACfC,YAAY,KAAK,EACjBC,cAAc,KAAK,EACnBC,gBAAgB,KAAK,EACrBC,kBAAkB,kBAAkB,EACpCC,iBAAiB,iBAAiB,EAClCC,iBAAiB,iBAAiB,EAClCC,iBAAiB,iBAAiB,EACnC,GAAGZ;IAEJ,IAAI,CAACG,UAAU;QACb,qBAAO;sBAAGC;;IACZ;IAEA,MAAMS,UAAUN,cAAc,CAACD,YAAYA;IAC3C,MAAMQ,gBAAgBnB,IACpB;QACE,CAACc,gBAAgB,EAAE,CAACJ,WAAW,CAACQ;QAChC,CAACH,eAAe,EAAE,CAACL,WAAWQ;QAC9B,CAACF,eAAe,EAAEN,WAAW,CAACQ;QAC9B,CAACD,eAAe,EAAEP,WAAWQ;IAC/B,GACAZ;IAGF,IAAIc,SAASZ;IACb,IAAIa,UAAUZ;IACd,IAAI,CAACI,+BAAiBV,eAAuCK,WAAW;QACtE,MAAMD,OAAON,SAASqB,IAAI,CAACd;QAC3BY,uBAASlB,aAAaK,MAAM;YAC1BD,WAAWN,IAAImB,eAAeZ,KAAKF,KAAK,CAACC,SAAS;QACpD;IACF,OAAO,IAAIE,UAAU;QACnBY,uBACE,KAACG;YAAKjB,WAAWN,IAAI,yBAAyBmB;sBAC3CX;;IAGP;IAEA,IAAIY,QAAQ;QACVC,wBACE;;gBACG,CAACV,aAAaS;gBACdX;gBACAE,aAAaS;;;IAGpB;IAEA,qBAAO;kBAAGC;;AACZ"}
1
+ {"version":3,"sources":["../../src/icon/TextIconSpacing.tsx"],"sourcesContent":["import { cnb } from \"cnbuilder\";\nimport {\n Children,\n cloneElement,\n isValidElement,\n type ReactElement,\n type ReactNode,\n} from \"react\";\n\nexport interface TextIconSpacingProps {\n /**\n * An optional className to apply to the surrounding `<span>` when the\n * `forceIconWrap` prop is enabled or the icon is not a valid React Element.\n * If the `forceIconWrap` prop is not enabled, it will be cloned into the icon\n * instead.\n */\n className?: string;\n\n /**\n * An optional icon to display with a text button. This is invalid for icon\n * buttons. If this is a single element, a new class name will be cloned into\n * the element to get correct spacing so if you have a custom icon element,\n * you **must** also pass that class name down. If you are using one of the\n * react-md icon component packages, this is handled automatically.\n *\n * If this is not a valid react element, the icon will be wrapped in a\n * `<span>` instead with the class names applied.\n */\n icon?: ReactElement<{ className?: string }> | ReactNode;\n\n /**\n * Boolean if the icon should appear after the text instead of before.\n *\n * @defaultValue `false`\n */\n iconAfter?: boolean;\n\n /**\n * The children to render before or after the provided icon. This is defaulted\n * to `null` so that providing a `null` icon will correctly render without\n * React crashing.\n *\n * @defaultValue `null`\n */\n children?: ReactNode;\n\n /**\n * The class name to use for an icon that is placed before text.\n *\n * @defaultValue `\"rmd-icon--before\"`\n */\n beforeClassName?: string;\n\n /**\n * The class name to use for an icon that is placed after text.\n *\n * @defaultValue `\"rmd-icon--after\"`\n */\n afterClassName?: string;\n\n /**\n * The class name to use for an icon that is placed before above the text.\n * This is used when the `stacked` prop is enabled and the `iconAfter` prop is\n * disabled or omitted.\n *\n * @defaultValue `\"rmd-icon--above\"`\n */\n aboveClassName?: string;\n\n /**\n * The class name to use for an icon that is placed before above the text.\n * This is used when the `stacked` prop is enabled and the `iconAfter` prop is\n * enabled.\n *\n * @defaultValue `\"rmd-icon--below\"`\n */\n belowClassName?: string;\n\n /**\n * Boolean if the icon should be forced into a `<span>` with the class names\n * applied instead of attempting to clone into the provided icon.\n *\n * @defaultValue `false`\n */\n forceIconWrap?: boolean;\n\n /**\n * Boolean if the icon and text should be stacked instead of inline. Note:\n * You'll normally want to update the container element to have\n * `display: flex` and `flex-direction: column` for this to work.\n *\n * @defaultValue `false`\n */\n stacked?: boolean;\n\n /**\n * Boolean if the icon and content are in a `column-reverse` or `row-reverse`\n * `flex-direction`. This will swap the different classnames as needed.\n *\n * @since 2.5.0\n * @defaultValue `false`\n */\n flexReverse?: boolean;\n}\n\n/**\n * Note: This component **must** be rendered within a flex container unless the\n * {@link TextIconSpacingProps.forceIconWrap} is set to `true`.\n *\n * @example Simple Example\n * ```tsx\n * import { TextIconSpacing, FontIcon } from \"@react-md/core\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * return (\n * <TextIconSpacing icon={<FontIcon>favorite</FontIcon>}>\n * Favorite\n * </TextIconSpacing>\n * );\n * }\n * ```\n *\n * @example Stacked Example\n * ```tsx\n * import { TextIconSpacing, FontIcon } from \"@react-md/core\";\n * import type { ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * return (\n * <TextIconSpacing icon={<FontIcon>favorite</FontIcon>} stacked>\n * Favorite\n * </TextIconSpacing>\n * );\n * }\n * ```\n */\nexport function TextIconSpacing(props: TextIconSpacingProps): ReactElement {\n const {\n className,\n icon: propIcon,\n children = null,\n stacked = false,\n iconAfter = false,\n flexReverse = false,\n forceIconWrap = false,\n beforeClassName = \"rmd-icon--before\",\n afterClassName = \"rmd-icon--after\",\n aboveClassName = \"rmd-icon--above\",\n belowClassName = \"rmd-icon--below\",\n } = props;\n\n if (!propIcon) {\n return <>{children}</>;\n }\n\n const isAfter = flexReverse ? !iconAfter : iconAfter;\n const baseClassName = cnb(\n {\n [beforeClassName]: !stacked && !isAfter,\n [afterClassName]: !stacked && isAfter,\n [aboveClassName]: stacked && !isAfter,\n [belowClassName]: stacked && isAfter,\n },\n className\n );\n\n let iconEl = propIcon;\n let content = children;\n if (!forceIconWrap && isValidElement<{ className?: string }>(propIcon)) {\n const icon = Children.only(propIcon);\n iconEl = cloneElement(icon, {\n className: cnb(baseClassName, icon.props.className),\n });\n } else if (propIcon) {\n iconEl = (\n <span className={cnb(\"rmd-text-icon-spacing\", baseClassName)}>\n {propIcon}\n </span>\n );\n }\n\n if (iconEl) {\n content = (\n <>\n {!iconAfter && iconEl}\n {children}\n {iconAfter && iconEl}\n </>\n );\n }\n\n return <>{content}</>;\n}\n"],"names":["cnb","Children","cloneElement","isValidElement","TextIconSpacing","props","className","icon","propIcon","children","stacked","iconAfter","flexReverse","forceIconWrap","beforeClassName","afterClassName","aboveClassName","belowClassName","isAfter","baseClassName","iconEl","content","only","span"],"mappings":";AAAA,SAASA,GAAG,QAAQ,YAAY;AAChC,SACEC,QAAQ,EACRC,YAAY,EACZC,cAAc,QAGT,QAAQ;AAkGf;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BC,GACD,OAAO,SAASC,gBAAgBC,KAA2B;IACzD,MAAM,EACJC,SAAS,EACTC,MAAMC,QAAQ,EACdC,WAAW,IAAI,EACfC,UAAU,KAAK,EACfC,YAAY,KAAK,EACjBC,cAAc,KAAK,EACnBC,gBAAgB,KAAK,EACrBC,kBAAkB,kBAAkB,EACpCC,iBAAiB,iBAAiB,EAClCC,iBAAiB,iBAAiB,EAClCC,iBAAiB,iBAAiB,EACnC,GAAGZ;IAEJ,IAAI,CAACG,UAAU;QACb,qBAAO;sBAAGC;;IACZ;IAEA,MAAMS,UAAUN,cAAc,CAACD,YAAYA;IAC3C,MAAMQ,gBAAgBnB,IACpB;QACE,CAACc,gBAAgB,EAAE,CAACJ,WAAW,CAACQ;QAChC,CAACH,eAAe,EAAE,CAACL,WAAWQ;QAC9B,CAACF,eAAe,EAAEN,WAAW,CAACQ;QAC9B,CAACD,eAAe,EAAEP,WAAWQ;IAC/B,GACAZ;IAGF,IAAIc,SAASZ;IACb,IAAIa,UAAUZ;IACd,IAAI,CAACI,+BAAiBV,eAAuCK,WAAW;QACtE,MAAMD,OAAON,SAASqB,IAAI,CAACd;QAC3BY,uBAASlB,aAAaK,MAAM;YAC1BD,WAAWN,IAAImB,eAAeZ,KAAKF,KAAK,CAACC,SAAS;QACpD;IACF,OAAO,IAAIE,UAAU;QACnBY,uBACE,KAACG;YAAKjB,WAAWN,IAAI,yBAAyBmB;sBAC3CX;;IAGP;IAEA,IAAIY,QAAQ;QACVC,wBACE;;gBACG,CAACV,aAAaS;gBACdX;gBACAE,aAAaS;;;IAGpB;IAEA,qBAAO;kBAAGC;;AACZ"}
@@ -139,14 +139,14 @@ export interface SetupResizeObserverMockOptions {
139
139
  *
140
140
  * @example Main Usage
141
141
  * ```tsx
142
- * import { useCallback, useState } from "react";
143
142
  * import {
144
143
  * cleanupResizeObserverAfterEach,
145
144
  * render,
146
145
  * screen,
147
146
  * setupResizeObserverMock,
148
147
  * } from "@react-md/core/test-utils";
149
- * import { useResizeObserver } from "@react-md/core";
148
+ * import { useResizeObserver } from "@react-md/core/useResizeObserver";
149
+ * import { useCallback, useState } from "react";
150
150
  *
151
151
  * function ExampleComponent() {
152
152
  * const [size, setSize] = useState({ height: 0, width: 0 });
@@ -156,7 +156,7 @@ export interface SetupResizeObserverMockOptions {
156
156
  * height: entry.contentRect.height,
157
157
  * width: entry.contentRect.width,
158
158
  * });
159
- * });
159
+ * }, []),
160
160
  * });
161
161
  *
162
162
  * return (
@@ -172,10 +172,10 @@ export interface SetupResizeObserverMockOptions {
172
172
  * describe("ExampleComponent", () => {
173
173
  * it("should do stuff", () => {
174
174
  * const observer = setupResizeObserverMock();
175
- * render(<ExampleComponent />)
175
+ * render(<ExampleComponent />);
176
176
  *
177
177
  * const size = screen.getByTestId("size");
178
- * const resizeTarget = screen.getByTestId("resize-target")
178
+ * const resizeTarget = screen.getByTestId("resize-target");
179
179
  *
180
180
  * // jsdom sets all element sizes to 0 by default
181
181
  * expect(size).toHaveTextContent(JSON.stringify({ height: 0, width: 0 }));
@@ -187,19 +187,18 @@ export interface SetupResizeObserverMockOptions {
187
187
  * expect(size).toHaveTextContent(JSON.stringify({ height: 100, width: 100 }));
188
188
  *
189
189
  * // or you can mock the `getBoundingClientRect` result
190
- * jest.spyOn(resizeTarget, "getBoundingClientRect")
191
- * .mockReturnValue({
192
- * ...document.body.getBoundingClientRect(),
193
- * height: 200,
194
- * width: 200,
195
- * }):
190
+ * jest.spyOn(resizeTarget, "getBoundingClientRect").mockReturnValue({
191
+ * ...document.body.getBoundingClientRect(),
192
+ * height: 200,
193
+ * width: 200,
194
+ * });
196
195
  *
197
196
  * act(() => {
198
197
  * observer.resizeElement(resizeTarget);
199
198
  * });
200
199
  * expect(size).toHaveTextContent(JSON.stringify({ height: 200, width: 200 }));
201
200
  * });
202
- * })
201
+ * });
203
202
  * ```
204
203
  *
205
204
  * @since 6.0.0
@@ -174,14 +174,14 @@ import { resizeObserverManager } from "../useResizeObserver.js";
174
174
  *
175
175
  * @example Main Usage
176
176
  * ```tsx
177
- * import { useCallback, useState } from "react";
178
177
  * import {
179
178
  * cleanupResizeObserverAfterEach,
180
179
  * render,
181
180
  * screen,
182
181
  * setupResizeObserverMock,
183
182
  * } from "@react-md/core/test-utils";
184
- * import { useResizeObserver } from "@react-md/core";
183
+ * import { useResizeObserver } from "@react-md/core/useResizeObserver";
184
+ * import { useCallback, useState } from "react";
185
185
  *
186
186
  * function ExampleComponent() {
187
187
  * const [size, setSize] = useState({ height: 0, width: 0 });
@@ -191,7 +191,7 @@ import { resizeObserverManager } from "../useResizeObserver.js";
191
191
  * height: entry.contentRect.height,
192
192
  * width: entry.contentRect.width,
193
193
  * });
194
- * });
194
+ * }, []),
195
195
  * });
196
196
  *
197
197
  * return (
@@ -207,10 +207,10 @@ import { resizeObserverManager } from "../useResizeObserver.js";
207
207
  * describe("ExampleComponent", () => {
208
208
  * it("should do stuff", () => {
209
209
  * const observer = setupResizeObserverMock();
210
- * render(<ExampleComponent />)
210
+ * render(<ExampleComponent />);
211
211
  *
212
212
  * const size = screen.getByTestId("size");
213
- * const resizeTarget = screen.getByTestId("resize-target")
213
+ * const resizeTarget = screen.getByTestId("resize-target");
214
214
  *
215
215
  * // jsdom sets all element sizes to 0 by default
216
216
  * expect(size).toHaveTextContent(JSON.stringify({ height: 0, width: 0 }));
@@ -222,19 +222,18 @@ import { resizeObserverManager } from "../useResizeObserver.js";
222
222
  * expect(size).toHaveTextContent(JSON.stringify({ height: 100, width: 100 }));
223
223
  *
224
224
  * // or you can mock the `getBoundingClientRect` result
225
- * jest.spyOn(resizeTarget, "getBoundingClientRect")
226
- * .mockReturnValue({
227
- * ...document.body.getBoundingClientRect(),
228
- * height: 200,
229
- * width: 200,
230
- * }):
225
+ * jest.spyOn(resizeTarget, "getBoundingClientRect").mockReturnValue({
226
+ * ...document.body.getBoundingClientRect(),
227
+ * height: 200,
228
+ * width: 200,
229
+ * });
231
230
  *
232
231
  * act(() => {
233
232
  * observer.resizeElement(resizeTarget);
234
233
  * });
235
234
  * expect(size).toHaveTextContent(JSON.stringify({ height: 200, width: 200 }));
236
235
  * });
237
- * })
236
+ * });
238
237
  * ```
239
238
  *
240
239
  * @since 6.0.0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/test-utils/ResizeObserver.ts"],"sourcesContent":["import { afterEach, jest } from \"@jest/globals\";\nimport {\n resizeObserverManager,\n type ResizeObserverManager,\n} from \"../useResizeObserver.js\";\n\n/**\n * @since 6.0.0\n */\nexport interface ResizeObserverEntrySize {\n height?: number;\n width?: number;\n}\n\n/**\n * @since 6.0.0\n */\nexport type GetResizeObserverEntryMock = (\n target: Element,\n size?: ResizeObserverEntrySize\n) => ResizeObserverEntry;\n\n/**\n * This is mostly an internal function to be used with the {@link ResizeObserverMock}\n * and {@link setupResizeObserverMock}\n *\n * @since 6.0.0\n */\nexport const createResizeObserverEntry: GetResizeObserverEntryMock = (\n target,\n size\n) => {\n const contentRect = target.getBoundingClientRect();\n if (typeof size?.height === \"number\") {\n contentRect.height = size.height;\n }\n if (typeof size?.width === \"number\") {\n contentRect.width = size.width;\n }\n\n const boxSize: ResizeObserverSize = {\n blockSize: contentRect.height,\n inlineSize: contentRect.width,\n };\n\n return {\n target,\n contentRect,\n borderBoxSize: [boxSize],\n contentBoxSize: [boxSize],\n devicePixelContentBoxSize: [],\n };\n};\n\n/**\n * This is the default ResizeObserver implementation if it does not already\n * exist in jsdom. You normally should not use this directly and instead use the\n * {@link setupResizeObserverMock} instead.\n *\n * @since 6.0.0\n */\nexport class ResizeObserverMock implements ResizeObserver {\n elements: Set<Element>;\n\n constructor(public callback: ResizeObserverCallback) {\n this.elements = new Set();\n }\n\n observe = (target: Element): void => {\n this.elements.add(target);\n this.resizeAllElements(createResizeObserverEntry);\n };\n\n unobserve = (target: Element): void => {\n this.elements.delete(target);\n };\n\n disconnect = (): void => {\n this.elements.clear();\n };\n\n /**\n * Triggers the resize event for a specific element. This must be wrapped in\n * `act`.\n *\n * @example Main Usage\n * ```tsx\n * import {\n * cleanupResizeObserverAfterEach,\n * setupResizeObserverMock,\n * } from \"@react-md/core/test-utils\";\n * import { useResizeObserver } from \"@react-md/core\";\n * import { ExampleComponent } from \"../ExampleComponent.js\";\n *\n * cleanupResizeObserverAfterEach();\n *\n * describe(\"ExampleComponent\", () => {\n * it(\"should do stuff\", () => {\n * const observer = setupResizeObserverMock();\n * render(<ExampleComponent />)\n *\n * const resizeTarget = screen.getByTestId(\"resize-target\")\n *\n * // you can trigger with a custom change\n * act(() => {\n * observer.resizeElement(resizeTarget, { height: 100, width: 100 });\n * });\n * // expect resize changes\n * });\n * })\n * ```\n */\n resizeElement = (\n target: Element,\n changesOrGetEntry:\n | GetResizeObserverEntryMock\n | ResizeObserverEntrySize\n | ResizeObserverEntry = createResizeObserverEntry\n ): void => {\n if (!this.elements.has(target)) {\n throw new Error(\n \"The `ResizeObserverMock` is not watching the target element and cannot be resized\"\n );\n }\n\n let entry: ResizeObserverEntry;\n if (typeof changesOrGetEntry === \"function\") {\n entry = changesOrGetEntry(target);\n } else if (!(\"contentRect\" in changesOrGetEntry)) {\n entry = createResizeObserverEntry(target, changesOrGetEntry);\n } else {\n entry = changesOrGetEntry;\n }\n\n this.callback([entry], this);\n };\n\n /**\n * You'll normally want to use {@link resizeElement} instead, but this can be\n * used to resize all the watched elements at once.\n *\n * @example\n * ```tsx\n * import {\n * act,\n * createResizeObserverEntry,\n * render,\n * screen,\n * setupResizeObserverMock,\n * } from \"@react-md/core/test-utils\";\n *\n * const observer = setupResizeObserverMock();\n * const { container } = render(<Test />)\n * expect(container).toMatchSnapshot()\n *\n * const target1 = screen.getByTestId('target-1');\n * const target2 = screen.getByTestId('target-2');\n * const target3 = screen.getByTestId('target-3');\n *\n * act(() => {\n * observer.resizeAllElements((element) => {\n * let height: number | undefined;\n * let width: number | undefined;\n * switch (element) {\n * case target1:\n * height = 400;\n * width = 250;\n * break;\n * case target2:\n * height = 100;\n * width = 380;\n * break;\n * case target3:\n * height = 24;\n * width = 24;\n * break;\n * }\n *\n * return createResizeObserverEntry(element, { height, width });\n * });\n * });\n * expect(container).toMatchSnapshot()\n * ```\n */\n resizeAllElements = (getEntry = createResizeObserverEntry): void => {\n const entries = [...this.elements].map((element) => getEntry(element));\n this.callback(entries, this);\n };\n}\n\n/**\n * @since 6.0.0\n */\nexport interface SetupResizeObserverMockOptions {\n /**\n * Set this to `true` to mimic the real `ResizeObserver` behavior where the\n * updates occur after an animation frame instead of invoking immediately.\n *\n * Keeping this as `false` is recommended since this option was only added to\n * make testing this function itself easier.\n *\n * @defaultValue `false`\n */\n raf?: boolean;\n\n /**\n * Keeping this as the `resizeObserverManager` is recommended since this\n * option was only added to make testing this function easier itself.\n *\n * @defaultValue `resizeObserverManager`\n */\n manager?: ResizeObserverManager;\n}\n\n/**\n * Initializes the `ResizeObserverMock` to be used for tests.\n *\n * @example Main Usage\n * ```tsx\n * import { useCallback, useState } from \"react\";\n * import {\n * cleanupResizeObserverAfterEach,\n * render,\n * screen,\n * setupResizeObserverMock,\n * } from \"@react-md/core/test-utils\";\n * import { useResizeObserver } from \"@react-md/core\";\n *\n * function ExampleComponent() {\n * const [size, setSize] = useState({ height: 0, width: 0 });\n * const ref = useResizeObserver({\n * onUpdate: useCallback((entry) => {\n * setSize({\n * height: entry.contentRect.height,\n * width: entry.contentRect.width,\n * });\n * });\n * });\n *\n * return (\n * <>\n * <div data-testid=\"size\">{JSON.stringify(size)}</div>\n * <div data-testid=\"resize-target\" ref={ref} />\n * </>\n * );\n * }\n *\n * cleanupResizeObserverAfterEach();\n *\n * describe(\"ExampleComponent\", () => {\n * it(\"should do stuff\", () => {\n * const observer = setupResizeObserverMock();\n * render(<ExampleComponent />)\n *\n * const size = screen.getByTestId(\"size\");\n * const resizeTarget = screen.getByTestId(\"resize-target\")\n *\n * // jsdom sets all element sizes to 0 by default\n * expect(size).toHaveTextContent(JSON.stringify({ height: 0, width: 0 }));\n *\n * // you can trigger with a custom change\n * act(() => {\n * observer.resizeElement(resizeTarget, { height: 100, width: 100 });\n * });\n * expect(size).toHaveTextContent(JSON.stringify({ height: 100, width: 100 }));\n *\n * // or you can mock the `getBoundingClientRect` result\n * jest.spyOn(resizeTarget, \"getBoundingClientRect\")\n * .mockReturnValue({\n * ...document.body.getBoundingClientRect(),\n * height: 200,\n * width: 200,\n * }):\n *\n * act(() => {\n * observer.resizeElement(resizeTarget);\n * });\n * expect(size).toHaveTextContent(JSON.stringify({ height: 200, width: 200 }));\n * });\n * })\n * ```\n *\n * @since 6.0.0\n */\nexport function setupResizeObserverMock(\n options: SetupResizeObserverMockOptions = {}\n): ResizeObserverMock {\n const { raf, manager = resizeObserverManager } = options;\n\n const resizeObserver = new ResizeObserverMock((entries) => {\n if (raf) {\n window.cancelAnimationFrame(manager.frame);\n manager.frame = window.requestAnimationFrame(() => {\n manager.handleResizeEntries(entries);\n });\n } else {\n manager.handleResizeEntries(entries);\n }\n });\n manager.sharedObserver = resizeObserver;\n return resizeObserver;\n}\n\n/**\n * @see {@link setupResizeObserverMock}\n * @since 6.0.0\n */\nexport function cleanupResizeObserverAfterEach(restoreAllMocks = true): void {\n afterEach(() => {\n resizeObserverManager.frame = 0;\n resizeObserverManager.subscriptions = new Map();\n resizeObserverManager.sharedObserver = undefined;\n\n if (restoreAllMocks) {\n jest.restoreAllMocks();\n }\n });\n}\n"],"names":["afterEach","jest","resizeObserverManager","createResizeObserverEntry","target","size","contentRect","getBoundingClientRect","height","width","boxSize","blockSize","inlineSize","borderBoxSize","contentBoxSize","devicePixelContentBoxSize","ResizeObserverMock","constructor","callback","elements","observe","unobserve","disconnect","resizeElement","resizeAllElements","add","delete","clear","changesOrGetEntry","has","Error","entry","getEntry","entries","map","element","Set","setupResizeObserverMock","options","raf","manager","resizeObserver","window","cancelAnimationFrame","frame","requestAnimationFrame","handleResizeEntries","sharedObserver","cleanupResizeObserverAfterEach","restoreAllMocks","subscriptions","Map","undefined"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/test-utils/ResizeObserver.ts"],"sourcesContent":["import { afterEach, jest } from \"@jest/globals\";\nimport {\n resizeObserverManager,\n type ResizeObserverManager,\n} from \"../useResizeObserver.js\";\n\n/**\n * @since 6.0.0\n */\nexport interface ResizeObserverEntrySize {\n height?: number;\n width?: number;\n}\n\n/**\n * @since 6.0.0\n */\nexport type GetResizeObserverEntryMock = (\n target: Element,\n size?: ResizeObserverEntrySize\n) => ResizeObserverEntry;\n\n/**\n * This is mostly an internal function to be used with the {@link ResizeObserverMock}\n * and {@link setupResizeObserverMock}\n *\n * @since 6.0.0\n */\nexport const createResizeObserverEntry: GetResizeObserverEntryMock = (\n target,\n size\n) => {\n const contentRect = target.getBoundingClientRect();\n if (typeof size?.height === \"number\") {\n contentRect.height = size.height;\n }\n if (typeof size?.width === \"number\") {\n contentRect.width = size.width;\n }\n\n const boxSize: ResizeObserverSize = {\n blockSize: contentRect.height,\n inlineSize: contentRect.width,\n };\n\n return {\n target,\n contentRect,\n borderBoxSize: [boxSize],\n contentBoxSize: [boxSize],\n devicePixelContentBoxSize: [],\n };\n};\n\n/**\n * This is the default ResizeObserver implementation if it does not already\n * exist in jsdom. You normally should not use this directly and instead use the\n * {@link setupResizeObserverMock} instead.\n *\n * @since 6.0.0\n */\nexport class ResizeObserverMock implements ResizeObserver {\n elements: Set<Element>;\n\n constructor(public callback: ResizeObserverCallback) {\n this.elements = new Set();\n }\n\n observe = (target: Element): void => {\n this.elements.add(target);\n this.resizeAllElements(createResizeObserverEntry);\n };\n\n unobserve = (target: Element): void => {\n this.elements.delete(target);\n };\n\n disconnect = (): void => {\n this.elements.clear();\n };\n\n /**\n * Triggers the resize event for a specific element. This must be wrapped in\n * `act`.\n *\n * @example Main Usage\n * ```tsx\n * import {\n * cleanupResizeObserverAfterEach,\n * setupResizeObserverMock,\n * } from \"@react-md/core/test-utils\";\n * import { useResizeObserver } from \"@react-md/core\";\n * import { ExampleComponent } from \"../ExampleComponent.js\";\n *\n * cleanupResizeObserverAfterEach();\n *\n * describe(\"ExampleComponent\", () => {\n * it(\"should do stuff\", () => {\n * const observer = setupResizeObserverMock();\n * render(<ExampleComponent />)\n *\n * const resizeTarget = screen.getByTestId(\"resize-target\")\n *\n * // you can trigger with a custom change\n * act(() => {\n * observer.resizeElement(resizeTarget, { height: 100, width: 100 });\n * });\n * // expect resize changes\n * });\n * })\n * ```\n */\n resizeElement = (\n target: Element,\n changesOrGetEntry:\n | GetResizeObserverEntryMock\n | ResizeObserverEntrySize\n | ResizeObserverEntry = createResizeObserverEntry\n ): void => {\n if (!this.elements.has(target)) {\n throw new Error(\n \"The `ResizeObserverMock` is not watching the target element and cannot be resized\"\n );\n }\n\n let entry: ResizeObserverEntry;\n if (typeof changesOrGetEntry === \"function\") {\n entry = changesOrGetEntry(target);\n } else if (!(\"contentRect\" in changesOrGetEntry)) {\n entry = createResizeObserverEntry(target, changesOrGetEntry);\n } else {\n entry = changesOrGetEntry;\n }\n\n this.callback([entry], this);\n };\n\n /**\n * You'll normally want to use {@link resizeElement} instead, but this can be\n * used to resize all the watched elements at once.\n *\n * @example\n * ```tsx\n * import {\n * act,\n * createResizeObserverEntry,\n * render,\n * screen,\n * setupResizeObserverMock,\n * } from \"@react-md/core/test-utils\";\n *\n * const observer = setupResizeObserverMock();\n * const { container } = render(<Test />)\n * expect(container).toMatchSnapshot()\n *\n * const target1 = screen.getByTestId('target-1');\n * const target2 = screen.getByTestId('target-2');\n * const target3 = screen.getByTestId('target-3');\n *\n * act(() => {\n * observer.resizeAllElements((element) => {\n * let height: number | undefined;\n * let width: number | undefined;\n * switch (element) {\n * case target1:\n * height = 400;\n * width = 250;\n * break;\n * case target2:\n * height = 100;\n * width = 380;\n * break;\n * case target3:\n * height = 24;\n * width = 24;\n * break;\n * }\n *\n * return createResizeObserverEntry(element, { height, width });\n * });\n * });\n * expect(container).toMatchSnapshot()\n * ```\n */\n resizeAllElements = (getEntry = createResizeObserverEntry): void => {\n const entries = [...this.elements].map((element) => getEntry(element));\n this.callback(entries, this);\n };\n}\n\n/**\n * @since 6.0.0\n */\nexport interface SetupResizeObserverMockOptions {\n /**\n * Set this to `true` to mimic the real `ResizeObserver` behavior where the\n * updates occur after an animation frame instead of invoking immediately.\n *\n * Keeping this as `false` is recommended since this option was only added to\n * make testing this function itself easier.\n *\n * @defaultValue `false`\n */\n raf?: boolean;\n\n /**\n * Keeping this as the `resizeObserverManager` is recommended since this\n * option was only added to make testing this function easier itself.\n *\n * @defaultValue `resizeObserverManager`\n */\n manager?: ResizeObserverManager;\n}\n\n/**\n * Initializes the `ResizeObserverMock` to be used for tests.\n *\n * @example Main Usage\n * ```tsx\n * import {\n * cleanupResizeObserverAfterEach,\n * render,\n * screen,\n * setupResizeObserverMock,\n * } from \"@react-md/core/test-utils\";\n * import { useResizeObserver } from \"@react-md/core/useResizeObserver\";\n * import { useCallback, useState } from \"react\";\n *\n * function ExampleComponent() {\n * const [size, setSize] = useState({ height: 0, width: 0 });\n * const ref = useResizeObserver({\n * onUpdate: useCallback((entry) => {\n * setSize({\n * height: entry.contentRect.height,\n * width: entry.contentRect.width,\n * });\n * }, []),\n * });\n *\n * return (\n * <>\n * <div data-testid=\"size\">{JSON.stringify(size)}</div>\n * <div data-testid=\"resize-target\" ref={ref} />\n * </>\n * );\n * }\n *\n * cleanupResizeObserverAfterEach();\n *\n * describe(\"ExampleComponent\", () => {\n * it(\"should do stuff\", () => {\n * const observer = setupResizeObserverMock();\n * render(<ExampleComponent />);\n *\n * const size = screen.getByTestId(\"size\");\n * const resizeTarget = screen.getByTestId(\"resize-target\");\n *\n * // jsdom sets all element sizes to 0 by default\n * expect(size).toHaveTextContent(JSON.stringify({ height: 0, width: 0 }));\n *\n * // you can trigger with a custom change\n * act(() => {\n * observer.resizeElement(resizeTarget, { height: 100, width: 100 });\n * });\n * expect(size).toHaveTextContent(JSON.stringify({ height: 100, width: 100 }));\n *\n * // or you can mock the `getBoundingClientRect` result\n * jest.spyOn(resizeTarget, \"getBoundingClientRect\").mockReturnValue({\n * ...document.body.getBoundingClientRect(),\n * height: 200,\n * width: 200,\n * });\n *\n * act(() => {\n * observer.resizeElement(resizeTarget);\n * });\n * expect(size).toHaveTextContent(JSON.stringify({ height: 200, width: 200 }));\n * });\n * });\n * ```\n *\n * @since 6.0.0\n */\nexport function setupResizeObserverMock(\n options: SetupResizeObserverMockOptions = {}\n): ResizeObserverMock {\n const { raf, manager = resizeObserverManager } = options;\n\n const resizeObserver = new ResizeObserverMock((entries) => {\n if (raf) {\n window.cancelAnimationFrame(manager.frame);\n manager.frame = window.requestAnimationFrame(() => {\n manager.handleResizeEntries(entries);\n });\n } else {\n manager.handleResizeEntries(entries);\n }\n });\n manager.sharedObserver = resizeObserver;\n return resizeObserver;\n}\n\n/**\n * @see {@link setupResizeObserverMock}\n * @since 6.0.0\n */\nexport function cleanupResizeObserverAfterEach(restoreAllMocks = true): void {\n afterEach(() => {\n resizeObserverManager.frame = 0;\n resizeObserverManager.subscriptions = new Map();\n resizeObserverManager.sharedObserver = undefined;\n\n if (restoreAllMocks) {\n jest.restoreAllMocks();\n }\n });\n}\n"],"names":["afterEach","jest","resizeObserverManager","createResizeObserverEntry","target","size","contentRect","getBoundingClientRect","height","width","boxSize","blockSize","inlineSize","borderBoxSize","contentBoxSize","devicePixelContentBoxSize","ResizeObserverMock","constructor","callback","elements","observe","unobserve","disconnect","resizeElement","resizeAllElements","add","delete","clear","changesOrGetEntry","has","Error","entry","getEntry","entries","map","element","Set","setupResizeObserverMock","options","raf","manager","resizeObserver","window","cancelAnimationFrame","frame","requestAnimationFrame","handleResizeEntries","sharedObserver","cleanupResizeObserverAfterEach","restoreAllMocks","subscriptions","Map","undefined"],"mappings":"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"}
@@ -71,7 +71,7 @@ export type MatchMediaSpiedFunction = jest.SpiedFunction<typeof window.matchMedi
71
71
  /**
72
72
  * @example Default Behavior
73
73
  * ```tsx
74
- * import { matchPhone, render, spyOnMatchMedia } from "@react-md/test-utils";
74
+ * import { matchPhone, render, spyOnMatchMedia } from "@react-md/core/test-utils";
75
75
  *
76
76
  * const matchMedia = spyOnMatchMedia();
77
77
  * render(<Test />);
@@ -84,7 +84,7 @@ export type MatchMediaSpiedFunction = jest.SpiedFunction<typeof window.matchMedi
84
84
  *
85
85
  * @example Set Default Media
86
86
  * ```tsx
87
- * import { matchPhone, render, spyOnMatchMedia } from "@react-md/test-utils";
87
+ * import { matchPhone, render, spyOnMatchMedia } from "@react-md/core/test-utils";
88
88
  *
89
89
  * const matchMedia = spyOnMatchMedia(matchPhone);
90
90
  * render(<Test />);
@@ -46,7 +46,7 @@ const noop = ()=>{
46
46
  /**
47
47
  * @example Default Behavior
48
48
  * ```tsx
49
- * import { matchPhone, render, spyOnMatchMedia } from "@react-md/test-utils";
49
+ * import { matchPhone, render, spyOnMatchMedia } from "@react-md/core/test-utils";
50
50
  *
51
51
  * const matchMedia = spyOnMatchMedia();
52
52
  * render(<Test />);
@@ -59,7 +59,7 @@ const noop = ()=>{
59
59
  *
60
60
  * @example Set Default Media
61
61
  * ```tsx
62
- * import { matchPhone, render, spyOnMatchMedia } from "@react-md/test-utils";
62
+ * import { matchPhone, render, spyOnMatchMedia } from "@react-md/core/test-utils";
63
63
  *
64
64
  * const matchMedia = spyOnMatchMedia(matchPhone);
65
65
  * render(<Test />);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/test-utils/matchMedia.ts"],"sourcesContent":["import { jest } from \"@jest/globals\";\nimport { act } from \"@testing-library/react\";\nimport {\n DEFAULT_DESKTOP_LARGE_MIN_WIDTH,\n DEFAULT_DESKTOP_MIN_WIDTH,\n DEFAULT_PHONE_MAX_WIDTH,\n DEFAULT_TABLET_MIN_WIDTH,\n} from \"../media-queries/appSize.js\";\n\nconst noop = (): void => {\n // do nothing\n};\n\n/**\n * @see {@link spyOnMatchMedia} instead\n * @internal\n * @since 6.0.0\n */\nexport const BASE_MEDIA_QUERY_LIST: MediaQueryList = {\n media: \"\",\n matches: false,\n onchange: noop,\n addListener: noop,\n addEventListener: noop,\n removeEventListener: noop,\n removeListener: noop,\n dispatchEvent: () => false,\n};\n\n/** @since 6.0.0 */\nexport type MatchMediaMatcher = (query: string) => boolean;\n\n/**\n * @see {@link spyOnMatchMedia} for usage\n * @since 6.0.0\n * @returns `true` for phone media queries\n */\nexport const matchPhone: MatchMediaMatcher = (query) =>\n query.includes(DEFAULT_PHONE_MAX_WIDTH);\n\n/**\n * @see {@link spyOnMatchMedia} for usage\n * @since 6.0.0\n * @returns `true` for tablet media queries\n */\nexport const matchTablet: MatchMediaMatcher = (query) =>\n query.includes(DEFAULT_TABLET_MIN_WIDTH);\n\n/**\n * @see {@link spyOnMatchMedia} for usage\n * @since 6.0.0\n * @returns `true` for desktop media queries\n */\nexport const matchDesktop: MatchMediaMatcher = (query) =>\n query.includes(DEFAULT_DESKTOP_MIN_WIDTH);\n\n/**\n * @see {@link spyOnMatchMedia} for usage\n * @since 6.0.0\n * @returns `true` for large desktop media queries\n */\nexport const matchLargeDesktop: MatchMediaMatcher = (query) =>\n query.includes(DEFAULT_DESKTOP_LARGE_MIN_WIDTH);\n\n/**\n * @see {@link spyOnMatchMedia} for usage\n * @since 6.0.0\n * @returns `true` for both desktop and large desktop media queries\n */\nexport const matchAnyDesktop: MatchMediaMatcher = (query) =>\n matchDesktop(query) || matchLargeDesktop(query);\n\n/**\n * @since 6.0.0\n */\nexport type MatchMediaSpiedFunction = jest.SpiedFunction<\n typeof window.matchMedia\n> & {\n /**\n * @example Default Behavior\n * ```tsx\n * const matchMedia = spyOnMatchMedia();\n * render(<Test />);\n *\n * // expect desktop results\n *\n * matchMedia.changeViewport(matchPhone);\n * // expect phone results\n * ```\n *\n * @example Custom Act Behavior\n * ```tsx\n * const matchMedia = spyOnMatchMedia();\n * const { rerender } = render(<Test />);\n *\n * // expect desktop results\n *\n * matchMedia.changeViewport(matchPhone, false);\n * rerender(<Test key=\"new-key\" />);\n *\n * // expect phone results\n * ```\n */\n changeViewport: (matcher: MatchMediaMatcher, disableAct?: boolean) => void;\n};\n\n/**\n * @example Default Behavior\n * ```tsx\n * import { matchPhone, render, spyOnMatchMedia } from \"@react-md/test-utils\";\n *\n * const matchMedia = spyOnMatchMedia();\n * render(<Test />);\n *\n * // expect desktop results\n *\n * matchMedia.changeViewport(matchPhone);\n * // expect phone results\n * ```\n *\n * @example Set Default Media\n * ```tsx\n * import { matchPhone, render, spyOnMatchMedia } from \"@react-md/test-utils\";\n *\n * const matchMedia = spyOnMatchMedia(matchPhone);\n * render(<Test />);\n *\n * // expect phone results\n * ```\n *\n * @since 6.0.0\n */\nexport function spyOnMatchMedia(\n defaultMatch: MatchMediaMatcher = matchDesktop\n): MatchMediaSpiedFunction {\n type Listener = (event: MediaQueryListEvent) => void;\n\n const listeners = new Map<string, Listener>();\n const matchMedia = jest\n .spyOn(window, \"matchMedia\")\n .mockImplementation((query) => ({\n ...BASE_MEDIA_QUERY_LIST,\n addEventListener(type: string, listener: Listener | EventListenerObject) {\n /* c8 ignore start */\n if (typeof listener !== \"function\" || type !== \"change\") {\n return;\n }\n /* c8 ignore stop */\n\n listeners.set(query, listener);\n },\n removeEventListener(\n type: string,\n listener: Listener | EventListenerObject\n ) {\n /* c8 ignore start */\n if (typeof listener !== \"function\" || type !== \"change\") {\n return;\n }\n /* c8 ignore stop */\n\n listeners.delete(query);\n },\n matches: defaultMatch(query),\n }));\n\n const changeViewport = (\n matcher: MatchMediaMatcher,\n disableAct = false\n ): void => {\n const update = (): void => {\n window.dispatchEvent(new Event(\"resize\"));\n\n const event = new Event(\"change\");\n listeners.forEach((listener, query) => {\n listener({\n ...event,\n media: \"\",\n matches: matcher(query),\n });\n });\n };\n if (disableAct) {\n update();\n } else {\n act(update);\n }\n };\n\n const mock = matchMedia as MatchMediaSpiedFunction;\n mock.changeViewport = changeViewport;\n\n return mock;\n}\n"],"names":["jest","act","DEFAULT_DESKTOP_LARGE_MIN_WIDTH","DEFAULT_DESKTOP_MIN_WIDTH","DEFAULT_PHONE_MAX_WIDTH","DEFAULT_TABLET_MIN_WIDTH","noop","BASE_MEDIA_QUERY_LIST","media","matches","onchange","addListener","addEventListener","removeEventListener","removeListener","dispatchEvent","matchPhone","query","includes","matchTablet","matchDesktop","matchLargeDesktop","matchAnyDesktop","spyOnMatchMedia","defaultMatch","listeners","Map","matchMedia","spyOn","window","mockImplementation","type","listener","set","delete","changeViewport","matcher","disableAct","update","Event","event","forEach","mock"],"mappings":"AAAA,SAASA,IAAI,QAAQ,gBAAgB;AACrC,SAASC,GAAG,QAAQ,yBAAyB;AAC7C,SACEC,+BAA+B,EAC/BC,yBAAyB,EACzBC,uBAAuB,EACvBC,wBAAwB,QACnB,8BAA8B;AAErC,MAAMC,OAAO;AACX,aAAa;AACf;AAEA;;;;CAIC,GACD,OAAO,MAAMC,wBAAwC;IACnDC,OAAO;IACPC,SAAS;IACTC,UAAUJ;IACVK,aAAaL;IACbM,kBAAkBN;IAClBO,qBAAqBP;IACrBQ,gBAAgBR;IAChBS,eAAe,IAAM;AACvB,EAAE;AAKF;;;;CAIC,GACD,OAAO,MAAMC,aAAgC,CAACC,QAC5CA,MAAMC,QAAQ,CAACd,yBAAyB;AAE1C;;;;CAIC,GACD,OAAO,MAAMe,cAAiC,CAACF,QAC7CA,MAAMC,QAAQ,CAACb,0BAA0B;AAE3C;;;;CAIC,GACD,OAAO,MAAMe,eAAkC,CAACH,QAC9CA,MAAMC,QAAQ,CAACf,2BAA2B;AAE5C;;;;CAIC,GACD,OAAO,MAAMkB,oBAAuC,CAACJ,QACnDA,MAAMC,QAAQ,CAAChB,iCAAiC;AAElD;;;;CAIC,GACD,OAAO,MAAMoB,kBAAqC,CAACL,QACjDG,aAAaH,UAAUI,kBAAkBJ,OAAO;AAoClD;;;;;;;;;;;;;;;;;;;;;;;;;CAyBC,GACD,OAAO,SAASM,gBACdC,eAAkCJ,YAAY;IAI9C,MAAMK,YAAY,IAAIC;IACtB,MAAMC,aAAa3B,KAChB4B,KAAK,CAACC,QAAQ,cACdC,kBAAkB,CAAC,CAACb,QAAW,CAAA;YAC9B,GAAGV,qBAAqB;YACxBK,kBAAiBmB,IAAY,EAAEC,QAAwC;gBACrE,mBAAmB,GACnB,IAAI,OAAOA,aAAa,cAAcD,SAAS,UAAU;oBACvD;gBACF;gBACA,kBAAkB,GAElBN,UAAUQ,GAAG,CAAChB,OAAOe;YACvB;YACAnB,qBACEkB,IAAY,EACZC,QAAwC;gBAExC,mBAAmB,GACnB,IAAI,OAAOA,aAAa,cAAcD,SAAS,UAAU;oBACvD;gBACF;gBACA,kBAAkB,GAElBN,UAAUS,MAAM,CAACjB;YACnB;YACAR,SAASe,aAAaP;QACxB,CAAA;IAEF,MAAMkB,iBAAiB,CACrBC,SACAC,aAAa,KAAK;QAElB,MAAMC,SAAS;YACbT,OAAOd,aAAa,CAAC,IAAIwB,MAAM;YAE/B,MAAMC,QAAQ,IAAID,MAAM;YACxBd,UAAUgB,OAAO,CAAC,CAACT,UAAUf;gBAC3Be,SAAS;oBACP,GAAGQ,KAAK;oBACRhC,OAAO;oBACPC,SAAS2B,QAAQnB;gBACnB;YACF;QACF;QACA,IAAIoB,YAAY;YACdC;QACF,OAAO;YACLrC,IAAIqC;QACN;IACF;IAEA,MAAMI,OAAOf;IACbe,KAAKP,cAAc,GAAGA;IAEtB,OAAOO;AACT"}
1
+ {"version":3,"sources":["../../src/test-utils/matchMedia.ts"],"sourcesContent":["import { jest } from \"@jest/globals\";\nimport { act } from \"@testing-library/react\";\nimport {\n DEFAULT_DESKTOP_LARGE_MIN_WIDTH,\n DEFAULT_DESKTOP_MIN_WIDTH,\n DEFAULT_PHONE_MAX_WIDTH,\n DEFAULT_TABLET_MIN_WIDTH,\n} from \"../media-queries/appSize.js\";\n\nconst noop = (): void => {\n // do nothing\n};\n\n/**\n * @see {@link spyOnMatchMedia} instead\n * @internal\n * @since 6.0.0\n */\nexport const BASE_MEDIA_QUERY_LIST: MediaQueryList = {\n media: \"\",\n matches: false,\n onchange: noop,\n addListener: noop,\n addEventListener: noop,\n removeEventListener: noop,\n removeListener: noop,\n dispatchEvent: () => false,\n};\n\n/** @since 6.0.0 */\nexport type MatchMediaMatcher = (query: string) => boolean;\n\n/**\n * @see {@link spyOnMatchMedia} for usage\n * @since 6.0.0\n * @returns `true` for phone media queries\n */\nexport const matchPhone: MatchMediaMatcher = (query) =>\n query.includes(DEFAULT_PHONE_MAX_WIDTH);\n\n/**\n * @see {@link spyOnMatchMedia} for usage\n * @since 6.0.0\n * @returns `true` for tablet media queries\n */\nexport const matchTablet: MatchMediaMatcher = (query) =>\n query.includes(DEFAULT_TABLET_MIN_WIDTH);\n\n/**\n * @see {@link spyOnMatchMedia} for usage\n * @since 6.0.0\n * @returns `true` for desktop media queries\n */\nexport const matchDesktop: MatchMediaMatcher = (query) =>\n query.includes(DEFAULT_DESKTOP_MIN_WIDTH);\n\n/**\n * @see {@link spyOnMatchMedia} for usage\n * @since 6.0.0\n * @returns `true` for large desktop media queries\n */\nexport const matchLargeDesktop: MatchMediaMatcher = (query) =>\n query.includes(DEFAULT_DESKTOP_LARGE_MIN_WIDTH);\n\n/**\n * @see {@link spyOnMatchMedia} for usage\n * @since 6.0.0\n * @returns `true` for both desktop and large desktop media queries\n */\nexport const matchAnyDesktop: MatchMediaMatcher = (query) =>\n matchDesktop(query) || matchLargeDesktop(query);\n\n/**\n * @since 6.0.0\n */\nexport type MatchMediaSpiedFunction = jest.SpiedFunction<\n typeof window.matchMedia\n> & {\n /**\n * @example Default Behavior\n * ```tsx\n * const matchMedia = spyOnMatchMedia();\n * render(<Test />);\n *\n * // expect desktop results\n *\n * matchMedia.changeViewport(matchPhone);\n * // expect phone results\n * ```\n *\n * @example Custom Act Behavior\n * ```tsx\n * const matchMedia = spyOnMatchMedia();\n * const { rerender } = render(<Test />);\n *\n * // expect desktop results\n *\n * matchMedia.changeViewport(matchPhone, false);\n * rerender(<Test key=\"new-key\" />);\n *\n * // expect phone results\n * ```\n */\n changeViewport: (matcher: MatchMediaMatcher, disableAct?: boolean) => void;\n};\n\n/**\n * @example Default Behavior\n * ```tsx\n * import { matchPhone, render, spyOnMatchMedia } from \"@react-md/core/test-utils\";\n *\n * const matchMedia = spyOnMatchMedia();\n * render(<Test />);\n *\n * // expect desktop results\n *\n * matchMedia.changeViewport(matchPhone);\n * // expect phone results\n * ```\n *\n * @example Set Default Media\n * ```tsx\n * import { matchPhone, render, spyOnMatchMedia } from \"@react-md/core/test-utils\";\n *\n * const matchMedia = spyOnMatchMedia(matchPhone);\n * render(<Test />);\n *\n * // expect phone results\n * ```\n *\n * @since 6.0.0\n */\nexport function spyOnMatchMedia(\n defaultMatch: MatchMediaMatcher = matchDesktop\n): MatchMediaSpiedFunction {\n type Listener = (event: MediaQueryListEvent) => void;\n\n const listeners = new Map<string, Listener>();\n const matchMedia = jest\n .spyOn(window, \"matchMedia\")\n .mockImplementation((query) => ({\n ...BASE_MEDIA_QUERY_LIST,\n addEventListener(type: string, listener: Listener | EventListenerObject) {\n /* c8 ignore start */\n if (typeof listener !== \"function\" || type !== \"change\") {\n return;\n }\n /* c8 ignore stop */\n\n listeners.set(query, listener);\n },\n removeEventListener(\n type: string,\n listener: Listener | EventListenerObject\n ) {\n /* c8 ignore start */\n if (typeof listener !== \"function\" || type !== \"change\") {\n return;\n }\n /* c8 ignore stop */\n\n listeners.delete(query);\n },\n matches: defaultMatch(query),\n }));\n\n const changeViewport = (\n matcher: MatchMediaMatcher,\n disableAct = false\n ): void => {\n const update = (): void => {\n window.dispatchEvent(new Event(\"resize\"));\n\n const event = new Event(\"change\");\n listeners.forEach((listener, query) => {\n listener({\n ...event,\n media: \"\",\n matches: matcher(query),\n });\n });\n };\n if (disableAct) {\n update();\n } else {\n act(update);\n }\n };\n\n const mock = matchMedia as MatchMediaSpiedFunction;\n mock.changeViewport = changeViewport;\n\n return mock;\n}\n"],"names":["jest","act","DEFAULT_DESKTOP_LARGE_MIN_WIDTH","DEFAULT_DESKTOP_MIN_WIDTH","DEFAULT_PHONE_MAX_WIDTH","DEFAULT_TABLET_MIN_WIDTH","noop","BASE_MEDIA_QUERY_LIST","media","matches","onchange","addListener","addEventListener","removeEventListener","removeListener","dispatchEvent","matchPhone","query","includes","matchTablet","matchDesktop","matchLargeDesktop","matchAnyDesktop","spyOnMatchMedia","defaultMatch","listeners","Map","matchMedia","spyOn","window","mockImplementation","type","listener","set","delete","changeViewport","matcher","disableAct","update","Event","event","forEach","mock"],"mappings":"AAAA,SAASA,IAAI,QAAQ,gBAAgB;AACrC,SAASC,GAAG,QAAQ,yBAAyB;AAC7C,SACEC,+BAA+B,EAC/BC,yBAAyB,EACzBC,uBAAuB,EACvBC,wBAAwB,QACnB,8BAA8B;AAErC,MAAMC,OAAO;AACX,aAAa;AACf;AAEA;;;;CAIC,GACD,OAAO,MAAMC,wBAAwC;IACnDC,OAAO;IACPC,SAAS;IACTC,UAAUJ;IACVK,aAAaL;IACbM,kBAAkBN;IAClBO,qBAAqBP;IACrBQ,gBAAgBR;IAChBS,eAAe,IAAM;AACvB,EAAE;AAKF;;;;CAIC,GACD,OAAO,MAAMC,aAAgC,CAACC,QAC5CA,MAAMC,QAAQ,CAACd,yBAAyB;AAE1C;;;;CAIC,GACD,OAAO,MAAMe,cAAiC,CAACF,QAC7CA,MAAMC,QAAQ,CAACb,0BAA0B;AAE3C;;;;CAIC,GACD,OAAO,MAAMe,eAAkC,CAACH,QAC9CA,MAAMC,QAAQ,CAACf,2BAA2B;AAE5C;;;;CAIC,GACD,OAAO,MAAMkB,oBAAuC,CAACJ,QACnDA,MAAMC,QAAQ,CAAChB,iCAAiC;AAElD;;;;CAIC,GACD,OAAO,MAAMoB,kBAAqC,CAACL,QACjDG,aAAaH,UAAUI,kBAAkBJ,OAAO;AAoClD;;;;;;;;;;;;;;;;;;;;;;;;;CAyBC,GACD,OAAO,SAASM,gBACdC,eAAkCJ,YAAY;IAI9C,MAAMK,YAAY,IAAIC;IACtB,MAAMC,aAAa3B,KAChB4B,KAAK,CAACC,QAAQ,cACdC,kBAAkB,CAAC,CAACb,QAAW,CAAA;YAC9B,GAAGV,qBAAqB;YACxBK,kBAAiBmB,IAAY,EAAEC,QAAwC;gBACrE,mBAAmB,GACnB,IAAI,OAAOA,aAAa,cAAcD,SAAS,UAAU;oBACvD;gBACF;gBACA,kBAAkB,GAElBN,UAAUQ,GAAG,CAAChB,OAAOe;YACvB;YACAnB,qBACEkB,IAAY,EACZC,QAAwC;gBAExC,mBAAmB,GACnB,IAAI,OAAOA,aAAa,cAAcD,SAAS,UAAU;oBACvD;gBACF;gBACA,kBAAkB,GAElBN,UAAUS,MAAM,CAACjB;YACnB;YACAR,SAASe,aAAaP;QACxB,CAAA;IAEF,MAAMkB,iBAAiB,CACrBC,SACAC,aAAa,KAAK;QAElB,MAAMC,SAAS;YACbT,OAAOd,aAAa,CAAC,IAAIwB,MAAM;YAE/B,MAAMC,QAAQ,IAAID,MAAM;YACxBd,UAAUgB,OAAO,CAAC,CAACT,UAAUf;gBAC3Be,SAAS;oBACP,GAAGQ,KAAK;oBACRhC,OAAO;oBACPC,SAAS2B,QAAQnB;gBACnB;YACF;QACF;QACA,IAAIoB,YAAY;YACdC;QACF,OAAO;YACLrC,IAAIqC;QACN;IACF;IAEA,MAAMI,OAAOf;IACbe,KAAKP,cAAc,GAAGA;IAEtB,OAAOO;AACT"}
@@ -109,11 +109,20 @@ $_linear-channel-values: (
109
109
  // @param {Color} color - The color to check
110
110
  // @returns {number} A number representing the luminance for the color.
111
111
  @function _luminance($color) {
112
- $red: list.nth($_linear-channel-values, color.channel($color, "red") + 1);
112
+ $red: list.nth(
113
+ $_linear-channel-values,
114
+ math.round(color.channel($color, "red")) + 1
115
+ );
113
116
  $red-multiplier: 0.2126;
114
- $green: list.nth($_linear-channel-values, color.channel($color, "green") + 1);
117
+ $green: list.nth(
118
+ $_linear-channel-values,
119
+ math.round(color.channel($color, "green")) + 1
120
+ );
115
121
  $green-multiplier: 0.7152;
116
- $blue: list.nth($_linear-channel-values, color.channel($color, "blue") + 1);
122
+ $blue: list.nth(
123
+ $_linear-channel-values,
124
+ math.round(color.channel($color, "blue")) + 1
125
+ );
117
126
  $blue-multiplier: 0.0722;
118
127
 
119
128
  @return ($red * $red-multiplier) + ($green * $green-multiplier) +
@@ -101,6 +101,25 @@ export interface ResizeObserverHookOptions<E extends HTMLElement> {
101
101
  * For most cases you can use the {@link useElementSize} instead, but this hook
102
102
  * can be used for more complex behavior with the {@link ResizeObserverEntry}.
103
103
  *
104
+ * @example Simple Example
105
+ * ```tsx
106
+ * import { useResizeObserver } from "@react-md/core/useResizeObserver";
107
+ * import { useCallback, useState, type ReactElement } from "react";
108
+ *
109
+ * function Example(): ReactElement {
110
+ * const elementRef = useResizeObserver({
111
+ * onUpdate: useCallback((entry) => {
112
+ * const element = entry.target;
113
+ * const { height, width } = entry.contentRect;
114
+ * const { inlineSize, blockSize } = entry.borderBoxSize[0]O
115
+ * // do something
116
+ * }, []),
117
+ * });
118
+ *
119
+ * return <div ref={elementRef}>{...whatever...}</div>
120
+ * }
121
+ * ```
122
+ *
104
123
  * @since 2.3.0
105
124
  * @since 6.0.0 The API was updated to match the `useIntersectionObserver`
106
125
  * implementation -- accepts only a single object parameter and returns a
@@ -99,6 +99,25 @@ import { useEnsuredRef } from "./useEnsuredRef.js";
99
99
  * For most cases you can use the {@link useElementSize} instead, but this hook
100
100
  * can be used for more complex behavior with the {@link ResizeObserverEntry}.
101
101
  *
102
+ * @example Simple Example
103
+ * ```tsx
104
+ * import { useResizeObserver } from "@react-md/core/useResizeObserver";
105
+ * import { useCallback, useState, type ReactElement } from "react";
106
+ *
107
+ * function Example(): ReactElement {
108
+ * const elementRef = useResizeObserver({
109
+ * onUpdate: useCallback((entry) => {
110
+ * const element = entry.target;
111
+ * const { height, width } = entry.contentRect;
112
+ * const { inlineSize, blockSize } = entry.borderBoxSize[0]O
113
+ * // do something
114
+ * }, []),
115
+ * });
116
+ *
117
+ * return <div ref={elementRef}>{...whatever...}</div>
118
+ * }
119
+ * ```
120
+ *
102
121
  * @since 2.3.0
103
122
  * @since 6.0.0 The API was updated to match the `useIntersectionObserver`
104
123
  * implementation -- accepts only a single object parameter and returns a
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/useResizeObserver.ts"],"sourcesContent":["\"use client\";\nimport { useEffect, type Ref, type RefCallback } from \"react\";\nimport { useEnsuredRef } from \"./useEnsuredRef.js\";\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport { type useElementSize } from \"./useElementSize.js\";\n\n/**\n * @since 6.0.0\n */\nexport type ResizeObserverEntryCallback = (entry: ResizeObserverEntry) => void;\n\n/** @internal */\ntype Unsubscribe = () => void;\n\n/** @internal */\ninterface TargetSize {\n height: number;\n width: number;\n scrollHeight: number;\n scrollWidth: number;\n}\n\n/** @internal */\ninterface TargetSubscription {\n onUpdate: ResizeObserverEntryCallback;\n disableHeight: boolean;\n disableWidth: boolean;\n\n size?: TargetSize;\n}\n\n/** @internal */\ninterface SubscribeOptions {\n element: Element;\n onUpdate: ResizeObserverEntryCallback;\n disableHeight: boolean;\n disableWidth: boolean;\n}\n\n/**\n * @internal\n * @since 6.0.0 This was added to help with testing. The\n * `subscriptions` and `sharedObserver` used to be module-level variables but\n * moving to a class makes it easier to mock. Checkout the\n * `src/tests-utils/ResizeObserver.ts`\n */\nexport class ResizeObserverManager {\n frame: number;\n subscriptions: Map<Element, Set<TargetSubscription>>;\n\n /**\n * Why is there a single shared observer instead of multiple and a\n * \"subscription\" model?\n *\n * Note: Probably a bit of a premature optimization right now...\n *\n * @see https://github.com/WICG/resize-observer/issues/59\n * @internal\n */\n sharedObserver: ResizeObserver | undefined;\n\n constructor() {\n this.frame = 0;\n this.subscriptions = new Map();\n }\n\n subscribe = (options: SubscribeOptions): Unsubscribe => {\n const { element, onUpdate, disableHeight, disableWidth } = options;\n\n // lazy initialize the observer\n const observer =\n this.sharedObserver ||\n new ResizeObserver((entries) => {\n // this prevents the `ResizeObserver loop limit exceeded`\n window.cancelAnimationFrame(this.frame);\n this.frame = window.requestAnimationFrame(() => {\n this.handleResizeEntries(entries);\n });\n });\n this.sharedObserver = observer;\n\n const updates = this.subscriptions.get(element) || new Set();\n const subscription: TargetSubscription = {\n onUpdate,\n disableHeight,\n disableWidth,\n };\n updates.add(subscription);\n if (!this.subscriptions.has(element)) {\n this.subscriptions.set(element, updates);\n }\n\n observer.observe(element);\n\n return () => {\n observer.unobserve(element);\n updates.delete(subscription);\n };\n };\n\n handleResizeEntries = (entries: ResizeObserverEntry[]): void => {\n for (const entry of entries) {\n const targetSubscriptions = this.subscriptions.get(entry.target);\n // shouldn't really happen\n /* c8 ignore start */\n if (!targetSubscriptions) {\n continue;\n }\n /* c8 ignore stop */\n\n const entries = targetSubscriptions.values();\n for (const subscription of entries) {\n const { height, width } = entry.contentRect;\n const { scrollHeight, scrollWidth } = entry.target;\n const { onUpdate, size, disableHeight, disableWidth } = subscription;\n const isHeightChange =\n !disableHeight &&\n (!size ||\n size.height !== height ||\n size.scrollHeight !== scrollHeight);\n const isWidthChange =\n !disableWidth &&\n (!size || size.width !== width || size.scrollWidth !== scrollWidth);\n\n subscription.size = {\n height,\n width,\n scrollHeight,\n scrollWidth,\n };\n if (isHeightChange || isWidthChange) {\n onUpdate(entry);\n }\n }\n }\n };\n}\n\n/**\n * @internal\n * @since 6.0.0\n */\nexport const resizeObserverManager = new ResizeObserverManager();\n\n/**\n * @since 2.3.0\n * @since 6.0.0 Renamed from `UseResizeObserverOptions` and added\n * `onUpdate`/`disabled` options.\n */\nexport interface ResizeObserverHookOptions<E extends HTMLElement> {\n /**\n * An optional ref to merge with the ref returned by this hook.\n */\n ref?: Ref<E>;\n\n /**\n * **Must be wrapped in `useCallback` to prevent re-creating the\n * ResizeObserver each render.**\n *\n * This function will be called whenever the target element resizes.\n *\n * @see {@link useResizeObserver} for an example.\n */\n onUpdate: ResizeObserverEntryCallback;\n\n /**\n * Set this to `true` to prevent observing the element's size changes. This is\n * equivalent to not attaching the returned ref to any element.\n *\n * @defaultValue `false`\n */\n disabled?: boolean;\n\n /**\n * Set this to `true` if the {@link onUpdate} should not be fired for height\n * changes.\n *\n * @defaultValue `false`\n */\n disableHeight?: boolean;\n\n /**\n * Set this to `true` if the {@link onUpdate} should not be fired for width\n * changes.\n *\n * @defaultValue `false`\n */\n disableWidth?: boolean;\n}\n\n/**\n * The resize observer is used to track the size changes of a specific element.\n * For most cases you can use the {@link useElementSize} instead, but this hook\n * can be used for more complex behavior with the {@link ResizeObserverEntry}.\n *\n * @since 2.3.0\n * @since 6.0.0 The API was updated to match the `useIntersectionObserver`\n * implementation -- accepts only a single object parameter and returns a\n * {@link RefCallback} instead of `[nodeRef, refCallback]`\n */\nexport function useResizeObserver<E extends HTMLElement>(\n options: ResizeObserverHookOptions<E>\n): RefCallback<E> {\n const {\n ref,\n onUpdate,\n disabled,\n disableHeight = false,\n disableWidth = false,\n } = options;\n\n const [targetNodeRef, refCallback] = useEnsuredRef(ref);\n useEffect(() => {\n const element = targetNodeRef.current;\n if (disabled || (disableHeight && disableWidth) || !element) {\n return;\n }\n\n const unsubscribe = resizeObserverManager.subscribe({\n element,\n onUpdate,\n disableHeight,\n disableWidth,\n });\n\n return () => {\n unsubscribe();\n };\n }, [disableHeight, disableWidth, disabled, onUpdate, targetNodeRef]);\n\n return refCallback;\n}\n"],"names":["useEffect","useEnsuredRef","ResizeObserverManager","constructor","frame","subscriptions","sharedObserver","subscribe","options","element","onUpdate","disableHeight","disableWidth","observer","ResizeObserver","entries","window","cancelAnimationFrame","requestAnimationFrame","handleResizeEntries","updates","get","Set","subscription","add","has","set","observe","unobserve","delete","entry","targetSubscriptions","target","values","height","width","contentRect","scrollHeight","scrollWidth","size","isHeightChange","isWidthChange","Map","resizeObserverManager","useResizeObserver","ref","disabled","targetNodeRef","refCallback","current","unsubscribe"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/useResizeObserver.ts"],"sourcesContent":["\"use client\";\nimport { useEffect, type Ref, type RefCallback } from \"react\";\nimport { useEnsuredRef } from \"./useEnsuredRef.js\";\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport { type useElementSize } from \"./useElementSize.js\";\n\n/**\n * @since 6.0.0\n */\nexport type ResizeObserverEntryCallback = (entry: ResizeObserverEntry) => void;\n\n/** @internal */\ntype Unsubscribe = () => void;\n\n/** @internal */\ninterface TargetSize {\n height: number;\n width: number;\n scrollHeight: number;\n scrollWidth: number;\n}\n\n/** @internal */\ninterface TargetSubscription {\n onUpdate: ResizeObserverEntryCallback;\n disableHeight: boolean;\n disableWidth: boolean;\n\n size?: TargetSize;\n}\n\n/** @internal */\ninterface SubscribeOptions {\n element: Element;\n onUpdate: ResizeObserverEntryCallback;\n disableHeight: boolean;\n disableWidth: boolean;\n}\n\n/**\n * @internal\n * @since 6.0.0 This was added to help with testing. The\n * `subscriptions` and `sharedObserver` used to be module-level variables but\n * moving to a class makes it easier to mock. Checkout the\n * `src/tests-utils/ResizeObserver.ts`\n */\nexport class ResizeObserverManager {\n frame: number;\n subscriptions: Map<Element, Set<TargetSubscription>>;\n\n /**\n * Why is there a single shared observer instead of multiple and a\n * \"subscription\" model?\n *\n * Note: Probably a bit of a premature optimization right now...\n *\n * @see https://github.com/WICG/resize-observer/issues/59\n * @internal\n */\n sharedObserver: ResizeObserver | undefined;\n\n constructor() {\n this.frame = 0;\n this.subscriptions = new Map();\n }\n\n subscribe = (options: SubscribeOptions): Unsubscribe => {\n const { element, onUpdate, disableHeight, disableWidth } = options;\n\n // lazy initialize the observer\n const observer =\n this.sharedObserver ||\n new ResizeObserver((entries) => {\n // this prevents the `ResizeObserver loop limit exceeded`\n window.cancelAnimationFrame(this.frame);\n this.frame = window.requestAnimationFrame(() => {\n this.handleResizeEntries(entries);\n });\n });\n this.sharedObserver = observer;\n\n const updates = this.subscriptions.get(element) || new Set();\n const subscription: TargetSubscription = {\n onUpdate,\n disableHeight,\n disableWidth,\n };\n updates.add(subscription);\n if (!this.subscriptions.has(element)) {\n this.subscriptions.set(element, updates);\n }\n\n observer.observe(element);\n\n return () => {\n observer.unobserve(element);\n updates.delete(subscription);\n };\n };\n\n handleResizeEntries = (entries: ResizeObserverEntry[]): void => {\n for (const entry of entries) {\n const targetSubscriptions = this.subscriptions.get(entry.target);\n // shouldn't really happen\n /* c8 ignore start */\n if (!targetSubscriptions) {\n continue;\n }\n /* c8 ignore stop */\n\n const entries = targetSubscriptions.values();\n for (const subscription of entries) {\n const { height, width } = entry.contentRect;\n const { scrollHeight, scrollWidth } = entry.target;\n const { onUpdate, size, disableHeight, disableWidth } = subscription;\n const isHeightChange =\n !disableHeight &&\n (!size ||\n size.height !== height ||\n size.scrollHeight !== scrollHeight);\n const isWidthChange =\n !disableWidth &&\n (!size || size.width !== width || size.scrollWidth !== scrollWidth);\n\n subscription.size = {\n height,\n width,\n scrollHeight,\n scrollWidth,\n };\n if (isHeightChange || isWidthChange) {\n onUpdate(entry);\n }\n }\n }\n };\n}\n\n/**\n * @internal\n * @since 6.0.0\n */\nexport const resizeObserverManager = new ResizeObserverManager();\n\n/**\n * @since 2.3.0\n * @since 6.0.0 Renamed from `UseResizeObserverOptions` and added\n * `onUpdate`/`disabled` options.\n */\nexport interface ResizeObserverHookOptions<E extends HTMLElement> {\n /**\n * An optional ref to merge with the ref returned by this hook.\n */\n ref?: Ref<E>;\n\n /**\n * **Must be wrapped in `useCallback` to prevent re-creating the\n * ResizeObserver each render.**\n *\n * This function will be called whenever the target element resizes.\n *\n * @see {@link useResizeObserver} for an example.\n */\n onUpdate: ResizeObserverEntryCallback;\n\n /**\n * Set this to `true` to prevent observing the element's size changes. This is\n * equivalent to not attaching the returned ref to any element.\n *\n * @defaultValue `false`\n */\n disabled?: boolean;\n\n /**\n * Set this to `true` if the {@link onUpdate} should not be fired for height\n * changes.\n *\n * @defaultValue `false`\n */\n disableHeight?: boolean;\n\n /**\n * Set this to `true` if the {@link onUpdate} should not be fired for width\n * changes.\n *\n * @defaultValue `false`\n */\n disableWidth?: boolean;\n}\n\n/**\n * The resize observer is used to track the size changes of a specific element.\n * For most cases you can use the {@link useElementSize} instead, but this hook\n * can be used for more complex behavior with the {@link ResizeObserverEntry}.\n *\n * @example Simple Example\n * ```tsx\n * import { useResizeObserver } from \"@react-md/core/useResizeObserver\";\n * import { useCallback, useState, type ReactElement } from \"react\";\n *\n * function Example(): ReactElement {\n * const elementRef = useResizeObserver({\n * onUpdate: useCallback((entry) => {\n * const element = entry.target;\n * const { height, width } = entry.contentRect;\n * const { inlineSize, blockSize } = entry.borderBoxSize[0]O\n * // do something\n * }, []),\n * });\n *\n * return <div ref={elementRef}>{...whatever...}</div>\n * }\n * ```\n *\n * @since 2.3.0\n * @since 6.0.0 The API was updated to match the `useIntersectionObserver`\n * implementation -- accepts only a single object parameter and returns a\n * {@link RefCallback} instead of `[nodeRef, refCallback]`\n */\nexport function useResizeObserver<E extends HTMLElement>(\n options: ResizeObserverHookOptions<E>\n): RefCallback<E> {\n const {\n ref,\n onUpdate,\n disabled,\n disableHeight = false,\n disableWidth = false,\n } = options;\n\n const [targetNodeRef, refCallback] = useEnsuredRef(ref);\n useEffect(() => {\n const element = targetNodeRef.current;\n if (disabled || (disableHeight && disableWidth) || !element) {\n return;\n }\n\n const unsubscribe = resizeObserverManager.subscribe({\n element,\n onUpdate,\n disableHeight,\n disableWidth,\n });\n\n return () => {\n unsubscribe();\n };\n }, [disableHeight, disableWidth, disabled, onUpdate, targetNodeRef]);\n\n return refCallback;\n}\n"],"names":["useEffect","useEnsuredRef","ResizeObserverManager","constructor","frame","subscriptions","sharedObserver","subscribe","options","element","onUpdate","disableHeight","disableWidth","observer","ResizeObserver","entries","window","cancelAnimationFrame","requestAnimationFrame","handleResizeEntries","updates","get","Set","subscription","add","has","set","observe","unobserve","delete","entry","targetSubscriptions","target","values","height","width","contentRect","scrollHeight","scrollWidth","size","isHeightChange","isWidthChange","Map","resizeObserverManager","useResizeObserver","ref","disabled","targetNodeRef","refCallback","current","unsubscribe"],"mappings":"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"}
@@ -71,7 +71,7 @@ export interface RenderRecursivelyProps<Item = Record<string, unknown>, Data = u
71
71
  * ```
72
72
  *
73
73
  * @see {@link getRecursiveItemKey}
74
- * @defaultValue `({ index, depth }) => ${depth}-${index}`.
74
+ * @defaultValue `` ({ index, depth }) => `${depth}-${index}` ``
75
75
  */
76
76
  getItemKey?: (options: RecursiveItemKeyOptions<Item>) => string;
77
77
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/RenderRecursively.tsx"],"sourcesContent":["// TODO: Figure out how to strictly enforce the data\nimport { type ComponentType, type ReactElement, type ReactNode } from \"react\";\n\n/**\n * @since 6.0.0\n */\nexport type RecursiveItem<T = Record<string, unknown>> = T & {\n items?: readonly RecursiveItem<T>[];\n};\n\n/**\n * @since 6.0.0\n */\nexport interface RecursiveItemKeyOptions<Item = Record<string, unknown>> {\n item: RecursiveItem<Item>;\n depth: number;\n index: number;\n}\n\n/**\n * This is the default implementation for {@link RenderRecursivelyProps.getItemKey}.\n *\n * ```ts\n * return `${options.depth}-${options.index}`\n * ```\n *\n * @since 6.0.0\n */\nexport function getRecursiveItemKey<Item = Record<string, unknown>>(\n options: RecursiveItemKeyOptions<Item>\n): string {\n const { depth, index } = options;\n\n return `${depth}-${index}`;\n}\n\n/**\n * @since 6.0.0\n */\nexport interface RenderRecursiveItemsProps<\n Item = Record<string, unknown>,\n Data = unknown,\n> {\n data?: Data;\n\n /**\n * The current item to render.\n */\n item: RecursiveItem<Item>;\n\n /**\n * The list of parent items which can be used to determine the depth or \"share\n * props\" if the items contained props.\n */\n parents: readonly RecursiveItem<Item>[];\n\n /**\n * This will be provided if the {@link item} had child items and will be the\n * rendered content.\n */\n children?: ReactNode;\n}\n\n/**\n * @since 6.0.0\n */\nexport interface RenderRecursivelyProps<\n Item = Record<string, unknown>,\n Data = unknown,\n> {\n data?: Data;\n items: readonly RecursiveItem<Item>[];\n\n /**\n * The renderer for each item.\n */\n render: ComponentType<RenderRecursiveItemsProps<Item, Data>>;\n\n /**\n * This should not be used for external users. This is used to build the\n * {@link RenderRecursiveItemsProps.parents} list.\n *\n * @internal\n * @defaultValue `[]`\n */\n parents?: readonly RecursiveItem<Item>[];\n\n /**\n * Gets a React `key` for a specific item. This should be provided if the\n * items can be moved around to improve performance.\n *\n * @example\n * ```ts\n * getItemKey={({ item }) => item.id}\n * ```\n *\n * @see {@link getRecursiveItemKey}\n * @defaultValue `({ index, depth }) => ${depth}-${index}`.\n */\n getItemKey?: (options: RecursiveItemKeyOptions<Item>) => string;\n}\n\n/**\n * Helper component for recursively rendering specific data structures (mostly\n * trees). The main use-case is for rendering site navigation and the `Tree`\n * component.\n *\n * @example\n * ```tsx\n * import {\n * buildTree,\n * List,\n * ListItem,\n * ListItemLink,\n * RenderRecursively,\n * TreeData,\n * type DefaultTreeItemNode,\n * type RenderRecursiveItemsProps,\n * } from \"@react-md/core\";\n * import { type ReactElement } from \"react\";\n *\n * const navItems = {\n * \"/\": {\n * href: \"/\",\n * itemId: \"/\",\n * parentId: null,\n * children: \"Home\",\n * },\n * \"/route-1\": {\n * itemId: \"/route-1\",\n * parentId: null,\n * children: \"Collapsible\",\n * },\n * \"/nested-route-1\": {\n * itemId: \"/nested-route-1\",\n * parentId: \"/route-1\",\n * children: \"Child 1\",\n * },\n * \"/nested-route-2\": {\n * itemId: \"/nested-route-2\",\n * parentId: \"/route-2\",\n * children: \"Child 2\",\n * },\n * \"/divider-1\": {\n * itemId: \"/divider-1\",\n * parentId: null\n * },\n * \"/route-2\": {\n * itemId: \"/route-2\"\n * parentId: null,\n * children: \"Route 2\",\n * },\n * } satisfies TreeData;\n *\n * function NestedNavigation(props: RenderRecursiveItemsProps<DefaultTreeItemNode>): ReactElement {\n * const { item, parents, children } = props;\n * const { toggle, toggled: collapsed } = useToggle(false);\n * const { elementProps } = useCollapseTransition({\n * transitionIn: !collapsed,\n * });\n *\n * return (\n * <li>\n * <Button onClick={toggle}>{item.children}</Button>\n * <List {...elementProps}>{children}</List>\n * </li>\n * ):\n * }\n *\n *\n * function Render(props: RenderRecursiveItemsProps<DefaultTreeItemNode>): ReactElement {\n * const { item, parents } = props;\n * if (item.itemId.includes(\"divider\")) {\n * return <Divider />;\n * }\n *\n * if (item.items) {\n * return <NestedNavigation {...props} />\n * }\n *\n * const prefix = parents.map((parent) => parent.itemId).join(\"/\");\n * return (\n * <Link href={`${prefix}${item.itemId}`}>\n * {item.children}\n * </Link>\n * );\n * }\n * ```\n *\n *\n * @since 6.0.0\n */\nexport function RenderRecursively<Item, Data>(\n props: RenderRecursivelyProps<Item, Data>\n): ReactElement {\n const {\n data,\n items,\n render: Render,\n getItemKey = getRecursiveItemKey,\n parents = [],\n } = props;\n\n return (\n <>\n {items.map((item, index) => {\n let children: ReactNode;\n const depth = parents.length;\n if (item.items?.length) {\n children = (\n <RenderRecursively\n data={data}\n items={item.items}\n render={Render}\n getItemKey={getItemKey}\n parents={[...parents, item]}\n />\n );\n }\n\n return (\n <Render\n key={getItemKey({ item, depth, index })}\n // typecast since it should be undefined in renderer as well?\n data={data}\n item={item}\n parents={parents}\n >\n {children}\n </Render>\n );\n })}\n </>\n );\n}\n"],"names":["getRecursiveItemKey","options","depth","index","RenderRecursively","props","data","items","render","Render","getItemKey","parents","map","item","children","length"],"mappings":"AAAA,oDAAoD;;AAmBpD;;;;;;;;CAQC,GACD,OAAO,SAASA,oBACdC,OAAsC;IAEtC,MAAM,EAAEC,KAAK,EAAEC,KAAK,EAAE,GAAGF;IAEzB,OAAO,GAAGC,MAAM,CAAC,EAAEC,OAAO;AAC5B;AAoEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyFC,GACD,OAAO,SAASC,kBACdC,KAAyC;IAEzC,MAAM,EACJC,IAAI,EACJC,KAAK,EACLC,QAAQC,MAAM,EACdC,aAAaV,mBAAmB,EAChCW,UAAU,EAAE,EACb,GAAGN;IAEJ,qBACE;kBACGE,MAAMK,GAAG,CAAC,CAACC,MAAMV;YAChB,IAAIW;YACJ,MAAMZ,QAAQS,QAAQI,MAAM;YAC5B,IAAIF,KAAKN,KAAK,EAAEQ,QAAQ;gBACtBD,yBACE,KAACV;oBACCE,MAAMA;oBACNC,OAAOM,KAAKN,KAAK;oBACjBC,QAAQC;oBACRC,YAAYA;oBACZC,SAAS;2BAAIA;wBAASE;qBAAK;;YAGjC;YAEA,qBACE,KAACJ;gBAEC,6DAA6D;gBAC7DH,MAAMA;gBACNO,MAAMA;gBACNF,SAASA;0BAERG;eANIJ,WAAW;gBAAEG;gBAAMX;gBAAOC;YAAM;QAS3C;;AAGN"}
1
+ {"version":3,"sources":["../../src/utils/RenderRecursively.tsx"],"sourcesContent":["// TODO: Figure out how to strictly enforce the data\nimport { type ComponentType, type ReactElement, type ReactNode } from \"react\";\n\n/**\n * @since 6.0.0\n */\nexport type RecursiveItem<T = Record<string, unknown>> = T & {\n items?: readonly RecursiveItem<T>[];\n};\n\n/**\n * @since 6.0.0\n */\nexport interface RecursiveItemKeyOptions<Item = Record<string, unknown>> {\n item: RecursiveItem<Item>;\n depth: number;\n index: number;\n}\n\n/**\n * This is the default implementation for {@link RenderRecursivelyProps.getItemKey}.\n *\n * ```ts\n * return `${options.depth}-${options.index}`\n * ```\n *\n * @since 6.0.0\n */\nexport function getRecursiveItemKey<Item = Record<string, unknown>>(\n options: RecursiveItemKeyOptions<Item>\n): string {\n const { depth, index } = options;\n\n return `${depth}-${index}`;\n}\n\n/**\n * @since 6.0.0\n */\nexport interface RenderRecursiveItemsProps<\n Item = Record<string, unknown>,\n Data = unknown,\n> {\n data?: Data;\n\n /**\n * The current item to render.\n */\n item: RecursiveItem<Item>;\n\n /**\n * The list of parent items which can be used to determine the depth or \"share\n * props\" if the items contained props.\n */\n parents: readonly RecursiveItem<Item>[];\n\n /**\n * This will be provided if the {@link item} had child items and will be the\n * rendered content.\n */\n children?: ReactNode;\n}\n\n/**\n * @since 6.0.0\n */\nexport interface RenderRecursivelyProps<\n Item = Record<string, unknown>,\n Data = unknown,\n> {\n data?: Data;\n items: readonly RecursiveItem<Item>[];\n\n /**\n * The renderer for each item.\n */\n render: ComponentType<RenderRecursiveItemsProps<Item, Data>>;\n\n /**\n * This should not be used for external users. This is used to build the\n * {@link RenderRecursiveItemsProps.parents} list.\n *\n * @internal\n * @defaultValue `[]`\n */\n parents?: readonly RecursiveItem<Item>[];\n\n /**\n * Gets a React `key` for a specific item. This should be provided if the\n * items can be moved around to improve performance.\n *\n * @example\n * ```ts\n * getItemKey={({ item }) => item.id}\n * ```\n *\n * @see {@link getRecursiveItemKey}\n * @defaultValue `` ({ index, depth }) => `${depth}-${index}` ``\n */\n getItemKey?: (options: RecursiveItemKeyOptions<Item>) => string;\n}\n\n/**\n * Helper component for recursively rendering specific data structures (mostly\n * trees). The main use-case is for rendering site navigation and the `Tree`\n * component.\n *\n * @example\n * ```tsx\n * import {\n * buildTree,\n * List,\n * ListItem,\n * ListItemLink,\n * RenderRecursively,\n * TreeData,\n * type DefaultTreeItemNode,\n * type RenderRecursiveItemsProps,\n * } from \"@react-md/core\";\n * import { type ReactElement } from \"react\";\n *\n * const navItems = {\n * \"/\": {\n * href: \"/\",\n * itemId: \"/\",\n * parentId: null,\n * children: \"Home\",\n * },\n * \"/route-1\": {\n * itemId: \"/route-1\",\n * parentId: null,\n * children: \"Collapsible\",\n * },\n * \"/nested-route-1\": {\n * itemId: \"/nested-route-1\",\n * parentId: \"/route-1\",\n * children: \"Child 1\",\n * },\n * \"/nested-route-2\": {\n * itemId: \"/nested-route-2\",\n * parentId: \"/route-2\",\n * children: \"Child 2\",\n * },\n * \"/divider-1\": {\n * itemId: \"/divider-1\",\n * parentId: null\n * },\n * \"/route-2\": {\n * itemId: \"/route-2\"\n * parentId: null,\n * children: \"Route 2\",\n * },\n * } satisfies TreeData;\n *\n * function NestedNavigation(props: RenderRecursiveItemsProps<DefaultTreeItemNode>): ReactElement {\n * const { item, parents, children } = props;\n * const { toggle, toggled: collapsed } = useToggle(false);\n * const { elementProps } = useCollapseTransition({\n * transitionIn: !collapsed,\n * });\n *\n * return (\n * <li>\n * <Button onClick={toggle}>{item.children}</Button>\n * <List {...elementProps}>{children}</List>\n * </li>\n * ):\n * }\n *\n *\n * function Render(props: RenderRecursiveItemsProps<DefaultTreeItemNode>): ReactElement {\n * const { item, parents } = props;\n * if (item.itemId.includes(\"divider\")) {\n * return <Divider />;\n * }\n *\n * if (item.items) {\n * return <NestedNavigation {...props} />\n * }\n *\n * const prefix = parents.map((parent) => parent.itemId).join(\"/\");\n * return (\n * <Link href={`${prefix}${item.itemId}`}>\n * {item.children}\n * </Link>\n * );\n * }\n * ```\n *\n *\n * @since 6.0.0\n */\nexport function RenderRecursively<Item, Data>(\n props: RenderRecursivelyProps<Item, Data>\n): ReactElement {\n const {\n data,\n items,\n render: Render,\n getItemKey = getRecursiveItemKey,\n parents = [],\n } = props;\n\n return (\n <>\n {items.map((item, index) => {\n let children: ReactNode;\n const depth = parents.length;\n if (item.items?.length) {\n children = (\n <RenderRecursively\n data={data}\n items={item.items}\n render={Render}\n getItemKey={getItemKey}\n parents={[...parents, item]}\n />\n );\n }\n\n return (\n <Render\n key={getItemKey({ item, depth, index })}\n // typecast since it should be undefined in renderer as well?\n data={data}\n item={item}\n parents={parents}\n >\n {children}\n </Render>\n );\n })}\n </>\n );\n}\n"],"names":["getRecursiveItemKey","options","depth","index","RenderRecursively","props","data","items","render","Render","getItemKey","parents","map","item","children","length"],"mappings":"AAAA,oDAAoD;;AAmBpD;;;;;;;;CAQC,GACD,OAAO,SAASA,oBACdC,OAAsC;IAEtC,MAAM,EAAEC,KAAK,EAAEC,KAAK,EAAE,GAAGF;IAEzB,OAAO,GAAGC,MAAM,CAAC,EAAEC,OAAO;AAC5B;AAoEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyFC,GACD,OAAO,SAASC,kBACdC,KAAyC;IAEzC,MAAM,EACJC,IAAI,EACJC,KAAK,EACLC,QAAQC,MAAM,EACdC,aAAaV,mBAAmB,EAChCW,UAAU,EAAE,EACb,GAAGN;IAEJ,qBACE;kBACGE,MAAMK,GAAG,CAAC,CAACC,MAAMV;YAChB,IAAIW;YACJ,MAAMZ,QAAQS,QAAQI,MAAM;YAC5B,IAAIF,KAAKN,KAAK,EAAEQ,QAAQ;gBACtBD,yBACE,KAACV;oBACCE,MAAMA;oBACNC,OAAOM,KAAKN,KAAK;oBACjBC,QAAQC;oBACRC,YAAYA;oBACZC,SAAS;2BAAIA;wBAASE;qBAAK;;YAGjC;YAEA,qBACE,KAACJ;gBAEC,6DAA6D;gBAC7DH,MAAMA;gBACNO,MAAMA;gBACNF,SAASA;0BAERG;eANIJ,WAAW;gBAAEG;gBAAMX;gBAAOC;YAAM;QAS3C;;AAGN"}
@@ -19,9 +19,9 @@ export interface AlphaNumericSortOptions<T> {
19
19
  *
20
20
  * const items: Item[] = [{ name: 'Hello' }, { name: 'World' }];
21
21
  *
22
- * `alphaNumericSort(items, {
23
- * extractor: item => item.name,
24
- * })`
22
+ * alphaNumericSort(items, {
23
+ * extractor: (item) => item.name,
24
+ * });
25
25
  * ```
26
26
  *
27
27
  * For javascript developers, this will throw an error in dev mode if an
@@ -83,7 +83,7 @@ export declare function alphaNumericSort<T extends string>(list: readonly T[], o
83
83
  * const items: Item[] = [{ name: "World" }, { name: "Hello" }];
84
84
  *
85
85
  * const sorted = alphaNumericSort(items, {
86
- * extractor: item => item.name,
86
+ * extractor: (item) => item.name,
87
87
  * });
88
88
  * // sorted == [{ name: "Hello" }, { name: "World" }]
89
89
  * ```
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/alphaNumericSort.ts"],"sourcesContent":["import { defaultExtractor } from \"../searching/utils.js\";\nimport { type TextExtractor } from \"../types.js\";\n\n/**\n * The default `Intl.Collator` that should be used for sorting large lists.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare#performance\n * @since 6.0.0\n */\nexport const DEFAULT_COLLATOR = new Intl.Collator(\"en-US\", {\n numeric: true,\n caseFirst: \"upper\",\n});\n\n/** @since 6.0.0 */\nexport interface AlphaNumericSortOptions<T> {\n /**\n * The extractor is only required when the list of items are not strings.\n *\n * @example Simple Example\n * ```ts\n * interface Item {\n * name: string;\n * }\n *\n * const items: Item[] = [{ name: 'Hello' }, { name: 'World' }];\n *\n * `alphaNumericSort(items, {\n * extractor: item => item.name,\n * })`\n * ```\n *\n * For javascript developers, this will throw an error in dev mode if an\n * extractor is not provided for non-string lists.\n *\n * @defaultValue `typeof item === \"string\" ? item : \"\"`\n */\n extractor?: TextExtractor<T>;\n\n /**\n * A custom compare function for sorting the list. This should really only be\n * provided if the language for your app is not `\"en-US\"` or you'd like to\n * provide some custom sorting options.\n *\n * @example Custom Compare using Intl.Collator\n * ```ts\n * const collator = new Intl.Collator(\"en-US\", {\n * numeric: false,\n * caseFirst: \"lower\",\n * usage: \"search\",\n * });\n *\n * alphaNumericSort(items, {\n * compare: collator.compare,\n * })\n * ```\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/Collator\n * @defaultValue `new Intl.Collator(\"en-US\", { numeric: true, caseFirst: \"upper\" }).compare`\n */\n compare?: (a: string, b: string) => number;\n\n /**\n * Setting this to `true` will return the list in descending order instead of\n * ascending.\n *\n * @defaultValue `false`\n */\n descending?: boolean;\n}\n\n/**\n * @example Simple Example\n * ```ts\n * const items = [\"World\", \"Hello\"];\n *\n * const sorted = alphaNumericSort(items);\n * // sorted == [\"Hello\", \"World\"]\n * ```\n *\n * @param list - The list of strings to sort\n * @returns a new sorted list\n */\nexport function alphaNumericSort<T extends string>(\n list: readonly T[],\n options?: Omit<AlphaNumericSortOptions<T>, \"extractor\">\n): readonly T[];\n/**\n * @example Simple Example\n * ```ts\n * interface Item {\n * name: string;\n * }\n *\n * const items: Item[] = [{ name: \"World\" }, { name: \"Hello\" }];\n *\n * const sorted = alphaNumericSort(items, {\n * extractor: item => item.name,\n * });\n * // sorted == [{ name: \"Hello\" }, { name: \"World\" }]\n * ```\n *\n * @param list - The list of items to sort\n * @returns a new sorted list\n */\nexport function alphaNumericSort<T>(\n list: readonly T[],\n options: AlphaNumericSortOptions<T> & { extractor: TextExtractor<T> }\n): readonly T[];\nexport function alphaNumericSort<T>(\n list: readonly T[],\n options: AlphaNumericSortOptions<T> = {}\n): readonly T[] {\n const {\n compare = DEFAULT_COLLATOR.compare,\n extractor = defaultExtractor(\"alphaNumericSort\"),\n descending = false,\n } = options;\n\n const sorted = list.slice();\n sorted.sort((a, b) => {\n const aValue = extractor(a);\n const bValue = extractor(b);\n\n const value1 = descending ? bValue : aValue;\n const value2 = descending ? aValue : bValue;\n\n return compare(value1, value2);\n });\n\n return sorted;\n}\n"],"names":["defaultExtractor","DEFAULT_COLLATOR","Intl","Collator","numeric","caseFirst","alphaNumericSort","list","options","compare","extractor","descending","sorted","slice","sort","a","b","aValue","bValue","value1","value2"],"mappings":"AAAA,SAASA,gBAAgB,QAAQ,wBAAwB;AAGzD;;;;;CAKC,GACD,OAAO,MAAMC,mBAAmB,IAAIC,KAAKC,QAAQ,CAAC,SAAS;IACzDC,SAAS;IACTC,WAAW;AACb,GAAG;AAiGH,OAAO,SAASC,iBACdC,IAAkB,EAClBC,UAAsC,CAAC,CAAC;IAExC,MAAM,EACJC,UAAUR,iBAAiBQ,OAAO,EAClCC,YAAYV,iBAAiB,mBAAmB,EAChDW,aAAa,KAAK,EACnB,GAAGH;IAEJ,MAAMI,SAASL,KAAKM,KAAK;IACzBD,OAAOE,IAAI,CAAC,CAACC,GAAGC;QACd,MAAMC,SAASP,UAAUK;QACzB,MAAMG,SAASR,UAAUM;QAEzB,MAAMG,SAASR,aAAaO,SAASD;QACrC,MAAMG,SAAST,aAAaM,SAASC;QAErC,OAAOT,QAAQU,QAAQC;IACzB;IAEA,OAAOR;AACT"}
1
+ {"version":3,"sources":["../../src/utils/alphaNumericSort.ts"],"sourcesContent":["import { defaultExtractor } from \"../searching/utils.js\";\nimport { type TextExtractor } from \"../types.js\";\n\n/**\n * The default `Intl.Collator` that should be used for sorting large lists.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare#performance\n * @since 6.0.0\n */\nexport const DEFAULT_COLLATOR = new Intl.Collator(\"en-US\", {\n numeric: true,\n caseFirst: \"upper\",\n});\n\n/** @since 6.0.0 */\nexport interface AlphaNumericSortOptions<T> {\n /**\n * The extractor is only required when the list of items are not strings.\n *\n * @example Simple Example\n * ```ts\n * interface Item {\n * name: string;\n * }\n *\n * const items: Item[] = [{ name: 'Hello' }, { name: 'World' }];\n *\n * alphaNumericSort(items, {\n * extractor: (item) => item.name,\n * });\n * ```\n *\n * For javascript developers, this will throw an error in dev mode if an\n * extractor is not provided for non-string lists.\n *\n * @defaultValue `typeof item === \"string\" ? item : \"\"`\n */\n extractor?: TextExtractor<T>;\n\n /**\n * A custom compare function for sorting the list. This should really only be\n * provided if the language for your app is not `\"en-US\"` or you'd like to\n * provide some custom sorting options.\n *\n * @example Custom Compare using Intl.Collator\n * ```ts\n * const collator = new Intl.Collator(\"en-US\", {\n * numeric: false,\n * caseFirst: \"lower\",\n * usage: \"search\",\n * });\n *\n * alphaNumericSort(items, {\n * compare: collator.compare,\n * })\n * ```\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/Collator\n * @defaultValue `new Intl.Collator(\"en-US\", { numeric: true, caseFirst: \"upper\" }).compare`\n */\n compare?: (a: string, b: string) => number;\n\n /**\n * Setting this to `true` will return the list in descending order instead of\n * ascending.\n *\n * @defaultValue `false`\n */\n descending?: boolean;\n}\n\n/**\n * @example Simple Example\n * ```ts\n * const items = [\"World\", \"Hello\"];\n *\n * const sorted = alphaNumericSort(items);\n * // sorted == [\"Hello\", \"World\"]\n * ```\n *\n * @param list - The list of strings to sort\n * @returns a new sorted list\n */\nexport function alphaNumericSort<T extends string>(\n list: readonly T[],\n options?: Omit<AlphaNumericSortOptions<T>, \"extractor\">\n): readonly T[];\n/**\n * @example Simple Example\n * ```ts\n * interface Item {\n * name: string;\n * }\n *\n * const items: Item[] = [{ name: \"World\" }, { name: \"Hello\" }];\n *\n * const sorted = alphaNumericSort(items, {\n * extractor: (item) => item.name,\n * });\n * // sorted == [{ name: \"Hello\" }, { name: \"World\" }]\n * ```\n *\n * @param list - The list of items to sort\n * @returns a new sorted list\n */\nexport function alphaNumericSort<T>(\n list: readonly T[],\n options: AlphaNumericSortOptions<T> & { extractor: TextExtractor<T> }\n): readonly T[];\nexport function alphaNumericSort<T>(\n list: readonly T[],\n options: AlphaNumericSortOptions<T> = {}\n): readonly T[] {\n const {\n compare = DEFAULT_COLLATOR.compare,\n extractor = defaultExtractor(\"alphaNumericSort\"),\n descending = false,\n } = options;\n\n const sorted = list.slice();\n sorted.sort((a, b) => {\n const aValue = extractor(a);\n const bValue = extractor(b);\n\n const value1 = descending ? bValue : aValue;\n const value2 = descending ? aValue : bValue;\n\n return compare(value1, value2);\n });\n\n return sorted;\n}\n"],"names":["defaultExtractor","DEFAULT_COLLATOR","Intl","Collator","numeric","caseFirst","alphaNumericSort","list","options","compare","extractor","descending","sorted","slice","sort","a","b","aValue","bValue","value1","value2"],"mappings":"AAAA,SAASA,gBAAgB,QAAQ,wBAAwB;AAGzD;;;;;CAKC,GACD,OAAO,MAAMC,mBAAmB,IAAIC,KAAKC,QAAQ,CAAC,SAAS;IACzDC,SAAS;IACTC,WAAW;AACb,GAAG;AAiGH,OAAO,SAASC,iBACdC,IAAkB,EAClBC,UAAsC,CAAC,CAAC;IAExC,MAAM,EACJC,UAAUR,iBAAiBQ,OAAO,EAClCC,YAAYV,iBAAiB,mBAAmB,EAChDW,aAAa,KAAK,EACnB,GAAGH;IAEJ,MAAMI,SAASL,KAAKM,KAAK;IACzBD,OAAOE,IAAI,CAAC,CAACC,GAAGC;QACd,MAAMC,SAASP,UAAUK;QACzB,MAAMG,SAASR,UAAUM;QAEzB,MAAMG,SAASR,aAAaO,SAASD;QACrC,MAAMG,SAAST,aAAaM,SAASC;QAErC,OAAOT,QAAQU,QAAQC;IACzB;IAEA,OAAOR;AACT"}
@@ -6,7 +6,7 @@ export type BEMResult = (elementOrModifier?: Element | Modifier, modifier?: Modi
6
6
  * Applies the BEM styled class name to an element.
7
7
  *
8
8
  * @example Simple Example
9
- * ```jsx
9
+ * ```tsx
10
10
  * import { bem } from "@react-md/core":
11
11
  *
12
12
  * const styles = bem("my-component"):
package/dist/utils/bem.js CHANGED
@@ -14,7 +14,7 @@ function modify(base, modifier) {
14
14
  * Applies the BEM styled class name to an element.
15
15
  *
16
16
  * @example Simple Example
17
- * ```jsx
17
+ * ```tsx
18
18
  * import { bem } from "@react-md/core":
19
19
  *
20
20
  * const styles = bem("my-component"):
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/bem.ts"],"sourcesContent":["export type Block = string;\nexport type Element = string;\nexport type Modifier = Record<string, unknown>;\n\nfunction modify(base: string, modifier?: Modifier): string {\n if (!modifier) {\n return base;\n }\n\n const hasOwn = Object.prototype.hasOwnProperty;\n return Object.keys(modifier).reduce((s, mod) => {\n if (hasOwn.call(modifier, mod) && modifier[mod]) {\n s = `${s} ${base}--${mod}`;\n }\n\n return s;\n }, base);\n}\n\nexport type BEMResult = (\n elementOrModifier?: Element | Modifier,\n modifier?: Modifier\n) => string;\n\n/**\n * Applies the BEM styled class name to an element.\n *\n * @example Simple Example\n * ```jsx\n * import { bem } from \"@react-md/core\":\n *\n * const styles = bem(\"my-component\"):\n *\n * export function MyComponent(props) {\n * const className = styles({\n * always: true,\n * never: false,\n * \"some-condition\": props.something,\n * }):\n * const childClassName = styles('child', {\n * always: true,\n * never: false,\n * \"some-condition\": props.something,\n * });\n *\n * // With a false-like `props.something`\n * // className === \"my-component__child my-component__child--always\"\n * // childClassName === \"my-component my-component--always\"\n * // With a truthy `props.something`\n * // className === \"my-component my-component--always my-component--some-condition\"\n * // childClassName === \"my-component__child my-component__child--always my-component__child--some-condition\"\n *\n * return (\n * <div className={className}>\n * <div className={childClassName} />\n * </div>\n * ):\n * }\n * ```\n *\n * @see https://en.bem.info/methodology/css/\n * @param base - The base class to use\n * @returns a function to call that generates the full class name\n */\nexport function bem(base: Block): BEMResult {\n if (process.env.NODE_ENV !== \"production\") {\n if (!base) {\n throw new Error(\n \"bem requires a base block class but none were provided.\"\n );\n }\n }\n\n /**\n * Creates the full class name from the base block name. This can be called\n * without any arguments which will just return the base block name (kind of\n * worthless), or you can provide a child element name and modifiers.\n *\n * @param elementOrModifier - This is either the child element name or an\n * object of modifiers to apply. This **must** be a string if the second\n * argument is provided.\n * @param modifier - Any optional modifiers to apply to the block and optional\n * element.\n * @returns the full class name\n */\n return function block(\n elementOrModifier?: Element | Modifier,\n modifier?: Modifier\n ): string {\n if (process.env.NODE_ENV !== \"production\") {\n if (typeof elementOrModifier !== \"string\" && modifier) {\n throw new TypeError(\n \"bem does not support having two modifier arguments.\"\n );\n }\n }\n\n if (!elementOrModifier) {\n return base;\n }\n\n if (typeof elementOrModifier !== \"string\") {\n return modify(base, elementOrModifier);\n }\n\n return modify(`${base}__${elementOrModifier}`, modifier);\n };\n}\n"],"names":["modify","base","modifier","hasOwn","Object","prototype","hasOwnProperty","keys","reduce","s","mod","call","bem","process","env","NODE_ENV","Error","block","elementOrModifier","TypeError"],"mappings":"AAIA,SAASA,OAAOC,IAAY,EAAEC,QAAmB;IAC/C,IAAI,CAACA,UAAU;QACb,OAAOD;IACT;IAEA,MAAME,SAASC,OAAOC,SAAS,CAACC,cAAc;IAC9C,OAAOF,OAAOG,IAAI,CAACL,UAAUM,MAAM,CAAC,CAACC,GAAGC;QACtC,IAAIP,OAAOQ,IAAI,CAACT,UAAUQ,QAAQR,QAAQ,CAACQ,IAAI,EAAE;YAC/CD,IAAI,GAAGA,EAAE,CAAC,EAAER,KAAK,EAAE,EAAES,KAAK;QAC5B;QAEA,OAAOD;IACT,GAAGR;AACL;AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCC,GACD,OAAO,SAASW,IAAIX,IAAW;IAC7B,IAAIY,QAAQC,GAAG,CAACC,QAAQ,KAAK,cAAc;QACzC,IAAI,CAACd,MAAM;YACT,MAAM,IAAIe,MACR;QAEJ;IACF;IAEA;;;;;;;;;;;GAWC,GACD,OAAO,SAASC,MACdC,iBAAsC,EACtChB,QAAmB;QAEnB,IAAIW,QAAQC,GAAG,CAACC,QAAQ,KAAK,cAAc;YACzC,IAAI,OAAOG,sBAAsB,YAAYhB,UAAU;gBACrD,MAAM,IAAIiB,UACR;YAEJ;QACF;QAEA,IAAI,CAACD,mBAAmB;YACtB,OAAOjB;QACT;QAEA,IAAI,OAAOiB,sBAAsB,UAAU;YACzC,OAAOlB,OAAOC,MAAMiB;QACtB;QAEA,OAAOlB,OAAO,GAAGC,KAAK,EAAE,EAAEiB,mBAAmB,EAAEhB;IACjD;AACF"}
1
+ {"version":3,"sources":["../../src/utils/bem.ts"],"sourcesContent":["export type Block = string;\nexport type Element = string;\nexport type Modifier = Record<string, unknown>;\n\nfunction modify(base: string, modifier?: Modifier): string {\n if (!modifier) {\n return base;\n }\n\n const hasOwn = Object.prototype.hasOwnProperty;\n return Object.keys(modifier).reduce((s, mod) => {\n if (hasOwn.call(modifier, mod) && modifier[mod]) {\n s = `${s} ${base}--${mod}`;\n }\n\n return s;\n }, base);\n}\n\nexport type BEMResult = (\n elementOrModifier?: Element | Modifier,\n modifier?: Modifier\n) => string;\n\n/**\n * Applies the BEM styled class name to an element.\n *\n * @example Simple Example\n * ```tsx\n * import { bem } from \"@react-md/core\":\n *\n * const styles = bem(\"my-component\"):\n *\n * export function MyComponent(props) {\n * const className = styles({\n * always: true,\n * never: false,\n * \"some-condition\": props.something,\n * }):\n * const childClassName = styles('child', {\n * always: true,\n * never: false,\n * \"some-condition\": props.something,\n * });\n *\n * // With a false-like `props.something`\n * // className === \"my-component__child my-component__child--always\"\n * // childClassName === \"my-component my-component--always\"\n * // With a truthy `props.something`\n * // className === \"my-component my-component--always my-component--some-condition\"\n * // childClassName === \"my-component__child my-component__child--always my-component__child--some-condition\"\n *\n * return (\n * <div className={className}>\n * <div className={childClassName} />\n * </div>\n * ):\n * }\n * ```\n *\n * @see https://en.bem.info/methodology/css/\n * @param base - The base class to use\n * @returns a function to call that generates the full class name\n */\nexport function bem(base: Block): BEMResult {\n if (process.env.NODE_ENV !== \"production\") {\n if (!base) {\n throw new Error(\n \"bem requires a base block class but none were provided.\"\n );\n }\n }\n\n /**\n * Creates the full class name from the base block name. This can be called\n * without any arguments which will just return the base block name (kind of\n * worthless), or you can provide a child element name and modifiers.\n *\n * @param elementOrModifier - This is either the child element name or an\n * object of modifiers to apply. This **must** be a string if the second\n * argument is provided.\n * @param modifier - Any optional modifiers to apply to the block and optional\n * element.\n * @returns the full class name\n */\n return function block(\n elementOrModifier?: Element | Modifier,\n modifier?: Modifier\n ): string {\n if (process.env.NODE_ENV !== \"production\") {\n if (typeof elementOrModifier !== \"string\" && modifier) {\n throw new TypeError(\n \"bem does not support having two modifier arguments.\"\n );\n }\n }\n\n if (!elementOrModifier) {\n return base;\n }\n\n if (typeof elementOrModifier !== \"string\") {\n return modify(base, elementOrModifier);\n }\n\n return modify(`${base}__${elementOrModifier}`, modifier);\n };\n}\n"],"names":["modify","base","modifier","hasOwn","Object","prototype","hasOwnProperty","keys","reduce","s","mod","call","bem","process","env","NODE_ENV","Error","block","elementOrModifier","TypeError"],"mappings":"AAIA,SAASA,OAAOC,IAAY,EAAEC,QAAmB;IAC/C,IAAI,CAACA,UAAU;QACb,OAAOD;IACT;IAEA,MAAME,SAASC,OAAOC,SAAS,CAACC,cAAc;IAC9C,OAAOF,OAAOG,IAAI,CAACL,UAAUM,MAAM,CAAC,CAACC,GAAGC;QACtC,IAAIP,OAAOQ,IAAI,CAACT,UAAUQ,QAAQR,QAAQ,CAACQ,IAAI,EAAE;YAC/CD,IAAI,GAAGA,EAAE,CAAC,EAAER,KAAK,EAAE,EAAES,KAAK;QAC5B;QAEA,OAAOD;IACT,GAAGR;AACL;AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCC,GACD,OAAO,SAASW,IAAIX,IAAW;IAC7B,IAAIY,QAAQC,GAAG,CAACC,QAAQ,KAAK,cAAc;QACzC,IAAI,CAACd,MAAM;YACT,MAAM,IAAIe,MACR;QAEJ;IACF;IAEA;;;;;;;;;;;GAWC,GACD,OAAO,SAASC,MACdC,iBAAsC,EACtChB,QAAmB;QAEnB,IAAIW,QAAQC,GAAG,CAACC,QAAQ,KAAK,cAAc;YACzC,IAAI,OAAOG,sBAAsB,YAAYhB,UAAU;gBACrD,MAAM,IAAIiB,UACR;YAEJ;QACF;QAEA,IAAI,CAACD,mBAAmB;YACtB,OAAOjB;QACT;QAEA,IAAI,OAAOiB,sBAAsB,UAAU;YACzC,OAAOlB,OAAOC,MAAMiB;QACtB;QAEA,OAAOlB,OAAO,GAAGC,KAAK,EAAE,EAAEiB,mBAAmB,EAAEhB;IACjD;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-md/core",
3
- "version": "1.0.0-next.17",
3
+ "version": "1.0.0-next.18",
4
4
  "description": "The core components and functionality for react-md.",
5
5
  "type": "module",
6
6
  "sass": "./dist/_core.scss",
@@ -53,26 +53,26 @@
53
53
  "license": "MIT",
54
54
  "dependencies": {
55
55
  "cnbuilder": "^3.1.0",
56
- "nanoid": "^5.0.8"
56
+ "nanoid": "^5.0.9"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@jest/globals": "^29.7.0",
60
60
  "@jest/types": "^29.6.3",
61
- "@microsoft/api-extractor": "^7.47.11",
62
- "@mlaursen/eslint-config": "^5.2.0",
63
- "@swc/cli": "^0.5.0",
64
- "@swc/core": "^1.9.1",
61
+ "@microsoft/api-extractor": "^7.48.1",
62
+ "@mlaursen/eslint-config": "^6.0.0",
63
+ "@swc/cli": "^0.5.2",
64
+ "@swc/core": "^1.10.1",
65
65
  "@swc/jest": "^0.2.37",
66
66
  "@testing-library/dom": "^10.4.0",
67
67
  "@testing-library/jest-dom": "^6.6.3",
68
- "@testing-library/react": "^16.0.1",
68
+ "@testing-library/react": "^16.1.0",
69
69
  "@testing-library/user-event": "^14.5.2",
70
70
  "@types/lodash": "^4.17.13",
71
- "@types/node": "^22.9.0",
71
+ "@types/node": "^22.10.2",
72
72
  "@types/react": "^18.3.12",
73
73
  "@types/react-dom": "^18.3.1",
74
- "chokidar": "^4.0.1",
75
- "eslint": "^9.14.0",
74
+ "chokidar": "^4.0.3",
75
+ "eslint": "^9.17.0",
76
76
  "filesize": "^10.1.6",
77
77
  "glob": "11.0.0",
78
78
  "jest": "^29.7.0",
@@ -81,18 +81,18 @@
81
81
  "lodash": "^4.17.21",
82
82
  "lz-string": "^1.5.0",
83
83
  "npm-run-all": "^4.1.5",
84
- "postcss": "^8.4.47",
84
+ "postcss": "^8.4.49",
85
85
  "postcss-scss": "^4.0.9",
86
- "prettier": "^3.3.3",
87
- "stylelint": "^16.10.0",
86
+ "prettier": "^3.4.2",
87
+ "stylelint": "^16.12.0",
88
88
  "stylelint-config-prettier-scss": "^1.0.0",
89
89
  "stylelint-config-recommended-scss": "^14.1.0",
90
90
  "stylelint-order": "^6.0.4",
91
- "stylelint-scss": "^6.8.1",
91
+ "stylelint-scss": "^6.10.0",
92
92
  "ts-morph": "^24.0.0",
93
93
  "ts-node": "^10.9.2",
94
94
  "tsx": "^4.19.2",
95
- "typescript": "^5.6.3"
95
+ "typescript": "^5.5.4"
96
96
  },
97
97
  "peerDependencies": {
98
98
  "@jest/globals": "^29.7.0",
@@ -135,7 +135,7 @@
135
135
  },
136
136
  "volta": {
137
137
  "node": "20.12.2",
138
- "pnpm": "9.12.1"
138
+ "pnpm": "9.14.1"
139
139
  },
140
140
  "scripts": {
141
141
  "run-script": "tsx --tsconfig scripts/tsconfig.json",
@@ -347,7 +347,7 @@ export interface ValidatedFilesResult<CustomError> {
347
347
  * };
348
348
  * ```
349
349
  *
350
- * @typeparam E - An optional custom file validation error.
350
+ * @typeParam E - An optional custom file validation error.
351
351
  * @param files - The list of files to check
352
352
  * @param options - The {@link FilesValidationOptions}
353
353
  * @returns the {@link ValidatedFilesResult}
@@ -364,7 +364,7 @@ export type FilesValidator<CustomError = never> = (
364
364
  * {@link useFileUpload} that ensures the {@link FilesValidationOptions} are
365
365
  * enforced before allowing a file to be uploaded.
366
366
  *
367
- * @typeparam E - An optional custom file validation error.
367
+ * @typeParam E - An optional custom file validation error.
368
368
  * @param files - The list of files to check
369
369
  * @param options - The {@link FilesValidationOptions}
370
370
  * @returns the {@link ValidatedFilesResult}
@@ -26,6 +26,7 @@ export function SelectedOption(props: SelectedOptionProps): ReactElement {
26
26
  disableAddon,
27
27
  option,
28
28
  className,
29
+ disableWrap = true,
29
30
  disablePadding = true,
30
31
  placeholder,
31
32
  ...remaining
@@ -44,6 +45,7 @@ export function SelectedOption(props: SelectedOptionProps): ReactElement {
44
45
  <Box
45
46
  {...remaining}
46
47
  className={cnb("rmd-selected-option", textField(), className)}
48
+ disableWrap={disableWrap}
47
49
  disablePadding={disablePadding}
48
50
  >
49
51
  {!disableAddon && option?.leftAddon}
@@ -18,9 +18,11 @@ export interface FontIconProps
18
18
 
19
19
  /**
20
20
  * Any children to render to create the font icon. This is required for
21
- * material-icons.
21
+ * material-icons. For example:
22
22
  *
23
- * @example `<FontIcon>clear</FontIcon>`
23
+ * ```tsx
24
+ * <FontIcon>clear</FontIcon>
25
+ * ```
24
26
  */
25
27
  children?: ReactNode;
26
28
  }
@@ -26,7 +26,7 @@ export interface TextIconSpacingProps {
26
26
  * If this is not a valid react element, the icon will be wrapped in a
27
27
  * `<span>` instead with the class names applied.
28
28
  */
29
- icon?: ReactElement | ReactNode;
29
+ icon?: ReactElement<{ className?: string }> | ReactNode;
30
30
 
31
31
  /**
32
32
  * Boolean if the icon should appear after the text instead of before.
@@ -217,14 +217,14 @@ export interface SetupResizeObserverMockOptions {
217
217
  *
218
218
  * @example Main Usage
219
219
  * ```tsx
220
- * import { useCallback, useState } from "react";
221
220
  * import {
222
221
  * cleanupResizeObserverAfterEach,
223
222
  * render,
224
223
  * screen,
225
224
  * setupResizeObserverMock,
226
225
  * } from "@react-md/core/test-utils";
227
- * import { useResizeObserver } from "@react-md/core";
226
+ * import { useResizeObserver } from "@react-md/core/useResizeObserver";
227
+ * import { useCallback, useState } from "react";
228
228
  *
229
229
  * function ExampleComponent() {
230
230
  * const [size, setSize] = useState({ height: 0, width: 0 });
@@ -234,7 +234,7 @@ export interface SetupResizeObserverMockOptions {
234
234
  * height: entry.contentRect.height,
235
235
  * width: entry.contentRect.width,
236
236
  * });
237
- * });
237
+ * }, []),
238
238
  * });
239
239
  *
240
240
  * return (
@@ -250,10 +250,10 @@ export interface SetupResizeObserverMockOptions {
250
250
  * describe("ExampleComponent", () => {
251
251
  * it("should do stuff", () => {
252
252
  * const observer = setupResizeObserverMock();
253
- * render(<ExampleComponent />)
253
+ * render(<ExampleComponent />);
254
254
  *
255
255
  * const size = screen.getByTestId("size");
256
- * const resizeTarget = screen.getByTestId("resize-target")
256
+ * const resizeTarget = screen.getByTestId("resize-target");
257
257
  *
258
258
  * // jsdom sets all element sizes to 0 by default
259
259
  * expect(size).toHaveTextContent(JSON.stringify({ height: 0, width: 0 }));
@@ -265,19 +265,18 @@ export interface SetupResizeObserverMockOptions {
265
265
  * expect(size).toHaveTextContent(JSON.stringify({ height: 100, width: 100 }));
266
266
  *
267
267
  * // or you can mock the `getBoundingClientRect` result
268
- * jest.spyOn(resizeTarget, "getBoundingClientRect")
269
- * .mockReturnValue({
270
- * ...document.body.getBoundingClientRect(),
271
- * height: 200,
272
- * width: 200,
273
- * }):
268
+ * jest.spyOn(resizeTarget, "getBoundingClientRect").mockReturnValue({
269
+ * ...document.body.getBoundingClientRect(),
270
+ * height: 200,
271
+ * width: 200,
272
+ * });
274
273
  *
275
274
  * act(() => {
276
275
  * observer.resizeElement(resizeTarget);
277
276
  * });
278
277
  * expect(size).toHaveTextContent(JSON.stringify({ height: 200, width: 200 }));
279
278
  * });
280
- * })
279
+ * });
281
280
  * ```
282
281
  *
283
282
  * @since 6.0.0
@@ -107,7 +107,7 @@ export type MatchMediaSpiedFunction = jest.SpiedFunction<
107
107
  /**
108
108
  * @example Default Behavior
109
109
  * ```tsx
110
- * import { matchPhone, render, spyOnMatchMedia } from "@react-md/test-utils";
110
+ * import { matchPhone, render, spyOnMatchMedia } from "@react-md/core/test-utils";
111
111
  *
112
112
  * const matchMedia = spyOnMatchMedia();
113
113
  * render(<Test />);
@@ -120,7 +120,7 @@ export type MatchMediaSpiedFunction = jest.SpiedFunction<
120
120
  *
121
121
  * @example Set Default Media
122
122
  * ```tsx
123
- * import { matchPhone, render, spyOnMatchMedia } from "@react-md/test-utils";
123
+ * import { matchPhone, render, spyOnMatchMedia } from "@react-md/core/test-utils";
124
124
  *
125
125
  * const matchMedia = spyOnMatchMedia(matchPhone);
126
126
  * render(<Test />);
@@ -194,6 +194,25 @@ export interface ResizeObserverHookOptions<E extends HTMLElement> {
194
194
  * For most cases you can use the {@link useElementSize} instead, but this hook
195
195
  * can be used for more complex behavior with the {@link ResizeObserverEntry}.
196
196
  *
197
+ * @example Simple Example
198
+ * ```tsx
199
+ * import { useResizeObserver } from "@react-md/core/useResizeObserver";
200
+ * import { useCallback, useState, type ReactElement } from "react";
201
+ *
202
+ * function Example(): ReactElement {
203
+ * const elementRef = useResizeObserver({
204
+ * onUpdate: useCallback((entry) => {
205
+ * const element = entry.target;
206
+ * const { height, width } = entry.contentRect;
207
+ * const { inlineSize, blockSize } = entry.borderBoxSize[0]O
208
+ * // do something
209
+ * }, []),
210
+ * });
211
+ *
212
+ * return <div ref={elementRef}>{...whatever...}</div>
213
+ * }
214
+ * ```
215
+ *
197
216
  * @since 2.3.0
198
217
  * @since 6.0.0 The API was updated to match the `useIntersectionObserver`
199
218
  * implementation -- accepts only a single object parameter and returns a
@@ -95,7 +95,7 @@ export interface RenderRecursivelyProps<
95
95
  * ```
96
96
  *
97
97
  * @see {@link getRecursiveItemKey}
98
- * @defaultValue `({ index, depth }) => ${depth}-${index}`.
98
+ * @defaultValue `` ({ index, depth }) => `${depth}-${index}` ``
99
99
  */
100
100
  getItemKey?: (options: RecursiveItemKeyOptions<Item>) => string;
101
101
  }
@@ -25,9 +25,9 @@ export interface AlphaNumericSortOptions<T> {
25
25
  *
26
26
  * const items: Item[] = [{ name: 'Hello' }, { name: 'World' }];
27
27
  *
28
- * `alphaNumericSort(items, {
29
- * extractor: item => item.name,
30
- * })`
28
+ * alphaNumericSort(items, {
29
+ * extractor: (item) => item.name,
30
+ * });
31
31
  * ```
32
32
  *
33
33
  * For javascript developers, this will throw an error in dev mode if an
@@ -95,7 +95,7 @@ export function alphaNumericSort<T extends string>(
95
95
  * const items: Item[] = [{ name: "World" }, { name: "Hello" }];
96
96
  *
97
97
  * const sorted = alphaNumericSort(items, {
98
- * extractor: item => item.name,
98
+ * extractor: (item) => item.name,
99
99
  * });
100
100
  * // sorted == [{ name: "Hello" }, { name: "World" }]
101
101
  * ```
package/src/utils/bem.ts CHANGED
@@ -26,7 +26,7 @@ export type BEMResult = (
26
26
  * Applies the BEM styled class name to an element.
27
27
  *
28
28
  * @example Simple Example
29
- * ```jsx
29
+ * ```tsx
30
30
  * import { bem } from "@react-md/core":
31
31
  *
32
32
  * const styles = bem("my-component"):