torch-glare 2.5.6 → 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 -403
- package/apps/lib/components/Breadcrumb.tsx +0 -280
- package/apps/lib/components/Button.tsx +0 -325
- 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 -395
- 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 -88
- 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 -72
- package/apps/lib/components/DataViews/states.tsx +0 -73
- 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 -451
- package/apps/lib/components/DropdownMenu.tsx +0 -554
- package/apps/lib/components/FieldHint.tsx +0 -69
- package/apps/lib/components/Form.tsx +0 -182
- package/apps/lib/components/FormBuilder/context.ts +0 -85
- 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 -168
- 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 -114
- 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 -67
- package/apps/lib/components/FormBuilder/types.ts +0 -316
- package/apps/lib/components/FormRenderer/FormDrawer.tsx +0 -250
- package/apps/lib/components/FormRenderer/detail.tsx +0 -256
- package/apps/lib/components/FormRenderer/form-renderer.tsx +0 -358
- package/apps/lib/components/FormRenderer/header.tsx +0 -88
- package/apps/lib/components/FormRenderer/index.ts +0 -17
- package/apps/lib/components/FormRenderer/notch-action.tsx +0 -64
- package/apps/lib/components/FormRenderer/section.tsx +0 -39
- package/apps/lib/components/FormRenderer/stepper.tsx +0 -393
- package/apps/lib/components/FormRenderer/types.ts +0 -115
- 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 -168
- 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 -255
- 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 -597
- 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 -61
- package/apps/lib/hooks/useClickOutside.ts +0 -27
- package/apps/lib/hooks/useDragDrop.tsx +0 -365
- package/apps/lib/hooks/useHtmlDir.ts +0 -31
- 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 -300
- 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 -1217
- 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/scroller.ts +0 -26
- 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 -738
- 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 -1521
- package/docs/components/data-views/migration.md +0 -81
- 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 -284
- package/docs/components/form-renderer.md +0 -396
- 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 -483
- 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 -60
- 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 -1523
- 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 -726
- 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,696 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: SpinLoading
|
|
3
|
-
description: Animated loading spinner with theme variants and customizable sizes for indicating loading states
|
|
4
|
-
group: Feedback & Status
|
|
5
|
-
keywords: [loading, spinner, loader, animation, spin, progress]
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# SpinLoading
|
|
9
|
-
|
|
10
|
-
> An animated loading spinner component with light/dark theme variants and three size options. Features a smooth rotating gradient ring with support for custom content in the center.
|
|
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 SpinLoading
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
`add` also copies any components, hooks, and utilities that `SpinLoading` 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 { SpinLoading } from "@/components/SpinLoading";
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Quick Examples
|
|
33
|
-
|
|
34
|
-
### Basic Usage
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
38
|
-
|
|
39
|
-
function Example() {
|
|
40
|
-
return <SpinLoading />
|
|
41
|
-
}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### With Different Sizes
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
48
|
-
|
|
49
|
-
function SizesExample() {
|
|
50
|
-
return (
|
|
51
|
-
<div className="space-y-4">
|
|
52
|
-
<SpinLoading size="S" />
|
|
53
|
-
<SpinLoading size="M" />
|
|
54
|
-
<SpinLoading size="L" />
|
|
55
|
-
</div>
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Dark Theme
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
64
|
-
|
|
65
|
-
function DarkTheme() {
|
|
66
|
-
return (
|
|
67
|
-
<div className="bg-black p-8">
|
|
68
|
-
<SpinLoading theme="dark" size="M" />
|
|
69
|
-
</div>
|
|
70
|
-
)
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### Light Theme
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
77
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
78
|
-
|
|
79
|
-
function LightTheme() {
|
|
80
|
-
return (
|
|
81
|
-
<div className="bg-white p-8">
|
|
82
|
-
<SpinLoading theme="light" size="M" />
|
|
83
|
-
</div>
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### With Center Content
|
|
89
|
-
|
|
90
|
-
```typescript
|
|
91
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
92
|
-
|
|
93
|
-
function WithContent() {
|
|
94
|
-
return (
|
|
95
|
-
<SpinLoading size="M">
|
|
96
|
-
<div className="text-center">
|
|
97
|
-
<p className="text-sm font-medium">Loading...</p>
|
|
98
|
-
<p className="text-xs text-gray-500">Please wait</p>
|
|
99
|
-
</div>
|
|
100
|
-
</SpinLoading>
|
|
101
|
-
)
|
|
102
|
-
}
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### With Percentage
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
109
|
-
import { useState, useEffect } from 'react'
|
|
110
|
-
|
|
111
|
-
function PercentageLoader() {
|
|
112
|
-
const [progress, setProgress] = useState(0)
|
|
113
|
-
|
|
114
|
-
useEffect(() => {
|
|
115
|
-
const interval = setInterval(() => {
|
|
116
|
-
setProgress(p => (p >= 100 ? 0 : p + 10))
|
|
117
|
-
}, 500)
|
|
118
|
-
|
|
119
|
-
return () => clearInterval(interval)
|
|
120
|
-
}, [])
|
|
121
|
-
|
|
122
|
-
return (
|
|
123
|
-
<SpinLoading size="M">
|
|
124
|
-
<div className="text-center">
|
|
125
|
-
<p className="text-2xl font-bold">{progress}%</p>
|
|
126
|
-
</div>
|
|
127
|
-
</SpinLoading>
|
|
128
|
-
)
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### Loading Overlay
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
136
|
-
|
|
137
|
-
function LoadingOverlay({ loading, children }: { loading: boolean; children: React.ReactNode }) {
|
|
138
|
-
return (
|
|
139
|
-
<div className="relative">
|
|
140
|
-
{children}
|
|
141
|
-
|
|
142
|
-
{loading && (
|
|
143
|
-
<div className="absolute inset-0 bg-black/50 flex items-center justify-center">
|
|
144
|
-
<SpinLoading size="M" theme="dark" />
|
|
145
|
-
</div>
|
|
146
|
-
)}
|
|
147
|
-
</div>
|
|
148
|
-
)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Usage
|
|
152
|
-
function App() {
|
|
153
|
-
const [loading, setLoading] = useState(false)
|
|
154
|
-
|
|
155
|
-
return (
|
|
156
|
-
<LoadingOverlay loading={loading}>
|
|
157
|
-
<div className="p-8">
|
|
158
|
-
<h1>Content</h1>
|
|
159
|
-
<button onClick={() => setLoading(!loading)}>
|
|
160
|
-
Toggle Loading
|
|
161
|
-
</button>
|
|
162
|
-
</div>
|
|
163
|
-
</LoadingOverlay>
|
|
164
|
-
)
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
### Full Page Loading
|
|
169
|
-
|
|
170
|
-
```typescript
|
|
171
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
172
|
-
|
|
173
|
-
function FullPageLoading() {
|
|
174
|
-
return (
|
|
175
|
-
<div className="fixed inset-0 flex items-center justify-center bg-white">
|
|
176
|
-
<SpinLoading size="L" theme="light">
|
|
177
|
-
<div className="text-center">
|
|
178
|
-
<p className="text-lg font-semibold">Loading Application...</p>
|
|
179
|
-
</div>
|
|
180
|
-
</SpinLoading>
|
|
181
|
-
</div>
|
|
182
|
-
)
|
|
183
|
-
}
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### Data Loading State
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
190
|
-
import { useEffect, useState } from 'react'
|
|
191
|
-
|
|
192
|
-
function DataLoader() {
|
|
193
|
-
const [loading, setLoading] = useState(true)
|
|
194
|
-
const [data, setData] = useState(null)
|
|
195
|
-
|
|
196
|
-
useEffect(() => {
|
|
197
|
-
fetchData()
|
|
198
|
-
.then(setData)
|
|
199
|
-
.finally(() => setLoading(false))
|
|
200
|
-
}, [])
|
|
201
|
-
|
|
202
|
-
if (loading) {
|
|
203
|
-
return (
|
|
204
|
-
<div className="flex items-center justify-center p-12">
|
|
205
|
-
<SpinLoading size="M">
|
|
206
|
-
<p className="text-sm">Loading data...</p>
|
|
207
|
-
</SpinLoading>
|
|
208
|
-
</div>
|
|
209
|
-
)
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return <div>{/* Render data */}</div>
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### Card Loading
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
220
|
-
import { Card } from "@/components/Card";
|
|
221
|
-
|
|
222
|
-
function CardWithLoading({ loading }: { loading: boolean }) {
|
|
223
|
-
return (
|
|
224
|
-
<Card className="p-6">
|
|
225
|
-
{loading ? (
|
|
226
|
-
<div className="flex items-center justify-center py-8">
|
|
227
|
-
<SpinLoading size="S">
|
|
228
|
-
<p className="text-xs">Loading...</p>
|
|
229
|
-
</SpinLoading>
|
|
230
|
-
</div>
|
|
231
|
-
) : (
|
|
232
|
-
<div>
|
|
233
|
-
<h3>Card Content</h3>
|
|
234
|
-
<p>Content loaded successfully</p>
|
|
235
|
-
</div>
|
|
236
|
-
)}
|
|
237
|
-
</Card>
|
|
238
|
-
)
|
|
239
|
-
}
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
### Button Loading State
|
|
243
|
-
|
|
244
|
-
```typescript
|
|
245
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
246
|
-
import { Button } from "@/components/Button";
|
|
247
|
-
import { useState } from 'react'
|
|
248
|
-
|
|
249
|
-
function LoadingButton() {
|
|
250
|
-
const [loading, setLoading] = useState(false)
|
|
251
|
-
|
|
252
|
-
const handleClick = async () => {
|
|
253
|
-
setLoading(true)
|
|
254
|
-
try {
|
|
255
|
-
await performAction()
|
|
256
|
-
} finally {
|
|
257
|
-
setLoading(false)
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
return (
|
|
262
|
-
<Button
|
|
263
|
-
onClick={handleClick}
|
|
264
|
-
disabled={loading}
|
|
265
|
-
className="relative min-w-[120px]"
|
|
266
|
-
>
|
|
267
|
-
{loading ? (
|
|
268
|
-
<SpinLoading size="S" className="!w-[20px] !h-[20px]" />
|
|
269
|
-
) : (
|
|
270
|
-
'Submit'
|
|
271
|
-
)}
|
|
272
|
-
</Button>
|
|
273
|
-
)
|
|
274
|
-
}
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
## API Reference
|
|
278
|
-
|
|
279
|
-
### SpinLoading Props
|
|
280
|
-
|
|
281
|
-
Extends all standard HTML div attributes.
|
|
282
|
-
|
|
283
|
-
| Prop | Type | Default | Description |
|
|
284
|
-
|------|------|---------|-------------|
|
|
285
|
-
| `size` | `'S' \| 'M' \| 'L'` | `'M'` | Spinner size |
|
|
286
|
-
| `theme` | `'light' \| 'dark' \| 'default'` | `'light'` | Theme variant |
|
|
287
|
-
| `children` | `ReactNode` | - | Center content |
|
|
288
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
289
|
-
| ...HTMLDivElement | - | - | All div attributes |
|
|
290
|
-
|
|
291
|
-
### Size Specifications
|
|
292
|
-
|
|
293
|
-
| Size | Dimensions | Use Case |
|
|
294
|
-
|------|------------|----------|
|
|
295
|
-
| `S` | 200x200px | Small components, buttons |
|
|
296
|
-
| `M` | 400x400px | Cards, sections |
|
|
297
|
-
| `L` | 500x500px | Full page, major loading states |
|
|
298
|
-
|
|
299
|
-
## TypeScript
|
|
300
|
-
|
|
301
|
-
### Full Type Definitions
|
|
302
|
-
|
|
303
|
-
```typescript
|
|
304
|
-
import { HTMLAttributes } from 'react'
|
|
305
|
-
|
|
306
|
-
interface SpinLoadingProps extends HTMLAttributes<HTMLDivElement> {
|
|
307
|
-
size?: 'S' | 'M' | 'L'
|
|
308
|
-
theme?: 'light' | 'dark' | 'default'
|
|
309
|
-
children?: React.ReactNode
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
export const SpinLoading: React.FC<SpinLoadingProps>
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
### Usage with Types
|
|
316
|
-
|
|
317
|
-
```typescript
|
|
318
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
319
|
-
import { useState } from 'react'
|
|
320
|
-
|
|
321
|
-
function TypedExample() {
|
|
322
|
-
const [size, setSize] = useState<'S' | 'M' | 'L'>('M')
|
|
323
|
-
const [theme, setTheme] = useState<'light' | 'dark'>('light')
|
|
324
|
-
|
|
325
|
-
return (
|
|
326
|
-
<div>
|
|
327
|
-
<SpinLoading
|
|
328
|
-
size={size}
|
|
329
|
-
theme={theme}
|
|
330
|
-
aria-label="Loading content"
|
|
331
|
-
>
|
|
332
|
-
<p>Loading...</p>
|
|
333
|
-
</SpinLoading>
|
|
334
|
-
|
|
335
|
-
<select onChange={(e) => setSize(e.target.value as 'S' | 'M' | 'L')}>
|
|
336
|
-
<option value="S">Small</option>
|
|
337
|
-
<option value="M">Medium</option>
|
|
338
|
-
<option value="L">Large</option>
|
|
339
|
-
</select>
|
|
340
|
-
</div>
|
|
341
|
-
)
|
|
342
|
-
}
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
## Common Patterns
|
|
346
|
-
|
|
347
|
-
### Async Operation Wrapper
|
|
348
|
-
|
|
349
|
-
```typescript
|
|
350
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
351
|
-
import { ReactNode, useState } from 'react'
|
|
352
|
-
|
|
353
|
-
function AsyncWrapper({
|
|
354
|
-
children,
|
|
355
|
-
onLoad,
|
|
356
|
-
}: {
|
|
357
|
-
children: ReactNode
|
|
358
|
-
onLoad: () => Promise<void>
|
|
359
|
-
}) {
|
|
360
|
-
const [loading, setLoading] = useState(false)
|
|
361
|
-
const [error, setError] = useState<string>()
|
|
362
|
-
|
|
363
|
-
useEffect(() => {
|
|
364
|
-
setLoading(true)
|
|
365
|
-
onLoad()
|
|
366
|
-
.catch((err) => setError(err.message))
|
|
367
|
-
.finally(() => setLoading(false))
|
|
368
|
-
}, [onLoad])
|
|
369
|
-
|
|
370
|
-
if (loading) {
|
|
371
|
-
return (
|
|
372
|
-
<div className="flex items-center justify-center p-8">
|
|
373
|
-
<SpinLoading size="M" />
|
|
374
|
-
</div>
|
|
375
|
-
)
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
if (error) {
|
|
379
|
-
return <div className="text-red-500">Error: {error}</div>
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
return <>{children}</>
|
|
383
|
-
}
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
### Suspense-Like Fallback
|
|
387
|
-
|
|
388
|
-
```typescript
|
|
389
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
390
|
-
import { Suspense } from 'react'
|
|
391
|
-
|
|
392
|
-
function SuspenseWithLoading({ children }: { children: React.ReactNode }) {
|
|
393
|
-
return (
|
|
394
|
-
<Suspense
|
|
395
|
-
fallback={
|
|
396
|
-
<div className="flex items-center justify-center min-h-screen">
|
|
397
|
-
<SpinLoading size="L">
|
|
398
|
-
<p>Loading page...</p>
|
|
399
|
-
</SpinLoading>
|
|
400
|
-
</div>
|
|
401
|
-
}
|
|
402
|
-
>
|
|
403
|
-
{children}
|
|
404
|
-
</Suspense>
|
|
405
|
-
)
|
|
406
|
-
}
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
### Loading State Manager
|
|
410
|
-
|
|
411
|
-
```typescript
|
|
412
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
413
|
-
import { createContext, useContext, useState, ReactNode } from 'react'
|
|
414
|
-
|
|
415
|
-
const LoadingContext = createContext({
|
|
416
|
-
loading: false,
|
|
417
|
-
setLoading: (loading: boolean) => {},
|
|
418
|
-
})
|
|
419
|
-
|
|
420
|
-
export function LoadingProvider({ children }: { children: ReactNode }) {
|
|
421
|
-
const [loading, setLoading] = useState(false)
|
|
422
|
-
|
|
423
|
-
return (
|
|
424
|
-
<LoadingContext.Provider value={{ loading, setLoading }}>
|
|
425
|
-
{children}
|
|
426
|
-
{loading && (
|
|
427
|
-
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
|
428
|
-
<SpinLoading size="M" theme="dark" />
|
|
429
|
-
</div>
|
|
430
|
-
)}
|
|
431
|
-
</LoadingContext.Provider>
|
|
432
|
-
)
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
export const useLoading = () => useContext(LoadingContext)
|
|
436
|
-
|
|
437
|
-
// Usage
|
|
438
|
-
function MyComponent() {
|
|
439
|
-
const { setLoading } = useLoading()
|
|
440
|
-
|
|
441
|
-
const handleAction = async () => {
|
|
442
|
-
setLoading(true)
|
|
443
|
-
try {
|
|
444
|
-
await performAction()
|
|
445
|
-
} finally {
|
|
446
|
-
setLoading(false)
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
return <button onClick={handleAction}>Do Action</button>
|
|
451
|
-
}
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
## Testing
|
|
455
|
-
|
|
456
|
-
### Unit Test Examples
|
|
457
|
-
|
|
458
|
-
```typescript
|
|
459
|
-
import { render, screen } from '@testing-library/react'
|
|
460
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
461
|
-
|
|
462
|
-
describe('SpinLoading', () => {
|
|
463
|
-
it('renders spinner', () => {
|
|
464
|
-
render(<SpinLoading />)
|
|
465
|
-
|
|
466
|
-
const spinner = screen.getByRole('status', { hidden: true })
|
|
467
|
-
expect(spinner).toBeInTheDocument()
|
|
468
|
-
})
|
|
469
|
-
|
|
470
|
-
it('renders with different sizes', () => {
|
|
471
|
-
const { rerender } = render(<SpinLoading size="S" />)
|
|
472
|
-
let spinner = screen.getByRole('status', { hidden: true })
|
|
473
|
-
expect(spinner).toHaveClass('w-[200px]')
|
|
474
|
-
|
|
475
|
-
rerender(<SpinLoading size="M" />)
|
|
476
|
-
spinner = screen.getByRole('status', { hidden: true })
|
|
477
|
-
expect(spinner).toHaveClass('w-[400px]')
|
|
478
|
-
|
|
479
|
-
rerender(<SpinLoading size="L" />)
|
|
480
|
-
spinner = screen.getByRole('status', { hidden: true })
|
|
481
|
-
expect(spinner).toHaveClass('w-[500px]')
|
|
482
|
-
})
|
|
483
|
-
|
|
484
|
-
it('renders children in center', () => {
|
|
485
|
-
render(
|
|
486
|
-
<SpinLoading>
|
|
487
|
-
<p>Loading text</p>
|
|
488
|
-
</SpinLoading>
|
|
489
|
-
)
|
|
490
|
-
|
|
491
|
-
expect(screen.getByText('Loading text')).toBeInTheDocument()
|
|
492
|
-
})
|
|
493
|
-
|
|
494
|
-
it('applies theme correctly', () => {
|
|
495
|
-
const { rerender } = render(<SpinLoading theme="light" />)
|
|
496
|
-
|
|
497
|
-
let svg = document.querySelector('[data-theme="light"]')
|
|
498
|
-
expect(svg).toBeInTheDocument()
|
|
499
|
-
|
|
500
|
-
rerender(<SpinLoading theme="dark" />)
|
|
501
|
-
svg = document.querySelector('[data-theme="dark"]')
|
|
502
|
-
expect(svg).toBeInTheDocument()
|
|
503
|
-
})
|
|
504
|
-
|
|
505
|
-
it('applies custom className', () => {
|
|
506
|
-
render(<SpinLoading className="custom-class" />)
|
|
507
|
-
|
|
508
|
-
const spinner = screen.getByRole('status', { hidden: true })
|
|
509
|
-
expect(spinner).toHaveClass('custom-class')
|
|
510
|
-
})
|
|
511
|
-
|
|
512
|
-
it('has spinning animation', () => {
|
|
513
|
-
render(<SpinLoading />)
|
|
514
|
-
|
|
515
|
-
const svg = document.querySelector('svg')
|
|
516
|
-
expect(svg).toHaveClass('animate-spin')
|
|
517
|
-
})
|
|
518
|
-
})
|
|
519
|
-
```
|
|
520
|
-
|
|
521
|
-
### Integration Test
|
|
522
|
-
|
|
523
|
-
```typescript
|
|
524
|
-
import { render, screen, waitFor } from '@testing-library/react'
|
|
525
|
-
import { SpinLoading } from "@/components/SpinLoading";
|
|
526
|
-
|
|
527
|
-
describe('SpinLoading Integration', () => {
|
|
528
|
-
it('shows loading state during async operation', async () => {
|
|
529
|
-
function TestComponent() {
|
|
530
|
-
const [loading, setLoading] = useState(true)
|
|
531
|
-
|
|
532
|
-
useEffect(() => {
|
|
533
|
-
setTimeout(() => setLoading(false), 1000)
|
|
534
|
-
}, [])
|
|
535
|
-
|
|
536
|
-
return loading ? (
|
|
537
|
-
<SpinLoading>
|
|
538
|
-
<p>Loading...</p>
|
|
539
|
-
</SpinLoading>
|
|
540
|
-
) : (
|
|
541
|
-
<p>Content loaded</p>
|
|
542
|
-
)
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
render(<TestComponent />)
|
|
546
|
-
|
|
547
|
-
expect(screen.getByText('Loading...')).toBeInTheDocument()
|
|
548
|
-
|
|
549
|
-
await waitFor(() => {
|
|
550
|
-
expect(screen.getByText('Content loaded')).toBeInTheDocument()
|
|
551
|
-
}, { timeout: 1500 })
|
|
552
|
-
})
|
|
553
|
-
})
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
## Accessibility
|
|
557
|
-
|
|
558
|
-
- **ARIA Role**: Implicit `role="status"` for screen readers
|
|
559
|
-
- **ARIA Label**: Add `aria-label` for context
|
|
560
|
-
- **Keyboard**: Not interactive, doesn't affect keyboard navigation
|
|
561
|
-
- **Focus**: Does not trap or steal focus
|
|
562
|
-
- **Animation**: Uses `prefers-reduced-motion` media query support
|
|
563
|
-
- **Color Contrast**: Gradient provides sufficient visibility
|
|
564
|
-
|
|
565
|
-
### Accessibility Best Practices
|
|
566
|
-
|
|
567
|
-
```typescript
|
|
568
|
-
// Provide context with aria-label
|
|
569
|
-
<SpinLoading aria-label="Loading user profile" />
|
|
570
|
-
|
|
571
|
-
// Use with live region for updates
|
|
572
|
-
<div aria-live="polite" aria-busy={loading}>
|
|
573
|
-
{loading ? (
|
|
574
|
-
<SpinLoading>
|
|
575
|
-
<span className="sr-only">Loading content...</span>
|
|
576
|
-
</SpinLoading>
|
|
577
|
-
) : (
|
|
578
|
-
<Content />
|
|
579
|
-
)}
|
|
580
|
-
</div>
|
|
581
|
-
|
|
582
|
-
// Hide decorative content
|
|
583
|
-
<SpinLoading aria-hidden={false}>
|
|
584
|
-
<span>Loading...</span> {/* Announced by screen readers */}
|
|
585
|
-
</SpinLoading>
|
|
586
|
-
```
|
|
587
|
-
|
|
588
|
-
## Performance
|
|
589
|
-
|
|
590
|
-
| Metric | Value |
|
|
591
|
-
|--------|-------|
|
|
592
|
-
| Bundle size (minified) | ~4kb |
|
|
593
|
-
| Bundle size (gzipped) | ~1.5kb |
|
|
594
|
-
| Dependencies | None |
|
|
595
|
-
| Animation | CSS-only (60fps) |
|
|
596
|
-
| Repaints | Minimal (transform-only) |
|
|
597
|
-
| Tree-shakeable | ✅ |
|
|
598
|
-
|
|
599
|
-
### Performance Tips
|
|
600
|
-
|
|
601
|
-
1. **Use CSS animations**: Already optimized with `transform` only
|
|
602
|
-
2. **Avoid unnecessary re-renders**: Wrap in `memo` if parent re-renders frequently
|
|
603
|
-
```typescript
|
|
604
|
-
const MemoizedSpinner = memo(SpinLoading)
|
|
605
|
-
```
|
|
606
|
-
3. **Conditional rendering**: Only render when needed
|
|
607
|
-
```typescript
|
|
608
|
-
{loading && <SpinLoading />}
|
|
609
|
-
```
|
|
610
|
-
4. **Proper size selection**: Use smallest size that fits the use case
|
|
611
|
-
|
|
612
|
-
## Styling
|
|
613
|
-
|
|
614
|
-
### Custom Styles
|
|
615
|
-
|
|
616
|
-
```typescript
|
|
617
|
-
// Override size
|
|
618
|
-
<SpinLoading
|
|
619
|
-
size="M"
|
|
620
|
-
className="!w-[300px] !h-[300px]"
|
|
621
|
-
/>
|
|
622
|
-
|
|
623
|
-
// Custom colors (theme-specific)
|
|
624
|
-
<SpinLoading
|
|
625
|
-
theme="light"
|
|
626
|
-
style={{
|
|
627
|
-
'--spinner-primary': '#3b82f6',
|
|
628
|
-
'--spinner-secondary': '#1e40af',
|
|
629
|
-
} as React.CSSProperties}
|
|
630
|
-
/>
|
|
631
|
-
|
|
632
|
-
// With backdrop
|
|
633
|
-
<div className="relative">
|
|
634
|
-
<SpinLoading className="backdrop-blur-sm" />
|
|
635
|
-
</div>
|
|
636
|
-
```
|
|
637
|
-
|
|
638
|
-
### Default Styling
|
|
639
|
-
|
|
640
|
-
- **Light Theme**: Purple gradient (`#D500F9` to `#5317FF`)
|
|
641
|
-
- **Dark Theme**: Purple gradient (`#b900d8` to `#e75cff`)
|
|
642
|
-
- **Animation**: Infinite spin (2s duration)
|
|
643
|
-
- **Effects**: Gaussian blur filters for glow effect
|
|
644
|
-
- **Center Content**: Absolute positioning with transform
|
|
645
|
-
|
|
646
|
-
## Animation Details
|
|
647
|
-
|
|
648
|
-
### Spin Animation
|
|
649
|
-
|
|
650
|
-
```css
|
|
651
|
-
.animate-spin {
|
|
652
|
-
animation: spin 2s linear infinite;
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
@keyframes spin {
|
|
656
|
-
from {
|
|
657
|
-
transform: rotate(0deg);
|
|
658
|
-
}
|
|
659
|
-
to {
|
|
660
|
-
transform: rotate(360deg);
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
```
|
|
664
|
-
|
|
665
|
-
### Reduced Motion
|
|
666
|
-
|
|
667
|
-
```css
|
|
668
|
-
@media (prefers-reduced-motion: reduce) {
|
|
669
|
-
.animate-spin {
|
|
670
|
-
animation-duration: 4s; /* Slower */
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
## Best Practices
|
|
676
|
-
|
|
677
|
-
1. **Match size to context**: Use S for buttons, M for cards, L for pages
|
|
678
|
-
2. **Provide loading text**: Add children for screen reader context
|
|
679
|
-
```typescript
|
|
680
|
-
<SpinLoading><span className="sr-only">Loading...</span></SpinLoading>
|
|
681
|
-
```
|
|
682
|
-
3. **Use appropriate theme**: Match background (light on dark, dark on light)
|
|
683
|
-
4. **Show progress when possible**: Add percentage or status text
|
|
684
|
-
```typescript
|
|
685
|
-
<SpinLoading>{progress}%</SpinLoading>
|
|
686
|
-
```
|
|
687
|
-
5. **Don't block unnecessarily**: Use inline loading for small sections
|
|
688
|
-
6. **Timeout long operations**: Show error after reasonable time
|
|
689
|
-
7. **Test with reduced motion**: Ensure accessible for motion-sensitive users
|
|
690
|
-
|
|
691
|
-
## Related Components
|
|
692
|
-
|
|
693
|
-
- [Toast](./toast.md) - Toast notifications for feedback
|
|
694
|
-
- [FieldHint](./field-hint.md) - Inline field hints and alerts
|
|
695
|
-
- [Skeleton](./skeleton.md) - Content placeholder loading states
|
|
696
|
-
- [PasswordLevel](./password-level.md) - Password strength indicator
|