@mittwald/flow-react-components 1.6.0-next.45 → 1.6.0-next.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/component-index.json +1 -1
- package/dist/assets/doc-properties.json +1 -1
- package/dist/js/packages/components/src/components/List/List.mjs +27 -23
- package/dist/js/packages/components/src/components/List/List.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/components/Items/Items.mjs +24 -1
- package/dist/js/packages/components/src/components/List/components/Items/Items.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/components/ListItemView/ListItemView.mjs +46 -44
- package/dist/js/packages/components/src/components/List/components/ListItemView/ListItemView.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/components/ListItemView/components/ListItemViewContent/ListItemViewContent.mjs +25 -25
- package/dist/js/packages/components/src/components/List/components/ListItemView/components/ListItemViewContent/ListItemViewContent.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/hooks/useListSelector.mjs +18 -0
- package/dist/js/packages/components/src/components/List/hooks/useListSelector.mjs.map +1 -0
- package/dist/js/packages/components/src/components/List/listContext.mjs +32 -2
- package/dist/js/packages/components/src/components/List/listContext.mjs.map +1 -1
- package/dist/js/packages/components/src/components/PasswordCreationField/PasswordCreationField.mjs +2 -2
- package/dist/js/packages/components/src/components/PasswordCreationField/PasswordCreationField.mjs.map +1 -1
- package/dist/types/components/List/hooks/useListSelector.d.ts +6 -0
- package/dist/types/components/List/listContext.d.ts +16 -0
- package/package.json +10 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PasswordCreationField.mjs","names":["locales","styles","_Fragment"],"sources":["../../../../../../../src/components/PasswordCreationField/PasswordCreationField.tsx"],"sourcesContent":["import {\n type PropsWithChildren,\n useState,\n type ClipboardEvent,\n useCallback,\n useId,\n useMemo,\n useRef,\n} from \"react\";\nimport {\n dynamic,\n type PropsContext,\n PropsContextProvider,\n} from \"@/lib/propsContext\";\nimport {\n flowComponent,\n type FlowComponentProps,\n} from \"@/lib/componentFactory/flowComponent\";\nimport styles from \"./PasswordCreationField.module.scss\";\nimport * as Aria from \"react-aria-components\";\nimport clsx from \"clsx\";\nimport { type ActionFn } from \"@/components/Action\";\nimport getStateFromLatestPolicyValidationResult from \"@/components/PasswordCreationField/lib/getStateFromLatestPolicyValidationResult\";\nimport locales from \"./locales/*.locale.json\";\nimport generateValidationTranslation from \"@/components/PasswordCreationField/lib/generateValidationTranslation\";\nimport FieldDescription from \"@/components/FieldDescription\";\nimport ComplexityIndicator from \"@/components/PasswordCreationField/components/ComplexityIndicator/ComplexityIndicator\";\nimport { generatePassword } from \"@/components/PasswordCreationField/worker/generatePassword\";\nimport TogglePasswordVisibilityButton from \"@/components/PasswordCreationField/components/TogglePasswordVisibilityButton/TogglePasswordVisibilityButton\";\nimport { ValidationResultButton } from \"@/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton\";\nimport { PasswordGenerateButton } from \"@/components/PasswordCreationField/components/PasswordGenerateButton/PasswordGenerateButton\";\nimport type {\n PolicyValidationResult,\n PolicyGenericDeclaration,\n RuleValidationResult,\n} from \"@/integrations/@mittwald/password-tools-js\";\nimport {\n defaultPasswordCreationPolicy,\n Policy,\n} from \"@/integrations/@mittwald/password-tools-js\";\nimport { usePolicyValidationResult } from \"@/components/PasswordCreationField/lib/usePolicyValidationResult\";\nimport { joinIds, useFieldComponent } from \"@/lib/hooks/useFieldComponent\";\nimport { FieldError } from \"@/components/FieldError\";\nimport { useControlledHostValueProps } from \"@/lib/remote/useControlledHostValueProps\";\nimport { useLocalizedStringFormatter } from \"@/components/TranslationProvider/useLocalizedStringFormatter\";\nimport { UiComponentTunnelExit } from \"@/components/UiComponentTunnel/UiComponentTunnelExit\";\nimport {\n useAriaAnnouncePasswordVisibility,\n useAriaAnnounceValidationState,\n} from \"@/components/PasswordCreationField/lib/ariaAnnounce\";\n\nexport interface PasswordCreationFieldProps\n extends\n PropsWithChildren<\n Omit<Aria.TextFieldProps, \"children\" | \"value\" | \"defaultValue\"> &\n Partial<Pick<Aria.FieldErrorRenderProps, \"validationErrors\">>\n >,\n FlowComponentProps<HTMLInputElement> {\n /** The password of a controlled field. */\n value?: string;\n /** Called with the password and its validity whenever the password changes. */\n onValidationResult?: (result: { password: string; isValid: boolean }) => void;\n /** The initial password of an uncontrolled field. */\n defaultValue?: string;\n /** The placeholder shown while the field is empty. */\n placeholder?: string;\n /**\n * The policy the password is validated against.\n *\n * @default defaultPasswordCreationPolicy\n */\n validationPolicy?: PolicyGenericDeclaration;\n}\n\nexport interface ResolvedPolicyValidationResult extends Omit<\n PolicyValidationResult,\n \"isValid\"\n> {\n isValid: boolean | \"indeterminate\";\n ruleResults: RuleValidationResult[];\n}\n\n/** @flr-generate all */\nexport const PasswordCreationField = flowComponent(\n \"PasswordCreationField\",\n (props) => {\n const {\n children,\n className,\n ref,\n isDisabled,\n onValidationResult,\n isInvalid: invalidFromProps,\n validationPolicy:\n validationPolicyFromProps = defaultPasswordCreationPolicy,\n isRequired,\n value,\n onChange,\n ...rest\n } = useControlledHostValueProps(props, \"\");\n\n const translate = useLocalizedStringFormatter(\n locales,\n \"PasswordCreationField\",\n );\n\n const validationPolicy = useMemo(\n () => Policy.fromDeclaration(validationPolicyFromProps),\n [validationPolicyFromProps],\n );\n\n const [isPasswordRevealed, setIsPasswordRevealed] = useState(false);\n const initialPolicyValidationState: ResolvedPolicyValidationResult = {\n isValid: \"indeterminate\",\n complexity: {\n min: validationPolicy.minComplexity,\n actual: 4,\n warning: null,\n },\n ruleResults: [],\n };\n\n const [policyValidationResult, setPolicyValidationResult] = useState(\n initialPolicyValidationState,\n );\n\n /**\n * A validation that outlasts the delay shows the loading bar, but keeps the\n * previous result until the new one arrives – dropping it flickered the\n * error and the bar color after every typing pause.\n */\n const [isValidationPending, setIsValidationPending] = useState(false);\n const loadingRef = useRef<ReturnType<typeof setTimeout>>(null);\n const { rememberValidationResult } = usePolicyValidationResult(\n validationPolicy,\n value ?? \"\",\n () => {\n if (isEmptyValue) {\n return;\n }\n\n if (loadingRef.current) {\n clearTimeout(loadingRef.current);\n }\n loadingRef.current = setTimeout(() => {\n setIsValidationPending(true);\n }, 150);\n },\n ({ password, isValid, results }) => {\n if (loadingRef.current) {\n clearTimeout(loadingRef.current);\n }\n setIsValidationPending(false);\n\n if (isEmptyValue) {\n setPolicyValidationResult(() => ({\n ...results,\n isValid: true,\n }));\n return;\n }\n\n setPolicyValidationResult(() => results);\n onValidationResult?.({ password, isValid });\n },\n );\n\n const isEmptyValue = !value;\n const stateFromValidationResult = getStateFromLatestPolicyValidationResult(\n isEmptyValue,\n policyValidationResult,\n );\n let latestValidationErrorText = undefined;\n if (stateFromValidationResult) {\n const [translationKey, translationValues] = generateValidationTranslation(\n stateFromValidationResult,\n );\n latestValidationErrorText = translate.format(\n translationKey,\n translationValues,\n );\n }\n\n const isValidFromValidationResult =\n !isEmptyValue && stateFromValidationResult?.isValid;\n\n const isInvalidFromValidationResult =\n !isEmptyValue && stateFromValidationResult?.isValid === false;\n const isInvalid = invalidFromProps || isInvalidFromValidationResult;\n\n const {\n FieldErrorView,\n FieldErrorCaptureContext,\n wrapperProps,\n controlProps,\n fieldPropsContext,\n renderedFieldErrorId,\n skeletonProps,\n } = useFieldComponent(props, \"PasswordCreationField\");\n\n /**\n * The result for a valid password appears after the asynchronous policy\n * validation. react-aria resolves its description slot only when the\n * field's validity changes, so a result that appears while the field stays\n * valid (a generated password) was never linked. It gets its own id\n * instead, referenced only while it is rendered.\n *\n * A shown error replaces it: the FormField styles hide descriptions next to\n * an error, and a hidden description would still be announced.\n */\n const resultDescriptionId = useId();\n const [isResultDescriptionRendered, setIsResultDescriptionRendered] =\n useState(false);\n const resultDescriptionRef = useCallback((element: Element | null) => {\n setIsResultDescriptionRendered(!!element);\n }, []);\n const describedBy = joinIds(\n isResultDescriptionRendered && resultDescriptionId,\n controlProps[\"aria-describedby\"],\n );\n\n useAriaAnnounceValidationState(\n latestValidationErrorText,\n !isEmptyValue && policyValidationResult.isValid !== \"indeterminate\",\n );\n useAriaAnnouncePasswordVisibility(isPasswordRevealed);\n\n const setOptimisticPolicyValidationResult = (\n state: Partial<ResolvedPolicyValidationResult> = {},\n ) => {\n setPolicyValidationResult((currentState) => ({\n ...currentState,\n isValid: true,\n ...state,\n }));\n };\n\n const onPasswordGenerateHandler: ActionFn = async () => {\n const generatedPassword = await generatePassword(validationPolicy);\n /**\n * The generator returns only passwords the policy accepts, so the rating\n * is set with the password instead of after the typing debounce. It is\n * the policy's own result: a guessed one (full strength) was overturned\n * by the real rating, e.g. to \"meets the minimum requirements\". Cached,\n * so the debounced validation of this password does not rate it again.\n */\n const generatedPasswordResult =\n await validationPolicy.validate(generatedPassword);\n rememberValidationResult(generatedPassword, generatedPasswordResult);\n setPolicyValidationResult(generatedPasswordResult);\n setIsPasswordRevealed(true);\n onChange(generatedPassword);\n };\n\n const onPasswordPasteHandler = (event: ClipboardEvent) => {\n const pastedValue = event.clipboardData?.getData(\"text\");\n if (typeof pastedValue === \"string\" && pastedValue !== value) {\n setOptimisticPolicyValidationResult({\n isValid: \"indeterminate\",\n });\n }\n };\n\n const togglePasswordVisibilityHandler = () => {\n setIsPasswordRevealed((old) => !old);\n };\n\n const propsContext: PropsContext = {\n ...fieldPropsContext,\n Button: {\n tunnel: {\n id: \"button\",\n component: \"PasswordCreationField\",\n },\n size: \"m\",\n variant: \"plain\",\n color: \"secondary\",\n isDisabled: isDisabled,\n className: styles.button,\n },\n CopyButton: {\n tunnel: {\n id: \"button\",\n component: \"PasswordCreationField\",\n },\n size: \"m\",\n variant: \"plain\",\n color: \"secondary\",\n isDisabled: isDisabled,\n className: styles.button,\n text: value,\n },\n Label: {\n ...fieldPropsContext.Label,\n children: dynamic((localProps) => {\n return (\n <>\n {localProps.children}\n <PasswordGenerateButton\n isDisabled={isDisabled}\n onGeneratePasswordAction={onPasswordGenerateHandler}\n />\n <ValidationResultButton\n isEmptyValue={isEmptyValue}\n isDisabled={isDisabled}\n policyValidationResult={policyValidationResult}\n />\n </>\n );\n }),\n },\n };\n\n return (\n <Aria.TextField\n {...rest}\n {...skeletonProps}\n aria-describedby={describedBy}\n value={value}\n type={isPasswordRevealed ? \"text\" : \"password\"}\n onChange={onChange}\n onPaste={onPasswordPasteHandler}\n className={clsx(wrapperProps.className, className)}\n isDisabled={isDisabled}\n isInvalid={isInvalid}\n isRequired={isRequired}\n >\n <FieldErrorCaptureContext>\n {latestValidationErrorText && (\n <FieldError>{latestValidationErrorText}</FieldError>\n )}\n <PropsContextProvider\n props={propsContext}\n dependencies={[\n isDisabled,\n isRequired,\n value,\n policyValidationResult,\n isEmptyValue,\n ]}\n >\n {children}\n <Aria.Group\n isDisabled={isDisabled}\n className={clsx(styles.inputGroup)}\n >\n <Aria.Input ref={ref} className={styles.input} />\n <Aria.Group className={styles.buttonContainer}>\n <TogglePasswordVisibilityButton\n className={styles.button}\n isVisible={isPasswordRevealed}\n isDisabled={isDisabled}\n onPress={togglePasswordVisibilityHandler}\n />\n <UiComponentTunnelExit\n id=\"button\"\n component=\"PasswordCreationField\"\n />\n </Aria.Group>\n <ComplexityIndicator\n key={value}\n isEmptyValue={isEmptyValue}\n isLoading={\n isValidationPending ||\n policyValidationResult.isValid === \"indeterminate\"\n }\n policyValidationResult={policyValidationResult}\n validationResultState={stateFromValidationResult}\n isInvalid={invalidFromProps}\n />\n </Aria.Group>\n {isValidFromValidationResult && !renderedFieldErrorId && (\n // Out of react-aria's description slot, which would set its own\n // id – see `resultDescriptionId`.\n <Aria.TextContext.Provider value={null}>\n <FieldDescription\n id={resultDescriptionId}\n ref={resultDescriptionRef}\n >\n {latestValidationErrorText}\n </FieldDescription>\n </Aria.TextContext.Provider>\n )}\n </PropsContextProvider>\n </FieldErrorCaptureContext>\n <FieldErrorView />\n </Aria.TextField>\n );\n },\n);\n\nexport default PasswordCreationField;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PasswordCreationField.mjs","names":["locales","styles","_Fragment"],"sources":["../../../../../../../src/components/PasswordCreationField/PasswordCreationField.tsx"],"sourcesContent":["import {\n type PropsWithChildren,\n useState,\n type ClipboardEvent,\n useCallback,\n useId,\n useMemo,\n useRef,\n} from \"react\";\nimport {\n dynamic,\n type PropsContext,\n PropsContextProvider,\n} from \"@/lib/propsContext\";\nimport {\n flowComponent,\n type FlowComponentProps,\n} from \"@/lib/componentFactory/flowComponent\";\nimport styles from \"./PasswordCreationField.module.scss\";\nimport * as Aria from \"react-aria-components\";\nimport clsx from \"clsx\";\nimport { type ActionFn } from \"@/components/Action\";\nimport getStateFromLatestPolicyValidationResult from \"@/components/PasswordCreationField/lib/getStateFromLatestPolicyValidationResult\";\nimport locales from \"./locales/*.locale.json\";\nimport generateValidationTranslation from \"@/components/PasswordCreationField/lib/generateValidationTranslation\";\nimport FieldDescription from \"@/components/FieldDescription\";\nimport ComplexityIndicator from \"@/components/PasswordCreationField/components/ComplexityIndicator/ComplexityIndicator\";\nimport { generatePassword } from \"@/components/PasswordCreationField/worker/generatePassword\";\nimport TogglePasswordVisibilityButton from \"@/components/PasswordCreationField/components/TogglePasswordVisibilityButton/TogglePasswordVisibilityButton\";\nimport { ValidationResultButton } from \"@/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton\";\nimport { PasswordGenerateButton } from \"@/components/PasswordCreationField/components/PasswordGenerateButton/PasswordGenerateButton\";\nimport type {\n PolicyValidationResult,\n PolicyGenericDeclaration,\n RuleValidationResult,\n} from \"@/integrations/@mittwald/password-tools-js\";\nimport {\n defaultPasswordCreationPolicy,\n Policy,\n} from \"@/integrations/@mittwald/password-tools-js\";\nimport { usePolicyValidationResult } from \"@/components/PasswordCreationField/lib/usePolicyValidationResult\";\nimport { joinIds, useFieldComponent } from \"@/lib/hooks/useFieldComponent\";\nimport { FieldError } from \"@/components/FieldError\";\nimport { useControlledHostValueProps } from \"@/lib/remote/useControlledHostValueProps\";\nimport { useLocalizedStringFormatter } from \"@/components/TranslationProvider/useLocalizedStringFormatter\";\nimport { UiComponentTunnelExit } from \"@/components/UiComponentTunnel/UiComponentTunnelExit\";\nimport {\n useAriaAnnouncePasswordVisibility,\n useAriaAnnounceValidationState,\n} from \"@/components/PasswordCreationField/lib/ariaAnnounce\";\n\nexport interface PasswordCreationFieldProps\n extends\n PropsWithChildren<\n Omit<Aria.TextFieldProps, \"children\" | \"value\" | \"defaultValue\"> &\n Partial<Pick<Aria.FieldErrorRenderProps, \"validationErrors\">>\n >,\n FlowComponentProps<HTMLInputElement> {\n /** The password of a controlled field. */\n value?: string;\n /** Called with the password and its validity whenever the password changes. */\n onValidationResult?: (result: { password: string; isValid: boolean }) => void;\n /** The initial password of an uncontrolled field. */\n defaultValue?: string;\n /** The placeholder shown while the field is empty. */\n placeholder?: string;\n /**\n * The policy the password is validated against.\n *\n * @default defaultPasswordCreationPolicy\n */\n validationPolicy?: PolicyGenericDeclaration;\n}\n\nexport interface ResolvedPolicyValidationResult extends Omit<\n PolicyValidationResult,\n \"isValid\"\n> {\n isValid: boolean | \"indeterminate\";\n ruleResults: RuleValidationResult[];\n}\n\n/** @flr-generate all */\nexport const PasswordCreationField = flowComponent(\n \"PasswordCreationField\",\n (props) => {\n const {\n children,\n className,\n ref,\n isDisabled,\n onValidationResult,\n isInvalid: invalidFromProps,\n validationPolicy:\n validationPolicyFromProps = defaultPasswordCreationPolicy,\n isRequired,\n value,\n onChange,\n ...rest\n } = useControlledHostValueProps(props, \"\");\n\n const translate = useLocalizedStringFormatter(\n locales,\n \"PasswordCreationField\",\n );\n\n const validationPolicy = useMemo(\n () => Policy.fromDeclaration(validationPolicyFromProps),\n [validationPolicyFromProps],\n );\n\n const [isPasswordRevealed, setIsPasswordRevealed] = useState(false);\n const initialPolicyValidationState: ResolvedPolicyValidationResult = {\n isValid: \"indeterminate\",\n complexity: {\n min: validationPolicy.minComplexity,\n actual: 4,\n warning: null,\n },\n ruleResults: [],\n };\n\n const [policyValidationResult, setPolicyValidationResult] = useState(\n initialPolicyValidationState,\n );\n\n /**\n * A validation that outlasts the delay shows the loading bar, but keeps the\n * previous result until the new one arrives – dropping it flickered the\n * error and the bar color after every typing pause.\n */\n const [isValidationPending, setIsValidationPending] = useState(false);\n const loadingRef = useRef<ReturnType<typeof setTimeout>>(null);\n const { rememberValidationResult } = usePolicyValidationResult(\n validationPolicy,\n value ?? \"\",\n () => {\n if (isEmptyValue) {\n return;\n }\n\n if (loadingRef.current) {\n clearTimeout(loadingRef.current);\n }\n loadingRef.current = setTimeout(() => {\n setIsValidationPending(true);\n }, 150);\n },\n ({ password, isValid, results }) => {\n if (loadingRef.current) {\n clearTimeout(loadingRef.current);\n }\n setIsValidationPending(false);\n\n if (isEmptyValue) {\n setPolicyValidationResult(() => ({\n ...results,\n isValid: true,\n }));\n return;\n }\n\n setPolicyValidationResult(() => results);\n onValidationResult?.({ password, isValid });\n },\n );\n\n const isEmptyValue = !value;\n const stateFromValidationResult = getStateFromLatestPolicyValidationResult(\n isEmptyValue,\n policyValidationResult,\n );\n let latestValidationErrorText = undefined;\n if (stateFromValidationResult) {\n const [translationKey, translationValues] = generateValidationTranslation(\n stateFromValidationResult,\n );\n latestValidationErrorText = translate.format(\n translationKey,\n translationValues,\n );\n }\n\n const isValidFromValidationResult =\n !isEmptyValue && stateFromValidationResult?.isValid;\n\n const isInvalidFromValidationResult =\n !isEmptyValue && stateFromValidationResult?.isValid === false;\n const isInvalid = invalidFromProps || isInvalidFromValidationResult;\n\n const {\n FieldErrorView,\n FieldErrorCaptureContext,\n wrapperProps,\n controlProps,\n fieldPropsContext,\n renderedFieldErrorId,\n skeletonProps,\n } = useFieldComponent(props, \"PasswordCreationField\");\n\n /**\n * The result for a valid password appears after the asynchronous policy\n * validation. react-aria resolves its description slot only when the\n * field's validity changes, so a result that appears while the field stays\n * valid (a generated password) was never linked. It gets its own id\n * instead, referenced only while it is rendered.\n *\n * A shown error replaces it: the FormField styles hide descriptions next to\n * an error, and a hidden description would still be announced.\n */\n const resultDescriptionId = useId();\n const [isResultDescriptionRendered, setIsResultDescriptionRendered] =\n useState(false);\n const resultDescriptionRef = useCallback((element: Element | null) => {\n setIsResultDescriptionRendered(!!element);\n }, []);\n const describedBy = joinIds(\n isResultDescriptionRendered && resultDescriptionId,\n controlProps[\"aria-describedby\"],\n );\n\n useAriaAnnounceValidationState(\n latestValidationErrorText,\n !isEmptyValue && policyValidationResult.isValid !== \"indeterminate\",\n );\n useAriaAnnouncePasswordVisibility(isPasswordRevealed);\n\n const setOptimisticPolicyValidationResult = (\n state: Partial<ResolvedPolicyValidationResult> = {},\n ) => {\n setPolicyValidationResult((currentState) => ({\n ...currentState,\n isValid: true,\n ...state,\n }));\n };\n\n const onPasswordGenerateHandler: ActionFn = async () => {\n const generatedPassword = await generatePassword(validationPolicy);\n /**\n * The generator returns only passwords the policy accepts, so the rating\n * is set with the password instead of after the typing debounce. It is\n * the policy's own result: a guessed one (full strength) was overturned\n * by the real rating, e.g. to \"meets the minimum requirements\". Cached,\n * so the debounced validation of this password does not rate it again.\n */\n const generatedPasswordResult =\n await validationPolicy.validate(generatedPassword);\n rememberValidationResult(generatedPassword, generatedPasswordResult);\n setPolicyValidationResult(generatedPasswordResult);\n setIsPasswordRevealed(true);\n onChange(generatedPassword);\n };\n\n const onPasswordPasteHandler = (event: ClipboardEvent) => {\n const pastedValue = event.clipboardData?.getData(\"text\");\n if (typeof pastedValue === \"string\" && pastedValue !== value) {\n setOptimisticPolicyValidationResult({\n isValid: \"indeterminate\",\n });\n }\n };\n\n const togglePasswordVisibilityHandler = () => {\n setIsPasswordRevealed((old) => !old);\n };\n\n const propsContext: PropsContext = {\n ...fieldPropsContext,\n Button: {\n tunnel: {\n id: \"button\",\n component: \"PasswordCreationField\",\n },\n size: \"m\",\n variant: \"plain\",\n color: \"secondary\",\n isDisabled: isDisabled,\n className: styles.button,\n },\n CopyButton: {\n tunnel: {\n id: \"button\",\n component: \"PasswordCreationField\",\n },\n size: \"m\",\n variant: \"plain\",\n color: \"secondary\",\n isDisabled: isDisabled,\n className: styles.button,\n text: value,\n },\n Label: {\n ...fieldPropsContext.Label,\n children: dynamic((localProps) => {\n return (\n <>\n {localProps.children}\n <PasswordGenerateButton\n isDisabled={isDisabled}\n onGeneratePasswordAction={onPasswordGenerateHandler}\n />\n <ValidationResultButton\n isEmptyValue={isEmptyValue}\n isDisabled={isDisabled}\n policyValidationResult={policyValidationResult}\n />\n </>\n );\n }),\n },\n };\n\n return (\n <Aria.TextField\n {...rest}\n {...skeletonProps}\n aria-describedby={describedBy}\n value={value}\n type={isPasswordRevealed ? \"text\" : \"password\"}\n onChange={onChange}\n onPaste={onPasswordPasteHandler}\n className={clsx(wrapperProps.className, className)}\n isDisabled={isDisabled}\n isInvalid={isInvalid}\n isRequired={isRequired}\n >\n <FieldErrorCaptureContext>\n <PropsContextProvider\n props={propsContext}\n dependencies={[\n isDisabled,\n isRequired,\n value,\n policyValidationResult,\n isEmptyValue,\n ]}\n >\n {children}\n <Aria.Group\n isDisabled={isDisabled}\n className={clsx(styles.inputGroup)}\n >\n <Aria.Input ref={ref} className={styles.input} />\n <Aria.Group className={styles.buttonContainer}>\n <TogglePasswordVisibilityButton\n className={styles.button}\n isVisible={isPasswordRevealed}\n isDisabled={isDisabled}\n onPress={togglePasswordVisibilityHandler}\n />\n <UiComponentTunnelExit\n id=\"button\"\n component=\"PasswordCreationField\"\n />\n </Aria.Group>\n <ComplexityIndicator\n key={value}\n isEmptyValue={isEmptyValue}\n isLoading={\n isValidationPending ||\n policyValidationResult.isValid === \"indeterminate\"\n }\n policyValidationResult={policyValidationResult}\n validationResultState={stateFromValidationResult}\n isInvalid={invalidFromProps}\n />\n </Aria.Group>\n {isValidFromValidationResult && !renderedFieldErrorId && (\n // Out of react-aria's description slot, which would set its own\n // id – see `resultDescriptionId`.\n <Aria.TextContext.Provider value={null}>\n <FieldDescription\n id={resultDescriptionId}\n ref={resultDescriptionRef}\n >\n {latestValidationErrorText}\n </FieldDescription>\n </Aria.TextContext.Provider>\n )}\n </PropsContextProvider>\n {/* After `children`: the field shows the first error in the tree, and\n a consumer's error wins over the policy's. */}\n {latestValidationErrorText && (\n <FieldError>{latestValidationErrorText}</FieldError>\n )}\n </FieldErrorCaptureContext>\n <FieldErrorView />\n </Aria.TextField>\n );\n },\n);\n\nexport default PasswordCreationField;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { default as List } from '../model/List';
|
|
2
|
+
/**
|
|
3
|
+
* A value read from the list model, re-rendering only when it changes. The
|
|
4
|
+
* selector has to return a primitive or a value that keeps its identity.
|
|
5
|
+
*/
|
|
6
|
+
export declare const useListSelector: <R, T = never>(selector: (list: List<T>) => R) => R;
|
|
@@ -3,4 +3,20 @@ interface ListContext {
|
|
|
3
3
|
list: List<never>;
|
|
4
4
|
}
|
|
5
5
|
export declare const listContext: import('react').Context<ListContext>;
|
|
6
|
+
/**
|
|
7
|
+
* Hands the committed list model to `useListSelector` without changing the
|
|
8
|
+
* context value. The model is rebuilt on every render of `List`; read from
|
|
9
|
+
* `listContext`, it re-renders every consumer, including the ones inside items
|
|
10
|
+
* that skip their own render. A selector re-renders only when the value it
|
|
11
|
+
* reads changes.
|
|
12
|
+
*/
|
|
13
|
+
export declare class ListContextStore {
|
|
14
|
+
private list;
|
|
15
|
+
private readonly listeners;
|
|
16
|
+
constructor(list: List<never>);
|
|
17
|
+
static useNew(list: List<never>): ListContextStore;
|
|
18
|
+
readonly getList: () => List<never>;
|
|
19
|
+
readonly subscribe: (listener: () => void) => (() => void);
|
|
20
|
+
}
|
|
21
|
+
export declare const listStoreContext: import('react').Context<ListContextStore>;
|
|
6
22
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mittwald/flow-react-components",
|
|
3
|
-
"version": "1.6.0-next.
|
|
3
|
+
"version": "1.6.0-next.47",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A React implementation of Flow, mittwald’s design system",
|
|
6
6
|
"homepage": "https://flow.mittwald.de",
|
|
@@ -84,9 +84,9 @@
|
|
|
84
84
|
"@internationalized/string-compiler": "^3.4.1",
|
|
85
85
|
"@lezer/common": "^1.5.2",
|
|
86
86
|
"@lezer/highlight": "^1.2.3",
|
|
87
|
-
"@mittwald/flow-icons": "^1.6.0-next.
|
|
87
|
+
"@mittwald/flow-icons": "^1.6.0-next.47",
|
|
88
88
|
"@mittwald/password-tools-js": "^3.0.0",
|
|
89
|
-
"@mittwald/react-tunnel": "^1.6.0-next.
|
|
89
|
+
"@mittwald/react-tunnel": "^1.6.0-next.47",
|
|
90
90
|
"@mittwald/react-use-promise": "^4.2.2",
|
|
91
91
|
"@react-aria/form": "^3.2.1",
|
|
92
92
|
"@react-aria/i18n": "^3.13.1",
|
|
@@ -139,13 +139,13 @@
|
|
|
139
139
|
"@internationalized/date": "^3.12.3",
|
|
140
140
|
"@lezer/generator": "^1.8.0",
|
|
141
141
|
"@lezer/lr": "^1.4.10",
|
|
142
|
-
"@mittwald/flow-components-base": "1.6.0-next.
|
|
143
|
-
"@mittwald/flow-core": "1.6.0-next.
|
|
144
|
-
"@mittwald/flow-design-tokens": "1.6.0-next.
|
|
145
|
-
"@mittwald/flow-icons-base": "1.6.0-next.
|
|
142
|
+
"@mittwald/flow-components-base": "1.6.0-next.47",
|
|
143
|
+
"@mittwald/flow-core": "1.6.0-next.47",
|
|
144
|
+
"@mittwald/flow-design-tokens": "1.6.0-next.47",
|
|
145
|
+
"@mittwald/flow-icons-base": "1.6.0-next.47",
|
|
146
146
|
"@mittwald/react-use-promise": "^4.2.2",
|
|
147
147
|
"@mittwald/remote-dom-react": "1.2.2-mittwald.10",
|
|
148
|
-
"@mittwald/typescript-config": "1.6.0-next.
|
|
148
|
+
"@mittwald/typescript-config": "1.6.0-next.47",
|
|
149
149
|
"@nx/storybook": "^22.7.6",
|
|
150
150
|
"@storybook/addon-a11y": "^10.5.10",
|
|
151
151
|
"@storybook/addon-actions": "^9.0.8",
|
|
@@ -199,7 +199,7 @@
|
|
|
199
199
|
},
|
|
200
200
|
"peerDependencies": {
|
|
201
201
|
"@internationalized/date": "^3.12.2",
|
|
202
|
-
"@mittwald/flow-icons-pro": "1.6.0-next.
|
|
202
|
+
"@mittwald/flow-icons-pro": "1.6.0-next.47",
|
|
203
203
|
"@mittwald/react-use-promise": "^4.2.2",
|
|
204
204
|
"next": "^16.2.3",
|
|
205
205
|
"react": "^19.2.0",
|
|
@@ -223,5 +223,5 @@
|
|
|
223
223
|
"browserslist": [
|
|
224
224
|
"baseline widely available"
|
|
225
225
|
],
|
|
226
|
-
"gitHead": "
|
|
226
|
+
"gitHead": "61251e5c592539ad100535d2cb53eece8c01dba5"
|
|
227
227
|
}
|