torch-glare 2.5.5 → 2.6.0
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/README.md +4 -3
- package/dist/bin/index.js +9 -24
- package/dist/bin/index.js.map +1 -1
- package/dist/src/commands/add.d.ts +2 -2
- package/dist/src/commands/add.d.ts.map +1 -1
- package/dist/src/commands/add.js +4 -70
- package/dist/src/commands/add.js.map +1 -1
- package/dist/src/commands/hook.d.ts +5 -4
- package/dist/src/commands/hook.d.ts.map +1 -1
- package/dist/src/commands/hook.js +7 -75
- package/dist/src/commands/hook.js.map +1 -1
- package/dist/src/commands/init.js +1 -1
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/layout.d.ts +5 -4
- package/dist/src/commands/layout.d.ts.map +1 -1
- package/dist/src/commands/layout.js +7 -75
- package/dist/src/commands/layout.js.map +1 -1
- package/dist/src/commands/provider.d.ts +5 -4
- package/dist/src/commands/provider.d.ts.map +1 -1
- package/dist/src/commands/provider.js +7 -75
- package/dist/src/commands/provider.js.map +1 -1
- package/dist/src/commands/update.d.ts.map +1 -1
- package/dist/src/commands/update.js +78 -43
- package/dist/src/commands/update.js.map +1 -1
- package/dist/src/commands/utils.d.ts +5 -4
- package/dist/src/commands/utils.d.ts.map +1 -1
- package/dist/src/commands/utils.js +7 -80
- package/dist/src/commands/utils.js.map +1 -1
- package/dist/src/shared/addFromRegistry.d.ts +16 -0
- package/dist/src/shared/addFromRegistry.d.ts.map +1 -0
- package/dist/src/shared/addFromRegistry.js +88 -0
- package/dist/src/shared/addFromRegistry.js.map +1 -0
- package/dist/src/shared/getInstallCommand.d.ts.map +1 -1
- package/dist/src/shared/getInstallCommand.js +5 -0
- package/dist/src/shared/getInstallCommand.js.map +1 -1
- package/dist/src/shared/installDependencies.d.ts +0 -10
- package/dist/src/shared/installDependencies.d.ts.map +1 -1
- package/dist/src/shared/installDependencies.js +0 -17
- package/dist/src/shared/installDependencies.js.map +1 -1
- package/dist/src/shared/installFromPlan.d.ts +7 -5
- package/dist/src/shared/installFromPlan.d.ts.map +1 -1
- package/dist/src/shared/installFromPlan.js +103 -36
- package/dist/src/shared/installFromPlan.js.map +1 -1
- package/dist/src/shared/loadRegistry.d.ts +12 -3
- package/dist/src/shared/loadRegistry.d.ts.map +1 -1
- package/dist/src/shared/loadRegistry.js +19 -20
- package/dist/src/shared/loadRegistry.js.map +1 -1
- package/dist/src/shared/registryClient.d.ts +19 -0
- package/dist/src/shared/registryClient.d.ts.map +1 -0
- package/dist/src/shared/registryClient.js +128 -0
- package/dist/src/shared/registryClient.js.map +1 -0
- package/dist/src/shared/resolveEntry.d.ts +10 -7
- package/dist/src/shared/resolveEntry.d.ts.map +1 -1
- package/dist/src/shared/resolveEntry.js +18 -13
- package/dist/src/shared/resolveEntry.js.map +1 -1
- package/dist/src/shared/suggestOtherCommand.d.ts +7 -1
- package/dist/src/shared/suggestOtherCommand.d.ts.map +1 -1
- package/dist/src/shared/suggestOtherCommand.js +29 -23
- package/dist/src/shared/suggestOtherCommand.js.map +1 -1
- package/dist/src/types/main.d.ts +31 -2
- package/dist/src/types/main.d.ts.map +1 -1
- package/package.json +6 -11
- package/apps/lib/components/ActionButton.tsx +0 -93
- package/apps/lib/components/ActionsGroup.tsx +0 -27
- package/apps/lib/components/AlertDialog.tsx +0 -204
- package/apps/lib/components/Avatar.tsx +0 -46
- package/apps/lib/components/Badge.tsx +0 -250
- package/apps/lib/components/BadgeField.tsx +0 -338
- package/apps/lib/components/Breadcrumb.tsx +0 -280
- package/apps/lib/components/Button.tsx +0 -317
- package/apps/lib/components/ButtonGroup.tsx +0 -190
- package/apps/lib/components/Calendar.tsx +0 -115
- package/apps/lib/components/Card.tsx +0 -95
- package/apps/lib/components/Checkbox.tsx +0 -42
- package/apps/lib/components/ColorPicker.tsx +0 -441
- package/apps/lib/components/ConclusionHeader.tsx +0 -148
- package/apps/lib/components/ContextMenu.tsx +0 -571
- package/apps/lib/components/CountBadge.tsx +0 -52
- package/apps/lib/components/DataTable.tsx +0 -213
- package/apps/lib/components/DataViews/badge.ts +0 -49
- package/apps/lib/components/DataViews/cell.tsx +0 -324
- package/apps/lib/components/DataViews/context.ts +0 -144
- package/apps/lib/components/DataViews/data-views.tsx +0 -383
- package/apps/lib/components/DataViews/filters/children.tsx +0 -98
- package/apps/lib/components/DataViews/filters/custom.tsx +0 -34
- package/apps/lib/components/DataViews/filters/filters.tsx +0 -157
- package/apps/lib/components/DataViews/filters/index.ts +0 -4
- package/apps/lib/components/DataViews/filters/labelled.tsx +0 -20
- package/apps/lib/components/DataViews/filters/presets.tsx +0 -65
- package/apps/lib/components/DataViews/filters/sync.tsx +0 -35
- package/apps/lib/components/DataViews/filters/values.ts +0 -173
- package/apps/lib/components/DataViews/header.tsx +0 -220
- package/apps/lib/components/DataViews/hooks/index.ts +0 -5
- package/apps/lib/components/DataViews/hooks/useActiveRow.ts +0 -22
- package/apps/lib/components/DataViews/hooks/useControllable.ts +0 -52
- package/apps/lib/components/DataViews/index.ts +0 -84
- package/apps/lib/components/DataViews/panel/columns.tsx +0 -153
- package/apps/lib/components/DataViews/panel/controls.tsx +0 -106
- package/apps/lib/components/DataViews/panel/index.ts +0 -3
- package/apps/lib/components/DataViews/panel/panel.tsx +0 -164
- package/apps/lib/components/DataViews/panel/saved-views.tsx +0 -67
- package/apps/lib/components/DataViews/panel/section.tsx +0 -81
- package/apps/lib/components/DataViews/panel/sort.tsx +0 -42
- package/apps/lib/components/DataViews/panel/tab.tsx +0 -31
- package/apps/lib/components/DataViews/slots.ts +0 -63
- package/apps/lib/components/DataViews/states.tsx +0 -38
- package/apps/lib/components/DataViews/types.ts +0 -510
- package/apps/lib/components/DataViews/views/board-view.tsx +0 -381
- package/apps/lib/components/DataViews/views/card-rows.tsx +0 -36
- package/apps/lib/components/DataViews/views/inbox-view.tsx +0 -260
- package/apps/lib/components/DataViews/views/pane-views.tsx +0 -196
- package/apps/lib/components/DataViews/views/table-view.tsx +0 -493
- package/apps/lib/components/DataViews/views/tree-view.tsx +0 -367
- package/apps/lib/components/DatePicker.tsx +0 -232
- package/apps/lib/components/Dialog.tsx +0 -116
- package/apps/lib/components/Divider.tsx +0 -28
- package/apps/lib/components/Drawer.tsx +0 -420
- package/apps/lib/components/DropdownMenu.tsx +0 -540
- package/apps/lib/components/FieldHint.tsx +0 -69
- package/apps/lib/components/Form.tsx +0 -182
- package/apps/lib/components/FormBuilder/context.ts +0 -73
- package/apps/lib/components/FormBuilder/field-kind.ts +0 -28
- package/apps/lib/components/FormBuilder/fields/ChoiceFields.tsx +0 -60
- package/apps/lib/components/FormBuilder/fields/ColorField.tsx +0 -59
- package/apps/lib/components/FormBuilder/fields/CustomField.tsx +0 -11
- package/apps/lib/components/FormBuilder/fields/DateField.tsx +0 -32
- package/apps/lib/components/FormBuilder/fields/FieldArray.tsx +0 -74
- package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +0 -163
- package/apps/lib/components/FormBuilder/fields/FileField.tsx +0 -39
- package/apps/lib/components/FormBuilder/fields/OptionListFields.tsx +0 -132
- package/apps/lib/components/FormBuilder/fields/OtpField.tsx +0 -31
- package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +0 -109
- package/apps/lib/components/FormBuilder/fields/RichTextEditorField.tsx +0 -31
- package/apps/lib/components/FormBuilder/fields/SelectField.tsx +0 -91
- package/apps/lib/components/FormBuilder/fields/SignatureField.tsx +0 -157
- package/apps/lib/components/FormBuilder/fields/SliderField.tsx +0 -67
- package/apps/lib/components/FormBuilder/fields/SwitchBoxField.tsx +0 -41
- package/apps/lib/components/FormBuilder/fields/TableField.tsx +0 -336
- package/apps/lib/components/FormBuilder/fields/TextField.tsx +0 -213
- package/apps/lib/components/FormBuilder/fields/TreeSelectField.tsx +0 -43
- package/apps/lib/components/FormBuilder/fields/countries.ts +0 -303
- package/apps/lib/components/FormBuilder/fields/index.ts +0 -25
- package/apps/lib/components/FormBuilder/form-builder.tsx +0 -198
- package/apps/lib/components/FormBuilder/index.ts +0 -31
- package/apps/lib/components/FormBuilder/numberFormat.ts +0 -16
- package/apps/lib/components/FormBuilder/submit.tsx +0 -47
- package/apps/lib/components/FormBuilder/types.ts +0 -311
- package/apps/lib/components/FormRenderer/FormDrawer.tsx +0 -128
- package/apps/lib/components/FormRenderer/detail.tsx +0 -207
- package/apps/lib/components/FormRenderer/form-renderer.tsx +0 -297
- package/apps/lib/components/FormRenderer/header.tsx +0 -88
- package/apps/lib/components/FormRenderer/index.ts +0 -15
- package/apps/lib/components/FormRenderer/section.tsx +0 -39
- package/apps/lib/components/FormRenderer/stepper.tsx +0 -339
- package/apps/lib/components/FormRenderer/types.ts +0 -78
- package/apps/lib/components/FormSummary.tsx +0 -282
- package/apps/lib/components/HeaderBar.tsx +0 -117
- package/apps/lib/components/ImageAttachment.tsx +0 -209
- package/apps/lib/components/InnerLabelField.tsx +0 -154
- package/apps/lib/components/Input.tsx +0 -224
- package/apps/lib/components/InputField.tsx +0 -147
- package/apps/lib/components/InputOTP.tsx +0 -84
- package/apps/lib/components/Label.tsx +0 -129
- package/apps/lib/components/LabelField.tsx +0 -76
- package/apps/lib/components/LabeledCheckBox.tsx +0 -51
- package/apps/lib/components/LabeledRadio.tsx +0 -51
- package/apps/lib/components/LinkButton.tsx +0 -94
- package/apps/lib/components/LoginButton.tsx +0 -51
- package/apps/lib/components/PasswordLevel.tsx +0 -58
- package/apps/lib/components/Popover.tsx +0 -312
- package/apps/lib/components/ProfileMenu.tsx +0 -85
- package/apps/lib/components/Radio.tsx +0 -59
- package/apps/lib/components/RadioCard.tsx +0 -50
- package/apps/lib/components/ScrollArea.tsx +0 -54
- package/apps/lib/components/SearchField.tsx +0 -62
- package/apps/lib/components/SearchableSelect.tsx +0 -318
- package/apps/lib/components/SearchableTable.tsx +0 -344
- package/apps/lib/components/SearchableTree.tsx +0 -485
- package/apps/lib/components/SearchableTreeDialog.tsx +0 -502
- package/apps/lib/components/SectionBlock.tsx +0 -147
- package/apps/lib/components/Select.tsx +0 -347
- package/apps/lib/components/SimpleSelect.tsx +0 -225
- package/apps/lib/components/Skeleton.tsx +0 -12
- package/apps/lib/components/SlideDatePicker.tsx +0 -253
- package/apps/lib/components/SpinLoading.tsx +0 -174
- package/apps/lib/components/Stepper.tsx +0 -480
- package/apps/lib/components/Switch.tsx +0 -82
- package/apps/lib/components/TabFormItem.tsx +0 -153
- package/apps/lib/components/TabSwitch.tsx +0 -172
- package/apps/lib/components/Table.tsx +0 -610
- package/apps/lib/components/TextEditor/ChartBlockTool.ts +0 -676
- package/apps/lib/components/TextEditor/RichTextField.tsx +0 -46
- package/apps/lib/components/TextEditor/TableDnDWrapper.ts +0 -462
- package/apps/lib/components/TextEditor/TextEditor.tsx +0 -1164
- package/apps/lib/components/TextEditor/TextEditorToolbar.tsx +0 -429
- package/apps/lib/components/TextEditor/editor-tools/AlignmentTune.ts +0 -70
- package/apps/lib/components/TextEditor/editor-tools/ColorInlineTool.ts +0 -50
- package/apps/lib/components/TextEditor/editor-tools/StrikethroughInlineTool.ts +0 -48
- package/apps/lib/components/TextEditor/editor-tools/inlineFormat.ts +0 -98
- package/apps/lib/components/TextEditor/editorjs.d.ts +0 -60
- package/apps/lib/components/TextEditor/index.ts +0 -7
- package/apps/lib/components/TextEditor/markdownParser.ts +0 -346
- package/apps/lib/components/Textarea.tsx +0 -117
- package/apps/lib/components/Timeline.tsx +0 -263
- package/apps/lib/components/Toast.tsx +0 -87
- package/apps/lib/components/Toggle.tsx +0 -125
- package/apps/lib/components/ToggleButton.tsx +0 -140
- package/apps/lib/components/Tooltip.tsx +0 -116
- package/apps/lib/components/TransparentLabel.tsx +0 -71
- package/apps/lib/components/TreeDropDown.tsx +0 -115
- package/apps/lib/components/TreeFolder/TreeFolder.tsx +0 -380
- package/apps/lib/components/TreeFolder/TreeFolderBreadcrumb.tsx +0 -75
- package/apps/lib/components/TreeFolder/TreeFolderRow.tsx +0 -354
- package/apps/lib/components/TreeFolder/TreeFolderStyles.tsx +0 -60
- package/apps/lib/components/TreeFolder/icons.tsx +0 -60
- package/apps/lib/components/TreeFolder/index.ts +0 -24
- package/apps/lib/components/TreeFolder/treeFolderUtils.ts +0 -109
- package/apps/lib/components/TreeFolder/types.ts +0 -77
- package/apps/lib/components/TreeFolder/useTreeFolderDnD.ts +0 -118
- package/apps/lib/hooks/useActiveTreeItem.ts +0 -58
- package/apps/lib/hooks/useClickOutside.ts +0 -27
- package/apps/lib/hooks/useDragDrop.tsx +0 -365
- package/apps/lib/hooks/useInfiniteScroll.ts +0 -108
- package/apps/lib/hooks/useIsMobile.ts +0 -21
- package/apps/lib/hooks/useResize.ts +0 -67
- package/apps/lib/hooks/useTagSelection.ts +0 -214
- package/apps/lib/layouts/CNLayout.tsx +0 -372
- package/apps/lib/layouts/DataViewCard.tsx +0 -62
- package/apps/lib/layouts/FieldSection.tsx +0 -91
- package/apps/lib/layouts/TreeSubLayout.tsx +0 -173
- package/apps/lib/providers/ThemeProvider.tsx +0 -84
- package/apps/lib/registry.json +0 -1201
- package/apps/lib/tsconfig.json +0 -9
- package/apps/lib/tsconfig.tsbuildinfo +0 -1
- package/apps/lib/utils/cn.ts +0 -6
- package/apps/lib/utils/color.ts +0 -175
- package/apps/lib/utils/dataViews/path.ts +0 -67
- package/apps/lib/utils/dataViews/query.ts +0 -73
- package/apps/lib/utils/dataViews/types.ts +0 -187
- package/apps/lib/utils/dateFormat.ts +0 -108
- package/apps/lib/utils/resize.ts +0 -31
- package/apps/lib/utils/types.ts +0 -16
- package/docs/BLOCKS.md +0 -129
- package/docs/CHANGELOG-1.1.16.md +0 -108
- package/docs/Cover.png +0 -0
- package/docs/README.md +0 -215
- package/docs/components/action-button.md +0 -638
- package/docs/components/actions-group.md +0 -708
- package/docs/components/alert-dialog.md +0 -920
- package/docs/components/avatar.md +0 -709
- package/docs/components/badge-field.md +0 -712
- package/docs/components/badge.md +0 -301
- package/docs/components/breadcrumb.md +0 -930
- package/docs/components/button-group.md +0 -650
- package/docs/components/button.md +0 -639
- package/docs/components/calendar.md +0 -555
- package/docs/components/card.md +0 -622
- package/docs/components/chart-block-tool.md +0 -129
- package/docs/components/checkbox.md +0 -699
- package/docs/components/cn-layout.md +0 -854
- package/docs/components/color-picker.md +0 -101
- package/docs/components/conclusion-header.md +0 -80
- package/docs/components/context-menu.md +0 -483
- package/docs/components/count-badge.md +0 -560
- package/docs/components/data-table.md +0 -854
- package/docs/components/data-views/backend-response.md +0 -324
- package/docs/components/data-views/examples/a11y-rtl.md +0 -250
- package/docs/components/data-views/examples/api-orders-route.md +0 -130
- package/docs/components/data-views/examples/fields.md +0 -362
- package/docs/components/data-views/examples/filters.md +0 -307
- package/docs/components/data-views/examples/inbox-routing.md +0 -218
- package/docs/components/data-views/examples/index.md +0 -29
- package/docs/components/data-views/examples/overview.md +0 -244
- package/docs/components/data-views/examples/panel.md +0 -212
- package/docs/components/data-views/examples/scale.md +0 -231
- package/docs/components/data-views/examples/server-side.md +0 -210
- package/docs/components/data-views/examples/state.md +0 -250
- package/docs/components/data-views/examples/tree-custom.md +0 -388
- package/docs/components/data-views/examples/view-registry.md +0 -313
- package/docs/components/data-views/examples/views.md +0 -534
- package/docs/components/data-views/guide.md +0 -405
- package/docs/components/data-views/index.md +0 -1495
- package/docs/components/data-views/migration.md +0 -79
- package/docs/components/date-picker.md +0 -857
- package/docs/components/dialog.md +0 -929
- package/docs/components/divider.md +0 -766
- package/docs/components/drawer.md +0 -654
- package/docs/components/dropdown-menu.md +0 -668
- package/docs/components/field-hint.md +0 -898
- package/docs/components/field-section.md +0 -781
- package/docs/components/form-builder.md +0 -276
- package/docs/components/form-renderer.md +0 -326
- package/docs/components/form-summary.md +0 -138
- package/docs/components/form.md +0 -785
- package/docs/components/header-bar.md +0 -188
- package/docs/components/image-attachment.md +0 -906
- package/docs/components/inner-label-field.md +0 -819
- package/docs/components/input-field.md +0 -623
- package/docs/components/input-otp.md +0 -756
- package/docs/components/input.md +0 -634
- package/docs/components/label-field.md +0 -770
- package/docs/components/label.md +0 -761
- package/docs/components/labeled-check-box.md +0 -642
- package/docs/components/labeled-radio.md +0 -796
- package/docs/components/link-button.md +0 -685
- package/docs/components/login-button.md +0 -872
- package/docs/components/password-level.md +0 -803
- package/docs/components/popover.md +0 -649
- package/docs/components/profile-menu.md +0 -656
- package/docs/components/radio-card.md +0 -801
- package/docs/components/radio.md +0 -735
- package/docs/components/scroll-area.md +0 -691
- package/docs/components/search-field.md +0 -702
- package/docs/components/searchable-select.md +0 -368
- package/docs/components/searchable-table.md +0 -438
- package/docs/components/searchable-tree-dialog.md +0 -190
- package/docs/components/searchable-tree.md +0 -200
- package/docs/components/section-block.md +0 -477
- package/docs/components/select.md +0 -709
- package/docs/components/simple-select.md +0 -741
- package/docs/components/skeleton.md +0 -706
- package/docs/components/slide-date-picker.md +0 -837
- package/docs/components/spin-loading.md +0 -696
- package/docs/components/stepper.md +0 -316
- package/docs/components/switch.md +0 -770
- package/docs/components/tab-form-item.md +0 -773
- package/docs/components/tab-switch.md +0 -170
- package/docs/components/table-dnd-wrapper.md +0 -107
- package/docs/components/table.md +0 -1088
- package/docs/components/text-editor.md +0 -735
- package/docs/components/textarea.md +0 -681
- package/docs/components/timeline.md +0 -254
- package/docs/components/toast.md +0 -773
- package/docs/components/toggle-button.md +0 -643
- package/docs/components/toggle.md +0 -882
- package/docs/components/tooltip.md +0 -513
- package/docs/components/transparent-label.md +0 -721
- package/docs/components/tree-drop-down.md +0 -740
- package/docs/components/tree-folder.md +0 -110
- package/docs/components/tree-sub-layout.md +0 -707
- package/docs/explanation/architecture.md +0 -54
- package/docs/explanation/design-system.md +0 -52
- package/docs/how-to/form-and-list-recipes.md +0 -386
- package/docs/how-to/forms-with-form-builder.md +0 -472
- package/docs/how-to/guides.md +0 -1027
- package/docs/migration/changelog.md +0 -54
- package/docs/migration/form-builder-2.5.2.md +0 -113
- package/docs/reference/cli.md +0 -112
- package/docs/reference/components.md +0 -572
- package/docs/reference/hooks.md +0 -1500
- package/docs/reference/providers.md +0 -866
- package/docs/reference/tailwind-plugins.md +0 -942
- package/docs/reference/theme.md +0 -85
- package/docs/reference/types.md +0 -1016
- package/docs/reference/utilities.md +0 -704
- package/docs/tutorials/building-first-form.md +0 -846
- package/docs/tutorials/component-composition.md +0 -882
- package/docs/tutorials/getting-started.md +0 -302
- package/docs/tutorials/theming-basics.md +0 -785
|
@@ -1,803 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: PasswordLevel
|
|
3
|
-
description: Real-time password strength indicator with visual feedback based on length, symbols, and uppercase characters
|
|
4
|
-
group: Feedback & Status
|
|
5
|
-
keywords: [password, strength, security, validation, indicator, level]
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# PasswordLevel
|
|
9
|
-
|
|
10
|
-
> A real-time password strength indicator that visually displays password security level through a three-bar progress indicator. Evaluates password strength based on length, special characters, and uppercase letters.
|
|
11
|
-
|
|
12
|
-
## Installation
|
|
13
|
-
|
|
14
|
-
TORCH Glare is a copy-in library: the CLI copies this component's source into your project
|
|
15
|
-
(you do **not** install it from the npm package). Run `init` once, then `add`:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npx torch-glare@latest init
|
|
19
|
-
npx torch-glare@latest add PasswordLevel
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
`add` also copies any components, hooks, and utilities that `PasswordLevel` depends on.
|
|
23
|
-
|
|
24
|
-
## Import
|
|
25
|
-
|
|
26
|
-
Import from your project's local path — the alias configured in `glare.json` (e.g. `@/*`):
|
|
27
|
-
|
|
28
|
-
```tsx
|
|
29
|
-
import { PasswordLevel } from "@/components/PasswordLevel";
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Quick Examples
|
|
33
|
-
|
|
34
|
-
### Basic Usage
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
import { PasswordLevel } from "@/components/PasswordLevel";
|
|
38
|
-
import { InputField } from "@/components/InputField";
|
|
39
|
-
import { useState } from 'react'
|
|
40
|
-
|
|
41
|
-
function Example() {
|
|
42
|
-
const [password, setPassword] = useState('')
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
<div className="space-y-2">
|
|
46
|
-
<InputField
|
|
47
|
-
type="password"
|
|
48
|
-
value={password}
|
|
49
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
50
|
-
label="Password"
|
|
51
|
-
/>
|
|
52
|
-
<PasswordLevel value={password} />
|
|
53
|
-
</div>
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### With Labels
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
import { PasswordLevel } from "@/components/PasswordLevel";
|
|
62
|
-
import { InputField } from "@/components/InputField";
|
|
63
|
-
import { useState } from 'react'
|
|
64
|
-
|
|
65
|
-
function WithLabels() {
|
|
66
|
-
const [password, setPassword] = useState('')
|
|
67
|
-
|
|
68
|
-
const getStrengthLabel = (password: string) => {
|
|
69
|
-
let level = 0
|
|
70
|
-
if (password.length >= 6) level++
|
|
71
|
-
if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) level++
|
|
72
|
-
if (/[A-Z]/.test(password)) level++
|
|
73
|
-
|
|
74
|
-
if (level === 0) return 'No password'
|
|
75
|
-
if (level === 1) return 'Weak'
|
|
76
|
-
if (level === 2) return 'Medium'
|
|
77
|
-
return 'Strong'
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const getStrengthColor = (password: string) => {
|
|
81
|
-
const label = getStrengthLabel(password)
|
|
82
|
-
if (label === 'Weak') return 'text-red-500'
|
|
83
|
-
if (label === 'Medium') return 'text-yellow-500'
|
|
84
|
-
if (label === 'Strong') return 'text-green-500'
|
|
85
|
-
return 'text-gray-400'
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return (
|
|
89
|
-
<div className="space-y-2">
|
|
90
|
-
<InputField
|
|
91
|
-
type="password"
|
|
92
|
-
value={password}
|
|
93
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
94
|
-
label="Password"
|
|
95
|
-
/>
|
|
96
|
-
<PasswordLevel value={password} />
|
|
97
|
-
<p className={`text-sm font-medium ${getStrengthColor(password)}`}>
|
|
98
|
-
Password strength: {getStrengthLabel(password)}
|
|
99
|
-
</p>
|
|
100
|
-
</div>
|
|
101
|
-
)
|
|
102
|
-
}
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### Registration Form
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
import { PasswordLevel } from "@/components/PasswordLevel";
|
|
109
|
-
import { Button } from "@/components/Button";
|
|
110
|
-
import { FieldHint } from "@/components/FieldHint";
|
|
111
|
-
import { InputField } from "@/components/InputField";
|
|
112
|
-
import { useState } from 'react'
|
|
113
|
-
|
|
114
|
-
function RegistrationForm() {
|
|
115
|
-
const [password, setPassword] = useState('')
|
|
116
|
-
const [confirmPassword, setConfirmPassword] = useState('')
|
|
117
|
-
|
|
118
|
-
const passwordMatch = password && confirmPassword && password === confirmPassword
|
|
119
|
-
|
|
120
|
-
const getPasswordLevel = (pwd: string) => {
|
|
121
|
-
let level = 0
|
|
122
|
-
if (pwd.length >= 6) level++
|
|
123
|
-
if (/[!@#$%^&*(),.?":{}|<>]/.test(pwd)) level++
|
|
124
|
-
if (/[A-Z]/.test(pwd)) level++
|
|
125
|
-
return level
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const isStrongPassword = getPasswordLevel(password) === 3
|
|
129
|
-
|
|
130
|
-
return (
|
|
131
|
-
<form className="space-y-4">
|
|
132
|
-
<div className="space-y-2">
|
|
133
|
-
<InputField
|
|
134
|
-
type="password"
|
|
135
|
-
value={password}
|
|
136
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
137
|
-
label="Create Password"
|
|
138
|
-
/>
|
|
139
|
-
<PasswordLevel value={password} />
|
|
140
|
-
<FieldHint
|
|
141
|
-
state="info"
|
|
142
|
-
label="Password must be at least 6 characters with uppercase and symbols"
|
|
143
|
-
/>
|
|
144
|
-
</div>
|
|
145
|
-
|
|
146
|
-
<InputField
|
|
147
|
-
type="password"
|
|
148
|
-
value={confirmPassword}
|
|
149
|
-
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
150
|
-
label="Confirm Password"
|
|
151
|
-
/>
|
|
152
|
-
|
|
153
|
-
{confirmPassword && !passwordMatch && (
|
|
154
|
-
<FieldHint state="error" label="Passwords do not match" />
|
|
155
|
-
)}
|
|
156
|
-
|
|
157
|
-
<Button
|
|
158
|
-
type="submit"
|
|
159
|
-
disabled={!isStrongPassword || !passwordMatch}
|
|
160
|
-
>
|
|
161
|
-
Create Account
|
|
162
|
-
</Button>
|
|
163
|
-
</form>
|
|
164
|
-
)
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
### With Requirements Checklist
|
|
169
|
-
|
|
170
|
-
```typescript
|
|
171
|
-
import { PasswordLevel } from "@/components/PasswordLevel";
|
|
172
|
-
import { InputField } from "@/components/InputField";
|
|
173
|
-
import { useState } from 'react'
|
|
174
|
-
|
|
175
|
-
function PasswordWithChecklist() {
|
|
176
|
-
const [password, setPassword] = useState('')
|
|
177
|
-
|
|
178
|
-
const requirements = [
|
|
179
|
-
{
|
|
180
|
-
label: 'At least 6 characters',
|
|
181
|
-
met: password.length >= 6,
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
label: 'Contains a special character',
|
|
185
|
-
met: /[!@#$%^&*(),.?":{}|<>]/.test(password),
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
label: 'Contains an uppercase letter',
|
|
189
|
-
met: /[A-Z]/.test(password),
|
|
190
|
-
},
|
|
191
|
-
]
|
|
192
|
-
|
|
193
|
-
return (
|
|
194
|
-
<div className="space-y-4">
|
|
195
|
-
<InputField
|
|
196
|
-
type="password"
|
|
197
|
-
value={password}
|
|
198
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
199
|
-
label="Password"
|
|
200
|
-
/>
|
|
201
|
-
|
|
202
|
-
<PasswordLevel value={password} />
|
|
203
|
-
|
|
204
|
-
<div className="space-y-2">
|
|
205
|
-
<p className="text-sm font-medium">Password requirements:</p>
|
|
206
|
-
<ul className="space-y-1">
|
|
207
|
-
{requirements.map((req, index) => (
|
|
208
|
-
<li key={index} className="flex items-center gap-2 text-sm">
|
|
209
|
-
<i
|
|
210
|
-
className={`${
|
|
211
|
-
req.met
|
|
212
|
-
? 'ri-checkbox-circle-fill text-green-500'
|
|
213
|
-
: 'ri-checkbox-blank-circle-line text-gray-400'
|
|
214
|
-
}`}
|
|
215
|
-
/>
|
|
216
|
-
<span className={req.met ? 'text-green-600' : 'text-gray-600'}>
|
|
217
|
-
{req.label}
|
|
218
|
-
</span>
|
|
219
|
-
</li>
|
|
220
|
-
))}
|
|
221
|
-
</ul>
|
|
222
|
-
</div>
|
|
223
|
-
</div>
|
|
224
|
-
)
|
|
225
|
-
}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
### Password Change Form
|
|
229
|
-
|
|
230
|
-
```typescript
|
|
231
|
-
import { PasswordLevel } from "@/components/PasswordLevel";
|
|
232
|
-
import { Button } from "@/components/Button";
|
|
233
|
-
import { InputField } from "@/components/InputField";
|
|
234
|
-
import { useState } from 'react'
|
|
235
|
-
|
|
236
|
-
function PasswordChangeForm() {
|
|
237
|
-
const [currentPassword, setCurrentPassword] = useState('')
|
|
238
|
-
const [newPassword, setNewPassword] = useState('')
|
|
239
|
-
const [confirmPassword, setConfirmPassword] = useState('')
|
|
240
|
-
|
|
241
|
-
const canSubmit =
|
|
242
|
-
currentPassword &&
|
|
243
|
-
newPassword &&
|
|
244
|
-
newPassword === confirmPassword &&
|
|
245
|
-
getPasswordLevel(newPassword) >= 2 // At least medium strength
|
|
246
|
-
|
|
247
|
-
return (
|
|
248
|
-
<form className="space-y-4">
|
|
249
|
-
<InputField
|
|
250
|
-
type="password"
|
|
251
|
-
value={currentPassword}
|
|
252
|
-
onChange={(e) => setCurrentPassword(e.target.value)}
|
|
253
|
-
label="Current Password"
|
|
254
|
-
/>
|
|
255
|
-
|
|
256
|
-
<div className="space-y-2">
|
|
257
|
-
<InputField
|
|
258
|
-
type="password"
|
|
259
|
-
value={newPassword}
|
|
260
|
-
onChange={(e) => setNewPassword(e.target.value)}
|
|
261
|
-
label="New Password"
|
|
262
|
-
/>
|
|
263
|
-
<PasswordLevel value={newPassword} />
|
|
264
|
-
</div>
|
|
265
|
-
|
|
266
|
-
<InputField
|
|
267
|
-
type="password"
|
|
268
|
-
value={confirmPassword}
|
|
269
|
-
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
270
|
-
label="Confirm New Password"
|
|
271
|
-
/>
|
|
272
|
-
|
|
273
|
-
<Button type="submit" disabled={!canSubmit}>
|
|
274
|
-
Change Password
|
|
275
|
-
</Button>
|
|
276
|
-
</form>
|
|
277
|
-
)
|
|
278
|
-
}
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
### Real-time Feedback
|
|
282
|
-
|
|
283
|
-
```typescript
|
|
284
|
-
import { PasswordLevel } from "@/components/PasswordLevel";
|
|
285
|
-
import { InputField } from "@/components/InputField";
|
|
286
|
-
import { useState, useEffect } from 'react'
|
|
287
|
-
|
|
288
|
-
function RealTimeFeedback() {
|
|
289
|
-
const [password, setPassword] = useState('')
|
|
290
|
-
const [feedback, setFeedback] = useState<string[]>([])
|
|
291
|
-
|
|
292
|
-
useEffect(() => {
|
|
293
|
-
const newFeedback: string[] = []
|
|
294
|
-
|
|
295
|
-
if (password.length > 0 && password.length < 6) {
|
|
296
|
-
newFeedback.push('Password is too short')
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (password.length >= 6 && !/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
|
|
300
|
-
newFeedback.push('Add a special character for better security')
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (password.length >= 6 && !/[A-Z]/.test(password)) {
|
|
304
|
-
newFeedback.push('Add an uppercase letter for better security')
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
if (!/[0-9]/.test(password) && password.length > 0) {
|
|
308
|
-
newFeedback.push('Consider adding a number')
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
setFeedback(newFeedback)
|
|
312
|
-
}, [password])
|
|
313
|
-
|
|
314
|
-
return (
|
|
315
|
-
<div className="space-y-2">
|
|
316
|
-
<InputField
|
|
317
|
-
type="password"
|
|
318
|
-
value={password}
|
|
319
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
320
|
-
label="Password"
|
|
321
|
-
/>
|
|
322
|
-
|
|
323
|
-
<PasswordLevel value={password} />
|
|
324
|
-
|
|
325
|
-
{feedback.length > 0 && (
|
|
326
|
-
<div className="space-y-1">
|
|
327
|
-
{feedback.map((msg, index) => (
|
|
328
|
-
<p key={index} className="text-xs text-gray-600">
|
|
329
|
-
💡 {msg}
|
|
330
|
-
</p>
|
|
331
|
-
))}
|
|
332
|
-
</div>
|
|
333
|
-
)}
|
|
334
|
-
</div>
|
|
335
|
-
)
|
|
336
|
-
}
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
### Different Themes
|
|
340
|
-
|
|
341
|
-
```typescript
|
|
342
|
-
import { PasswordLevel } from "@/components/PasswordLevel";
|
|
343
|
-
import { useState } from 'react'
|
|
344
|
-
|
|
345
|
-
function ThemeExample() {
|
|
346
|
-
const [password, setPassword] = useState('')
|
|
347
|
-
|
|
348
|
-
return (
|
|
349
|
-
<div className="space-y-4">
|
|
350
|
-
<div className="p-4 bg-white">
|
|
351
|
-
<input
|
|
352
|
-
type="password"
|
|
353
|
-
value={password}
|
|
354
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
355
|
-
/>
|
|
356
|
-
<PasswordLevel value={password} theme="light" />
|
|
357
|
-
</div>
|
|
358
|
-
|
|
359
|
-
<div className="p-4 bg-gray-900">
|
|
360
|
-
<input
|
|
361
|
-
type="password"
|
|
362
|
-
value={password}
|
|
363
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
364
|
-
/>
|
|
365
|
-
<PasswordLevel value={password} theme="dark" />
|
|
366
|
-
</div>
|
|
367
|
-
</div>
|
|
368
|
-
)
|
|
369
|
-
}
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
### Custom Styling
|
|
373
|
-
|
|
374
|
-
```typescript
|
|
375
|
-
import { PasswordLevel } from "@/components/PasswordLevel";
|
|
376
|
-
import { useState } from 'react'
|
|
377
|
-
|
|
378
|
-
function CustomStyling() {
|
|
379
|
-
const [password, setPassword] = useState('')
|
|
380
|
-
|
|
381
|
-
return (
|
|
382
|
-
<div className="space-y-2">
|
|
383
|
-
<input
|
|
384
|
-
type="password"
|
|
385
|
-
value={password}
|
|
386
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
387
|
-
className="w-full border rounded p-2"
|
|
388
|
-
/>
|
|
389
|
-
|
|
390
|
-
<PasswordLevel
|
|
391
|
-
value={password}
|
|
392
|
-
className="!rounded-[8px] !p-[6px] !gap-[6px]"
|
|
393
|
-
/>
|
|
394
|
-
</div>
|
|
395
|
-
)
|
|
396
|
-
}
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
### Animated Feedback
|
|
400
|
-
|
|
401
|
-
```typescript
|
|
402
|
-
import { PasswordLevel } from "@/components/PasswordLevel";
|
|
403
|
-
import { InputField } from "@/components/InputField";
|
|
404
|
-
import { useState } from 'react'
|
|
405
|
-
|
|
406
|
-
function AnimatedFeedback() {
|
|
407
|
-
const [password, setPassword] = useState('')
|
|
408
|
-
|
|
409
|
-
const getLevel = (pwd: string) => {
|
|
410
|
-
let level = 0
|
|
411
|
-
if (pwd.length >= 6) level++
|
|
412
|
-
if (/[!@#$%^&*(),.?":{}|<>]/.test(pwd)) level++
|
|
413
|
-
if (/[A-Z]/.test(pwd)) level++
|
|
414
|
-
return level
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
const level = getLevel(password)
|
|
418
|
-
|
|
419
|
-
const messages = [
|
|
420
|
-
'Enter a password',
|
|
421
|
-
'Weak - Add more characters',
|
|
422
|
-
'Medium - Almost there!',
|
|
423
|
-
'Strong - Great password!',
|
|
424
|
-
]
|
|
425
|
-
|
|
426
|
-
return (
|
|
427
|
-
<div className="space-y-2">
|
|
428
|
-
<InputField
|
|
429
|
-
type="password"
|
|
430
|
-
value={password}
|
|
431
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
432
|
-
label="Password"
|
|
433
|
-
/>
|
|
434
|
-
|
|
435
|
-
<PasswordLevel value={password} />
|
|
436
|
-
|
|
437
|
-
<div className="transition-all duration-300">
|
|
438
|
-
<p
|
|
439
|
-
className={`text-sm font-medium ${
|
|
440
|
-
level === 0
|
|
441
|
-
? 'text-gray-400'
|
|
442
|
-
: level === 1
|
|
443
|
-
? 'text-red-500'
|
|
444
|
-
: level === 2
|
|
445
|
-
? 'text-yellow-500'
|
|
446
|
-
: 'text-green-500'
|
|
447
|
-
}`}
|
|
448
|
-
>
|
|
449
|
-
{messages[level]}
|
|
450
|
-
</p>
|
|
451
|
-
</div>
|
|
452
|
-
</div>
|
|
453
|
-
)
|
|
454
|
-
}
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
## API Reference
|
|
458
|
-
|
|
459
|
-
### PasswordLevel Props
|
|
460
|
-
|
|
461
|
-
Extends all standard HTML div attributes.
|
|
462
|
-
|
|
463
|
-
| Prop | Type | Default | Description |
|
|
464
|
-
|------|------|---------|-------------|
|
|
465
|
-
| `value` | `string` | **required** | Password string to evaluate |
|
|
466
|
-
| `theme` | `'light' \| 'dark' \| 'default'` | - | Theme variant |
|
|
467
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
468
|
-
| ...HTMLDivElement | - | - | All div attributes |
|
|
469
|
-
|
|
470
|
-
### Strength Calculation
|
|
471
|
-
|
|
472
|
-
Password strength is calculated based on three criteria:
|
|
473
|
-
|
|
474
|
-
| Level | Criteria | Color |
|
|
475
|
-
|-------|----------|-------|
|
|
476
|
-
| 1 | Length ≥ 6 characters | Red (Weak) |
|
|
477
|
-
| 2 | + Contains special character | Yellow (Medium) |
|
|
478
|
-
| 3 | + Contains uppercase letter | Green (Strong) |
|
|
479
|
-
|
|
480
|
-
### Special Characters
|
|
481
|
-
|
|
482
|
-
The following characters count as special characters:
|
|
483
|
-
|
|
484
|
-
```
|
|
485
|
-
! @ # $ % ^ & * ( ) , . ? " : { } | < >
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
## TypeScript
|
|
489
|
-
|
|
490
|
-
### Full Type Definitions
|
|
491
|
-
|
|
492
|
-
```typescript
|
|
493
|
-
import { HTMLAttributes } from 'react'
|
|
494
|
-
|
|
495
|
-
interface PasswordLevelProps extends HTMLAttributes<HTMLDivElement> {
|
|
496
|
-
value: string
|
|
497
|
-
theme?: 'light' | 'dark' | 'default'
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
export const PasswordLevel: React.FC<PasswordLevelProps>
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
### Helper Functions
|
|
504
|
-
|
|
505
|
-
```typescript
|
|
506
|
-
// Calculate password level
|
|
507
|
-
function getPasswordLevel(password: string): number {
|
|
508
|
-
const symbolRegex = /[!@#$%^&*(),.?":{}|<>]/
|
|
509
|
-
const uppercaseRegex = /[A-Z]/
|
|
510
|
-
|
|
511
|
-
let level = 0
|
|
512
|
-
if (password.length >= 6) level++
|
|
513
|
-
if (symbolRegex.test(password)) level++
|
|
514
|
-
if (uppercaseRegex.test(password)) level++
|
|
515
|
-
|
|
516
|
-
return level
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// Get strength label
|
|
520
|
-
function getPasswordStrength(password: string): 'weak' | 'medium' | 'strong' | 'none' {
|
|
521
|
-
const level = getPasswordLevel(password)
|
|
522
|
-
if (level === 0) return 'none'
|
|
523
|
-
if (level === 1) return 'weak'
|
|
524
|
-
if (level === 2) return 'medium'
|
|
525
|
-
return 'strong'
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// Check if password is acceptable
|
|
529
|
-
function isPasswordAcceptable(password: string, minLevel: number = 2): boolean {
|
|
530
|
-
return getPasswordLevel(password) >= minLevel
|
|
531
|
-
}
|
|
532
|
-
```
|
|
533
|
-
|
|
534
|
-
## Common Patterns
|
|
535
|
-
|
|
536
|
-
### Validation Hook
|
|
537
|
-
|
|
538
|
-
```typescript
|
|
539
|
-
import { PasswordLevel } from "@/components/PasswordLevel";
|
|
540
|
-
import { useState, useMemo } from 'react'
|
|
541
|
-
|
|
542
|
-
function usePasswordValidation(password: string) {
|
|
543
|
-
const validation = useMemo(() => {
|
|
544
|
-
const symbolRegex = /[!@#$%^&*(),.?":{}|<>]/
|
|
545
|
-
const uppercaseRegex = /[A-Z]/
|
|
546
|
-
const numberRegex = /[0-9]/
|
|
547
|
-
|
|
548
|
-
return {
|
|
549
|
-
hasMinLength: password.length >= 6,
|
|
550
|
-
hasSymbol: symbolRegex.test(password),
|
|
551
|
-
hasUppercase: uppercaseRegex.test(password),
|
|
552
|
-
hasNumber: numberRegex.test(password),
|
|
553
|
-
level: [
|
|
554
|
-
password.length >= 6,
|
|
555
|
-
symbolRegex.test(password),
|
|
556
|
-
uppercaseRegex.test(password),
|
|
557
|
-
].filter(Boolean).length,
|
|
558
|
-
isStrong: password.length >= 6 && symbolRegex.test(password) && uppercaseRegex.test(password),
|
|
559
|
-
}
|
|
560
|
-
}, [password])
|
|
561
|
-
|
|
562
|
-
return validation
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
// Usage
|
|
566
|
-
function ValidatedPasswordField() {
|
|
567
|
-
const [password, setPassword] = useState('')
|
|
568
|
-
const validation = usePasswordValidation(password)
|
|
569
|
-
|
|
570
|
-
return (
|
|
571
|
-
<div className="space-y-2">
|
|
572
|
-
<InputField
|
|
573
|
-
type="password"
|
|
574
|
-
value={password}
|
|
575
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
576
|
-
label="Password"
|
|
577
|
-
/>
|
|
578
|
-
<PasswordLevel value={password} />
|
|
579
|
-
|
|
580
|
-
<div className="text-xs space-y-1">
|
|
581
|
-
<p className={validation.hasMinLength ? 'text-green-600' : 'text-gray-500'}>
|
|
582
|
-
✓ At least 6 characters
|
|
583
|
-
</p>
|
|
584
|
-
<p className={validation.hasSymbol ? 'text-green-600' : 'text-gray-500'}>
|
|
585
|
-
✓ Special character
|
|
586
|
-
</p>
|
|
587
|
-
<p className={validation.hasUppercase ? 'text-green-600' : 'text-gray-500'}>
|
|
588
|
-
✓ Uppercase letter
|
|
589
|
-
</p>
|
|
590
|
-
</div>
|
|
591
|
-
</div>
|
|
592
|
-
)
|
|
593
|
-
}
|
|
594
|
-
```
|
|
595
|
-
|
|
596
|
-
### Password Generator
|
|
597
|
-
|
|
598
|
-
```typescript
|
|
599
|
-
import { PasswordLevel } from "@/components/PasswordLevel";
|
|
600
|
-
import { Button } from "@/components/Button";
|
|
601
|
-
import { InputField } from "@/components/InputField";
|
|
602
|
-
import { useState } from 'react'
|
|
603
|
-
|
|
604
|
-
function PasswordGenerator() {
|
|
605
|
-
const [password, setPassword] = useState('')
|
|
606
|
-
|
|
607
|
-
const generatePassword = () => {
|
|
608
|
-
const uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
609
|
-
const lowercase = 'abcdefghijklmnopqrstuvwxyz'
|
|
610
|
-
const numbers = '0123456789'
|
|
611
|
-
const symbols = '!@#$%^&*(),.?":{}|<>'
|
|
612
|
-
const all = uppercase + lowercase + numbers + symbols
|
|
613
|
-
|
|
614
|
-
let pwd = ''
|
|
615
|
-
// Ensure at least one of each required type
|
|
616
|
-
pwd += uppercase[Math.floor(Math.random() * uppercase.length)]
|
|
617
|
-
pwd += symbols[Math.floor(Math.random() * symbols.length)]
|
|
618
|
-
|
|
619
|
-
// Fill remaining characters
|
|
620
|
-
for (let i = 0; i < 10; i++) {
|
|
621
|
-
pwd += all[Math.floor(Math.random() * all.length)]
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// Shuffle
|
|
625
|
-
pwd = pwd.split('').sort(() => Math.random() - 0.5).join('')
|
|
626
|
-
setPassword(pwd)
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
return (
|
|
630
|
-
<div className="space-y-2">
|
|
631
|
-
<InputField
|
|
632
|
-
type="text"
|
|
633
|
-
value={password}
|
|
634
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
635
|
-
label="Password"
|
|
636
|
-
childrenSide={
|
|
637
|
-
<Button onClick={generatePassword} size="S">
|
|
638
|
-
Generate
|
|
639
|
-
</Button>
|
|
640
|
-
}
|
|
641
|
-
/>
|
|
642
|
-
<PasswordLevel value={password} />
|
|
643
|
-
</div>
|
|
644
|
-
)
|
|
645
|
-
}
|
|
646
|
-
```
|
|
647
|
-
|
|
648
|
-
## Testing
|
|
649
|
-
|
|
650
|
-
### Unit Test Examples
|
|
651
|
-
|
|
652
|
-
```typescript
|
|
653
|
-
import { render, screen } from '@testing-library/react'
|
|
654
|
-
import { PasswordLevel } from "@/components/PasswordLevel";
|
|
655
|
-
|
|
656
|
-
describe('PasswordLevel', () => {
|
|
657
|
-
it('renders three bars', () => {
|
|
658
|
-
const { container } = render(<PasswordLevel value="" />)
|
|
659
|
-
|
|
660
|
-
const bars = container.querySelectorAll('.h-\\[4px\\]')
|
|
661
|
-
expect(bars).toHaveLength(3)
|
|
662
|
-
})
|
|
663
|
-
|
|
664
|
-
it('shows weak password (level 1)', () => {
|
|
665
|
-
const { container } = render(<PasswordLevel value="password" />)
|
|
666
|
-
|
|
667
|
-
const bars = container.querySelectorAll('[class*="bg-border-presentation-state-negative"]')
|
|
668
|
-
expect(bars).toHaveLength(1) // Only first bar filled
|
|
669
|
-
})
|
|
670
|
-
|
|
671
|
-
it('shows medium password (level 2)', () => {
|
|
672
|
-
const { container } = render(<PasswordLevel value="password!" />)
|
|
673
|
-
|
|
674
|
-
const negativeBars = container.querySelectorAll('[class*="negative"]')
|
|
675
|
-
const warningBars = container.querySelectorAll('[class*="warning"]')
|
|
676
|
-
|
|
677
|
-
expect(negativeBars).toHaveLength(1) // First bar
|
|
678
|
-
expect(warningBars).toHaveLength(1) // Second bar
|
|
679
|
-
})
|
|
680
|
-
|
|
681
|
-
it('shows strong password (level 3)', () => {
|
|
682
|
-
const { container } = render(<PasswordLevel value="Password!123" />)
|
|
683
|
-
|
|
684
|
-
const negativeBars = container.querySelectorAll('[class*="negative"]')
|
|
685
|
-
const warningBars = container.querySelectorAll('[class*="warning"]')
|
|
686
|
-
const successBars = container.querySelectorAll('[class*="success"]')
|
|
687
|
-
|
|
688
|
-
expect(negativeBars).toHaveLength(1) // First bar
|
|
689
|
-
expect(warningBars).toHaveLength(1) // Second bar
|
|
690
|
-
expect(successBars).toHaveLength(1) // Third bar
|
|
691
|
-
})
|
|
692
|
-
|
|
693
|
-
it('updates level when value changes', () => {
|
|
694
|
-
const { rerender, container } = render(<PasswordLevel value="pass" />)
|
|
695
|
-
|
|
696
|
-
let bars = container.querySelectorAll('[class*="negative"]')
|
|
697
|
-
expect(bars).toHaveLength(0) // Too short
|
|
698
|
-
|
|
699
|
-
rerender(<PasswordLevel value="password" />)
|
|
700
|
-
bars = container.querySelectorAll('[class*="negative"]')
|
|
701
|
-
expect(bars).toHaveLength(1) // Level 1
|
|
702
|
-
|
|
703
|
-
rerender(<PasswordLevel value="Password!123" />)
|
|
704
|
-
const successBars = container.querySelectorAll('[class*="success"]')
|
|
705
|
-
expect(successBars).toHaveLength(1) // Level 3
|
|
706
|
-
})
|
|
707
|
-
|
|
708
|
-
it('applies theme correctly', () => {
|
|
709
|
-
const { container } = render(
|
|
710
|
-
<PasswordLevel value="test" theme="dark" />
|
|
711
|
-
)
|
|
712
|
-
|
|
713
|
-
const element = container.querySelector('[data-theme="dark"]')
|
|
714
|
-
expect(element).toBeInTheDocument()
|
|
715
|
-
})
|
|
716
|
-
})
|
|
717
|
-
```
|
|
718
|
-
|
|
719
|
-
## Accessibility
|
|
720
|
-
|
|
721
|
-
- **Visual Indicators**: Color-coded bars (red, yellow, green)
|
|
722
|
-
- **Color Independence**: Always include text labels alongside colors
|
|
723
|
-
- **Progressive Enhancement**: Works without JavaScript (static display)
|
|
724
|
-
- **Screen Readers**: Add `aria-label` or `aria-describedby` for context
|
|
725
|
-
|
|
726
|
-
### Accessibility Best Practices
|
|
727
|
-
|
|
728
|
-
```typescript
|
|
729
|
-
// Provide text alternative
|
|
730
|
-
<div>
|
|
731
|
-
<PasswordLevel value={password} aria-label={`Password strength: ${getStrength(password)}`} />
|
|
732
|
-
<p className="sr-only">
|
|
733
|
-
Password strength: {getStrength(password)}
|
|
734
|
-
</p>
|
|
735
|
-
</div>
|
|
736
|
-
|
|
737
|
-
// Link with description
|
|
738
|
-
<div>
|
|
739
|
-
<InputField
|
|
740
|
-
id="password-input"
|
|
741
|
-
type="password"
|
|
742
|
-
aria-describedby="password-strength"
|
|
743
|
-
/>
|
|
744
|
-
<div id="password-strength">
|
|
745
|
-
<PasswordLevel value={password} />
|
|
746
|
-
<p className="text-sm">Strength: {getStrength(password)}</p>
|
|
747
|
-
</div>
|
|
748
|
-
</div>
|
|
749
|
-
```
|
|
750
|
-
|
|
751
|
-
## Performance
|
|
752
|
-
|
|
753
|
-
| Metric | Value |
|
|
754
|
-
|--------|-------|
|
|
755
|
-
| Bundle size (minified) | ~1kb |
|
|
756
|
-
| Bundle size (gzipped) | ~0.5kb |
|
|
757
|
-
| Dependencies | None |
|
|
758
|
-
| Re-renders | Only on value change |
|
|
759
|
-
| Calculation complexity | O(n) where n is password length |
|
|
760
|
-
| Tree-shakeable | ✅ |
|
|
761
|
-
|
|
762
|
-
### Performance Tips
|
|
763
|
-
|
|
764
|
-
1. **Debounce for real-time validation**: Avoid excessive calculations
|
|
765
|
-
```typescript
|
|
766
|
-
const debouncedPassword = useDebounce(password, 300)
|
|
767
|
-
<PasswordLevel value={debouncedPassword} />
|
|
768
|
-
```
|
|
769
|
-
|
|
770
|
-
2. **Memoize calculations**: Cache strength calculations
|
|
771
|
-
```typescript
|
|
772
|
-
const level = useMemo(() => getPasswordLevel(password), [password])
|
|
773
|
-
```
|
|
774
|
-
|
|
775
|
-
## Styling
|
|
776
|
-
|
|
777
|
-
### Default Styling
|
|
778
|
-
|
|
779
|
-
- Container: Rounded borders, grid layout with 3 columns
|
|
780
|
-
- Bars: 4px height, rounded, with color transitions
|
|
781
|
-
- Colors:
|
|
782
|
-
- Level 0: Gray (secondary border)
|
|
783
|
-
- Level 1: Red (negative state)
|
|
784
|
-
- Level 2: Yellow (warning state)
|
|
785
|
-
- Level 3: Green (success state)
|
|
786
|
-
- Animation: 300ms ease-in-out transitions
|
|
787
|
-
|
|
788
|
-
## Best Practices
|
|
789
|
-
|
|
790
|
-
1. **Always show with password input**: Provide immediate feedback
|
|
791
|
-
2. **Add text labels**: Don't rely solely on colors
|
|
792
|
-
3. **Set minimum requirements**: Enforce at least level 2 for important accounts
|
|
793
|
-
4. **Provide helpful hints**: Tell users what's missing
|
|
794
|
-
5. **Test all levels**: Ensure all three states display correctly
|
|
795
|
-
6. **Consider additional criteria**: Numbers, lowercase, special patterns
|
|
796
|
-
7. **Validate on submit**: Client-side indicator + server-side validation
|
|
797
|
-
|
|
798
|
-
## Related Components
|
|
799
|
-
|
|
800
|
-
- [InputField](./input-field.md) - Input field for password entry
|
|
801
|
-
- [FieldHint](./field-hint.md) - Inline validation messages
|
|
802
|
-
- [Toast](./toast.md) - Success/error notifications
|
|
803
|
-
- [Button](./button.md) - Submit buttons with disabled states
|