Compare commits

...

56 Commits

Author SHA1 Message Date
326ef4bf5e 0.0.9 2026-03-01 14:37:52 -05:00
0018e56938 Move some dependencies from peer dependencies 2026-03-01 14:18:32 -05:00
178f5c88e8 Add tests for checkbox 2026-02-28 16:37:25 -05:00
6c67604efc Add pill component 2026-02-26 23:10:08 -05:00
378dae159f Update sonarqube to receive test coverage 2026-02-26 21:41:18 -05:00
c55ce3ad77 Update drag and drop hover classes 2026-02-26 21:29:17 -05:00
434a27d90d Fix sonarqube issues 2026-02-26 21:18:37 -05:00
94f0f3ca13 Update various components 2026-02-26 21:03:21 -05:00
637b3a0c34 Update loading components 2026-02-24 22:16:46 -05:00
a61e7ce19a Update input components 2026-02-16 23:36:32 -05:00
da0db483aa Update import/exports to be more tree-shake friendly 2026-02-14 21:12:16 -05:00
61cceb41ba Updated progress bars 2026-02-14 17:19:22 -05:00
bcc5de6d7e Update message blocks 2026-02-14 17:10:48 -05:00
31c44d3d92 Update switches 2026-02-14 17:04:16 -05:00
1743ed6ce4 Update radio buttons 2026-02-14 16:55:54 -05:00
6c86fdd58b Update checkboxes 2026-02-14 16:40:57 -05:00
f224f3fa2c Update button components 2026-02-14 16:35:24 -05:00
aaa15b1cfc Update input components 2026-02-14 16:24:04 -05:00
f40845723d Update prop expansion on components 2026-02-12 21:30:04 -05:00
45ff84b106 Update message blocks 2026-02-12 21:29:48 -05:00
0bf4a6a08c Update switch with suggestions 2026-02-12 21:06:31 -05:00
1e8e0d3138 Update radio button with input suggestions 2026-02-12 20:59:24 -05:00
12ee0bc7ad Update radio list to align correctly 2026-02-12 19:49:19 -05:00
c4f1ccd869 Update checkboxes 2026-02-11 22:18:32 -05:00
ac042d0a4f Update vitest config 2026-02-10 22:24:53 -05:00
5abceb7bc7 Update themed component tests 2026-02-10 22:14:48 -05:00
2e54b81d8f Update themed components with refs and css 2026-02-10 21:09:36 -05:00
456feed128 Fix what the AI broke 2026-02-07 13:17:04 -05:00
dd052b0557 Updated button component accessibility 2026-02-06 19:45:58 -05:00
0bc94be283 Update to include sonarqube 2026-02-05 21:58:20 -05:00
f5f3078de7 Update dependencies 2026-01-07 14:10:13 -05:00
cfe6fc1a4e Add message block tests 2025-11-18 22:48:52 -05:00
91c8169e97 Added tests for buttons 2025-11-17 23:08:37 -05:00
7d9d0bdf73 Add test config 2025-11-17 21:59:15 -05:00
8567e67d1a Update dependency versions 2025-11-13 23:16:16 -05:00
ddf25232b4 Update component styling 2025-08-13 23:08:38 -04:00
3a76aed09b Update component definitions 2025-08-11 22:09:37 -04:00
ebe69de078 Add more missing components 2025-08-10 23:37:40 -04:00
7a31eade87 Fix broken types in imported library 2025-08-10 23:11:56 -04:00
4402edcfa0 Add components to index 2025-08-10 22:06:40 -04:00
f02a5ba8a3 Update deploy script 2025-08-10 22:01:24 -04:00
569c79d54f Add missing components 2025-08-10 21:58:16 -04:00
e4b91578e2 Added check for changes that haven't been committed 2025-08-09 23:14:02 -04:00
4120a6bac5 Fixed warnings in package 2025-08-09 22:56:59 -04:00
7078940ce3 Fix typo in script 2025-08-09 22:55:54 -04:00
35a8316ea8 Updated for deployment 2025-08-09 22:54:56 -04:00
f96c8e1067 Updated peerDependencies 2025-08-09 21:23:39 -04:00
b3803162b4 Complete configuration for build 2025-08-09 19:09:49 -04:00
689f446806 Updated some configs 2025-08-09 18:26:44 -04:00
46aa1e4dda Toaster component created 2025-08-09 16:39:58 -04:00
4e3c984125 Added progress component 2025-08-09 14:25:27 -04:00
e1b3000121 Temporal input components added 2025-07-31 22:23:00 -04:00
f6f77c9d42 Radio Button and Checkbox Created 2025-07-30 23:10:17 -04:00
cb8c2c23be Many inputs added 2025-07-20 23:33:21 -04:00
f84f0a0ebc Updated several input components 2025-07-19 23:58:16 -04:00
ea7ac27772 Created switches 2025-07-19 22:48:01 -04:00
262 changed files with 17712 additions and 2812 deletions

8
.gitignore vendored
View File

@@ -6,6 +6,10 @@ node_modules
dist dist
*.local *.local
.tanstack .tanstack
*.tgz
coverage
# Editor directories and files # Sonarqube
.vscode sonarBuild.sh
sonarBuild.ps1
.scannerwork

6
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"sonarlint.connectedMode.project": {
"connectionId": "mattrixwvSonarqube",
"projectKey": "MattrixwvReactComponents"
}
}

View File

@@ -1,69 +1,5 @@
# React + TypeScript + Vite # Mattrixwv React Components
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. [![Quality Gate Status](https://sonarqube.mattrixwv.com/api/project_badges/measure?project=MattrixwvReactComponents&metric=alert_status&token=sqb_e8b5362c32319b0612536e683cdbe2a77ec5bb32)](https://sonarqube.mattrixwv.com/dashboard?id=MattrixwvReactComponents)
Currently, two official plugins are available: Under Construction
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
export default tseslint.config([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
...tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
...tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
...tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default tseslint.config([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```

4
TODO.txt Normal file
View File

@@ -0,0 +1,4 @@
Inputs:
slider, multi-value slider
Move providers to context and components to providers

View File

@@ -1,37 +1,51 @@
import js from "@eslint/js"; import js from "@eslint/js";
import pluginRouter from "@tanstack/eslint-plugin-router"; import pluginRouter from "@tanstack/eslint-plugin-router";
import react from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks"; import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh"; import reactRefresh from "eslint-plugin-react-refresh";
import { globalIgnores } from "eslint/config"; import { defineConfig, globalIgnores } from "eslint/config";
import globals from "globals"; import globals from "globals";
import tseslint from "typescript-eslint"; import tseslint from "typescript-eslint";
export default tseslint.config([ export default defineConfig([
globalIgnores(['dist']), globalIgnores(['dist']),
{ {
files: ['**/*.{ts,tsx}'], files: ['**/*.{ts,tsx}'],
extends: [ extends: [
js.configs.recommended, js.configs.recommended,
react.configs.flat.recommended,
tseslint.configs.recommendedTypeChecked, tseslint.configs.recommendedTypeChecked,
reactHooks.configs['recommended-latest'], reactHooks.configs.flat["recommended-latest"],
reactRefresh.configs.vite, reactRefresh.configs.vite,
...pluginRouter.configs["flat/recommended"] pluginRouter.configs["flat/recommended"]
], ],
languageOptions: { languageOptions: {
ecmaVersion: 2020, ecmaVersion: 2022,
globals: globals.browser globals: globals.browser,
}, parserOptions: {
plugins: { project: [
"react-hooks": reactHooks, "./tsconfig.json",
"react-refresh": reactRefresh "./tsconfig.app.json",
"./tsconfig.lib.json",
"./tsconfig.node.json",
"./tsconfig.test.json"
],
tsconfigRootDir: import.meta.dirname
}
}, },
rules: { rules: {
...reactHooks.configs.recommended.rules, ...reactHooks.configs.recommended.rules,
"react-refresh/only-export-components": [ "react-refresh/only-export-components": [
"warn", "warn",
{ allowConstantExport: true } { allowConstantExport: true }
] ],
"react/react-in-jsx-scope": "off"
},
settings: {
react: {
version: "detect"
}
} }
} }
]); ]);

View File

@@ -1,26 +0,0 @@
import Button from "./button/Button";
import DangerButton from "./button/DangerButton";
import DarkButton from "./button/DarkButton";
import InfoButton from "./button/InfoButton";
import LightButton from "./button/LightButton";
import MoltenButton from "./button/MoltenButton";
import PrimaryButton from "./button/PrimaryButton";
import SecondaryButton from "./button/SecondaryButton";
import SuccessButton from "./button/SuccessButton";
import TertiaryButton from "./button/TertiaryButton";
import WarningButton from "./button/WarningButton";
export {
Button,
DangerButton,
DarkButton,
InfoButton,
LightButton,
MoltenButton,
PrimaryButton,
SecondaryButton,
SuccessButton,
TertiaryButton,
WarningButton
};

View File

@@ -1,30 +1,31 @@
import type { ButtonProps } from "$/types/Button"; import type { ButtonProps } from "$/types/ButtonTypes";
import clsx from "clsx"; import clsx from "clsx";
export default function Button(props: ButtonProps){
const { export default function Button({
className, className,
type="button",
rounding = "lg", rounding = "lg",
shape = "rectangle", shape = "rectangle",
size = "md", size = "md",
variant = "standard", variant = "standard",
disabled, disabled,
...buttonProps ...buttonProps
} = props; }: Readonly<ButtonProps>){
return ( return (
<button <button
{...buttonProps} data-testid="mattrixwv-button"
type={type}
disabled={disabled} disabled={disabled}
className={clsx( className={clsx(
className, className,
//Focus
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
//Rounding //Rounding
{ {
"rounded-none": rounding === "none",
"rounded-sm": rounding === "sm", "rounded-sm": rounding === "sm",
"rounded": rounding === "md", "rounded-md": rounding === "md",
"rounded-lg": rounding === "lg", "rounded-lg": rounding === "lg",
"rounded-full": rounding === "full" "rounded-full": rounding === "full"
}, },
@@ -49,9 +50,11 @@ export default function Button(props: ButtonProps){
}, },
//Disabled //Disabled
{ {
"cursor-pointer": !disabled "cursor-pointer": !disabled,
"cursor-not-allowed opacity-75": disabled
} }
)} )}
{...buttonProps}
/> />
); );
} }

View File

@@ -1,46 +1,45 @@
import type { ButtonProps } from "$/types/Button"; import type { ButtonProps } from "$/types/ButtonTypes";
import clsx from "clsx"; import clsx from "clsx";
import Button from "./Button"; import Button from "./Button";
export default function DangerButton(props: ButtonProps){
const { export default function DangerButton({
className, className,
variant = "standard", variant = "standard",
disabled disabled,
} = props; ...buttonProps
}: Readonly<ButtonProps>){
return ( return (
<Button <Button
{...props} data-testid="mattrixwv-danger-button"
variant={variant}
disabled={disabled}
className={clsx( className={clsx(
"transition duration-300", "transition duration-300",
className, className,
//Background //Background
{ {
"bg-red-600 hover:bg-red-700 active:bg-red-800": (variant === "standard") && (!disabled), "bg-danger active:bg-danger-dark": (variant === "standard") && (!disabled),
"bg-red-400/80": (variant === "standard") && (disabled), "bg-danger-light/80": (variant === "standard") && (disabled),
"bg-transparent": (variant === "outline" || variant === "icon"), "bg-transparent": (variant === "outline" || variant === "icon"),
"bg-transparent hover:bg-red-600 active:bg-red-700": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "bg-transparent hover:bg-danger active:bg-danger-dark": (variant === "ghost" || variant === "outline-ghost") && (!disabled),
"bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled) "bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
}, },
//Outline //Outline
{ {
"border-red-600 hover:border-red-700 active:border-red-800": (variant === "standard" || variant === "outline") && (!disabled), "border-danger active:border-danger-dark": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (!disabled),
"border-red-400/80": (variant === "standard" || variant === "outline") && (disabled), "border-danger-light/80": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (disabled)
"border-red-600 hover:border-red-600 active:border-red-700": (variant === "outline-ghost") && (!disabled),
"border-red-400/80 ": (variant === "outline-ghost") && (disabled)
}, },
//Text //Text
{ {
"text-white": variant === "standard", "text-white": variant === "standard",
"text-red-600 hover:text-red-700 active:text-red-800": (variant === "outline" || variant === "icon") && (!disabled), "text-danger active:text-danger-dark": (variant === "outline" || variant === "icon") && (!disabled),
"text-red-400/80": (variant === "outline" || variant === "icon") && (disabled), "text-danger-light/80": (variant === "outline" || variant === "icon" || variant === "ghost" || variant === "outline-ghost") && (disabled),
"text-red-600 hover:text-white active:text-white": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "text-danger hover:text-white active:text-white": (variant === "ghost" || variant === "outline-ghost") && (!disabled)
"text-red-400/80 ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
} }
)} )}
{...buttonProps}
/> />
); );
} }

View File

@@ -1,46 +1,44 @@
import type { ButtonProps } from "$/types/Button"; import type { ButtonProps } from "$/types/ButtonTypes";
import clsx from "clsx"; import clsx from "clsx";
import Button from "./Button"; import Button from "./Button";
export default function DarkButton(props: ButtonProps){ export default function DarkButton({
const {
className, className,
variant = "standard", variant = "standard",
disabled disabled,
} = props; ...buttonProps
}: Readonly<ButtonProps>){
return ( return (
<Button <Button
{...props} data-testid="mattrixwv-dark-button"
variant={variant}
disabled={disabled}
className={clsx( className={clsx(
"transition duration-300", "transition duration-300",
className, className,
//Background //Background
{ {
"bg-black hover:bg-neutral-700 active:bg-neutral-600": (variant === "standard") && (!disabled), "bg-dark active:bg-dark-mid": (variant === "standard") && (!disabled),
"bg-neutral-700/80": (variant === "standard") && (disabled), "bg-dark-mid/80": (variant === "standard") && (disabled),
"bg-transparent": (variant === "outline" || variant === "icon"), "bg-transparent": (variant === "outline" || variant === "icon"),
"bg-transparent hover:bg-black active:bg-neutral-700": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "bg-transparent hover:bg-dark active:bg-dark-mid": (variant === "ghost" || variant === "outline-ghost") && (!disabled),
"bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled) "bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
}, },
//Outline //Outline
{ {
"border-black hover:border-neutral-700 active:border-neutral-600": (variant === "standard" || variant === "outline") && (!disabled), "border-dark active:border-dark-mid": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (!disabled),
"border-neutral-700/80": (variant === "standard" || variant === "outline") && (disabled), "border-dark-mid/80": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (disabled)
"border-black hover:border-black active:border-neutral-700": (variant === "outline-ghost") && (!disabled),
"border-neutral-700/80 ": (variant === "outline-ghost") && (disabled)
}, },
//Text //Text
{ {
"text-white": variant === "standard", "text-white": variant === "standard",
"text-black hover:text-neutral-700 active:text-neutral-600": (variant === "outline" || variant === "icon") && (!disabled), "text-dark active:text-dark-mid": (variant === "outline" || variant === "icon") && (!disabled),
"text-neutral-700/80": (variant === "outline" || variant === "icon") && (disabled), "text-dark-mid/80": (variant === "outline" || variant === "icon" || variant === "ghost" || variant === "outline-ghost") && (disabled),
"text-black hover:text-white active:text-white": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "text-dark hover:text-white active:text-white": (variant === "ghost" || variant === "outline-ghost") && (!disabled)
"text-neutral-700/80 ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
} }
)} )}
{...buttonProps}
/> />
); );
} }

View File

@@ -1,46 +1,44 @@
import type { ButtonProps } from "$/types/Button"; import type { ButtonProps } from "$/types/ButtonTypes";
import clsx from "clsx"; import clsx from "clsx";
import Button from "./Button"; import Button from "./Button";
export default function InfoButton(props: ButtonProps){ export default function InfoButton({
const {
className, className,
variant = "standard", variant = "standard",
disabled disabled,
} = props; ...buttonProps
}: Readonly<ButtonProps>){
return ( return (
<Button <Button
{...props} data-testid="mattrixwv-info-button"
variant={variant}
disabled={disabled}
className={clsx( className={clsx(
"transition duration-300", "transition duration-300",
className, className,
//Background //Background
{ {
"bg-cyan-500 hover:bg-cyan-600 active:bg-cyan-700": (variant === "standard") && (!disabled), "bg-info active:bg-info-dark": (variant === "standard") && (!disabled),
"bg-sky-300/80": (variant === "standard") && (disabled), "bg-info-light/80": (variant === "standard") && (disabled),
"bg-transparent": (variant === "outline" || variant === "icon"), "bg-transparent": (variant === "outline" || variant === "icon"),
"bg-transparent hover:bg-cyan-500 active:bg-cyan-600": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "bg-transparent hover:bg-info active:bg-info-dark": (variant === "ghost" || variant === "outline-ghost") && (!disabled),
"bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled) "bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
}, },
//Outline //Outline
{ {
"border-cyan-500 hover:border-cyan-600 active:border-cyan-700": (variant === "standard" || variant === "outline") && (!disabled), "border-info active:border-info-dark": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (!disabled),
"border-sky-300/80": (variant === "standard" || variant === "outline") && (disabled), "border-info-light/80": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (disabled)
"border-cyan-500 hover:border-cyan-500 active:border-cyan-600": (variant === "outline-ghost") && (!disabled),
"border-sky-300/80 ": (variant === "outline-ghost") && (disabled)
}, },
//Text //Text
{ {
"text-black": variant === "standard", "text-black": variant === "standard",
"text-cyan-500 hover:text-cyan-600 active:text-cyan-700": (variant === "outline" || variant === "icon") && (!disabled), "text-info active:text-info-dark": (variant === "outline" || variant === "icon") && (!disabled),
"text-sky-300/80": (variant === "outline" || variant === "icon") && (disabled), "text-info-light/80": (variant === "outline" || variant === "icon" || variant === "ghost" || variant === "outline-ghost") && (disabled),
"text-cyan-500 hover:text-black active:text-black": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "text-info hover:text-black active:text-black": (variant === "ghost" || variant === "outline-ghost") && (!disabled)
"text-sky-300/80 ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
} }
)} )}
{...buttonProps}
/> />
); );
} }

View File

@@ -1,46 +1,44 @@
import type { ButtonProps } from "$/types/Button"; import type { ButtonProps } from "$/types/ButtonTypes";
import clsx from "clsx"; import clsx from "clsx";
import Button from "./Button"; import Button from "./Button";
export default function LightButton(props: ButtonProps){ export default function LightButton({
const {
className, className,
variant = "standard", variant = "standard",
disabled disabled,
} = props; ...buttonProps
}: Readonly<ButtonProps>){
return ( return (
<Button <Button
{...props} data-testid="mattrixwv-light-button"
variant={variant}
disabled={disabled}
className={clsx( className={clsx(
"transition duration-300", "transition duration-300",
className, className,
//Background //Background
{ {
"bg-white hover:bg-neutral-300 active:bg-neutral-400": (variant === "standard") && (!disabled), "bg-light active:bg-light-dark": (variant === "standard") && (!disabled),
"bg-neutral-400/80": (variant === "standard") && (disabled), "bg-light-light/80": (variant === "standard") && (disabled),
"bg-transparent": (variant === "outline" || variant === "icon"), "bg-transparent": (variant === "outline" || variant === "icon"),
"bg-transparent hover:bg-white active:bg-neutral-300": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "bg-transparent hover:bg-light active:bg-light-dark": (variant === "ghost" || variant === "outline-ghost") && (!disabled),
"bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled) "bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
}, },
//Outline //Outline
{ {
"border-white hover:border-neutral-300 active:border-neutral-400": (variant === "standard" || variant === "outline") && (!disabled), "border-light active:border-light-dark": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (!disabled),
"border-neutral-400/80": (variant === "standard" || variant === "outline") && (disabled), "border-light-light/80": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (disabled)
"border-white hover:border-white active:border-neutral-300": (variant === "outline-ghost") && (!disabled),
"border-neutral-400/80 ": (variant === "outline-ghost") && (disabled)
}, },
//Text //Text
{ {
"text-black": variant === "standard", "text-black": variant === "standard",
"text-white hover:text-neutral-300 active:text-neutral-400": (variant === "outline" || variant === "icon") && (!disabled), "text-light active:text-light-dark": (variant === "outline" || variant === "icon") && (!disabled),
"text-neutral-400/80": (variant === "outline" || variant === "icon") && (disabled), "text-light-light/80": (variant === "outline" || variant === "icon" || variant === "ghost" || variant === "outline-ghost") && (disabled),
"text-white hover:text-black active:text-black": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "text-light hover:text-black active:text-black": (variant === "ghost" || variant === "outline-ghost") && (!disabled)
"text-neutral-400/80 ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
} }
)} )}
{...buttonProps}
/> />
); );
} }

View File

@@ -1,46 +1,44 @@
import type { ButtonProps } from "$/types/Button"; import type { ButtonProps } from "$/types/ButtonTypes";
import clsx from "clsx"; import clsx from "clsx";
import Button from "./Button"; import Button from "./Button";
export default function MoltenButton(props: ButtonProps){ export default function MoltenButton({
const {
className, className,
variant = "standard", variant = "standard",
disabled disabled,
} = props; ...buttonProps
}: Readonly<ButtonProps>){
return ( return (
<Button <Button
{...props} data-testid="mattrixwv-molten-button"
variant={variant}
disabled={disabled}
className={clsx( className={clsx(
"transition duration-300", "transition duration-300",
className, className,
//Background //Background
{ {
"bg-orange-600 hover:bg-orange-700 active:bg-orange-800": (variant === "standard") && (!disabled), "bg-molten active:bg-molten-dark": (variant === "standard") && (!disabled),
"bg-orange-400/80": (variant === "standard") && (disabled), "bg-molten-light/80": (variant === "standard") && (disabled),
"bg-transparent": (variant === "outline" || variant === "icon"), "bg-transparent": (variant === "outline" || variant === "icon"),
"bg-transparent hover:bg-orange-600 active:bg-orange-700": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "bg-transparent hover:bg-molten active:bg-molten-dark": (variant === "ghost" || variant === "outline-ghost") && (!disabled),
"bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled) "bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
}, },
//Outline //Outline
{ {
"border-orange-600 hover:border-orange-700 active:border-orange-800": (variant === "standard" || variant === "outline") && (!disabled), "border-molten active:border-molten-dark": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (!disabled),
"border-orange-400/80": (variant === "standard" || variant === "outline") && (disabled), "border-molten-light/80": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (disabled)
"border-orange-600 hover:border-orange-600 active:border-orange-700": (variant === "outline-ghost") && (!disabled),
"border-orange-400/80 ": (variant === "outline-ghost") && (disabled)
}, },
//Text //Text
{ {
"text-white": variant === "standard", "text-black": variant === "standard",
"text-orange-600 hover:text-orange-700 active:text-orange-800": (variant === "outline" || variant === "icon") && (!disabled), "text-molten active:text-molten-dark": (variant === "outline" || variant === "icon") && (!disabled),
"text-orange-400/80": (variant === "outline" || variant === "icon") && (disabled), "text-molten-light/80": (variant === "outline" || variant === "icon" || variant === "ghost" || variant === "outline-ghost") && (disabled),
"text-orange-600 hover:text-white active:text-white": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "text-molten hover:text-black active:text-black": (variant === "ghost" || variant === "outline-ghost") && (!disabled)
"text-orange-400/80 ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
} }
)} )}
{...buttonProps}
/> />
); );
} }

View File

@@ -1,46 +1,44 @@
import type { ButtonProps } from "$/types/Button"; import type { ButtonProps } from "$/types/ButtonTypes";
import clsx from "clsx"; import clsx from "clsx";
import Button from "./Button"; import Button from "./Button";
export default function PrimaryButton(props: ButtonProps){ export default function PrimaryButton({
const {
className, className,
variant = "standard", variant = "standard",
disabled disabled,
} = props; ...buttonProps
}: Readonly<ButtonProps>){
return ( return (
<Button <Button
{...props} data-testid="mattrixwv-primary-button"
variant={variant}
disabled={disabled}
className={clsx( className={clsx(
"transition duration-300", "transition duration-300",
className, className,
//Background //Background
{ {
"bg-blue-600 hover:bg-blue-700 active:bg-blue-800": (variant === "standard") && (!disabled), "bg-primary active:bg-primary-dark": (variant === "standard") && (!disabled),
"bg-blue-400/80": (variant === "standard") && (disabled), "bg-primary-light/80": (variant === "standard") && (disabled),
"bg-transparent": (variant === "outline" || variant === "icon"), "bg-transparent": (variant === "outline" || variant === "icon"),
"bg-transparent hover:bg-blue-600 active:bg-blue-700": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "bg-transparent hover:bg-primary active:bg-primary-dark": (variant === "ghost" || variant === "outline-ghost") && (!disabled),
"bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled) "bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
}, },
//Outline //Outline
{ {
"border-blue-600 hover:border-blue-700 active:border-blue-800": (variant === "standard" || variant === "outline") && (!disabled), "border-primary active:border-primary-dark": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (!disabled),
"border-blue-400/80": (variant === "standard" || variant === "outline") && (disabled), "border-primary-light/80": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (disabled)
"border-blue-600 hover:border-blue-600 active:border-blue-700": (variant === "outline-ghost") && (!disabled),
"border-blue-400/80 ": (variant === "outline-ghost") && (disabled)
}, },
//Text //Text
{ {
"text-white": variant === "standard", "text-white": variant === "standard",
"text-blue-600 hover:text-blue-700 active:text-blue-800": (variant === "outline" || variant === "icon") && (!disabled), "text-primary active:text-primary-dark": (variant === "outline" || variant === "icon") && (!disabled),
"text-blue-400/80": (variant === "outline" || variant === "icon") && (disabled), "text-primary-light/80": (variant === "outline" || variant === "icon" || variant === "ghost" || variant === "outline-ghost") && (disabled),
"text-blue-600 hover:text-white active:text-white": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "text-primary hover:text-white active:text-white": (variant === "ghost" || variant === "outline-ghost") && (!disabled)
"text-blue-400/80 ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
} }
)} )}
{...buttonProps}
/> />
); );
} }

View File

@@ -1,46 +1,44 @@
import type { ButtonProps } from "$/types/Button"; import type { ButtonProps } from "$/types/ButtonTypes";
import clsx from "clsx"; import clsx from "clsx";
import Button from "./Button"; import Button from "./Button";
export default function SecondaryButton(props: ButtonProps){ export default function SecondaryButton({
const {
className, className,
variant = "standard", variant = "standard",
disabled disabled,
} = props; ...buttonProps
}: Readonly<ButtonProps>){
return ( return (
<Button <Button
{...props} data-testid="mattrixwv-secondary-button"
variant={variant}
disabled={disabled}
className={clsx( className={clsx(
"transition duration-300", "transition duration-300",
className, className,
//Background //Background
{ {
"bg-neutral-500 hover:bg-neutral-600 active:bg-neutral-700": (variant === "standard") && (!disabled), "bg-secondary active:bg-secondary-dark": (variant === "standard") && (!disabled),
"bg-neutral-300/80": (variant === "standard") && (disabled), "bg-secondary-light/80": (variant === "standard") && (disabled),
"bg-transparent": (variant === "outline" || variant === "icon"), "bg-transparent": (variant === "outline" || variant === "icon"),
"bg-transparent hover:bg-neutral-500 active:bg-neutral-600": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "bg-transparent hover:bg-secondary active:bg-secondary-dark": (variant === "ghost" || variant === "outline-ghost") && (!disabled),
"bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled) "bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
}, },
//Outline //Outline
{ {
"border-neutral-500 hover:border-neutral-600 active:border-neutral-700": (variant === "standard" || variant === "outline") && (!disabled), "border-secondary active:border-secondary-dark": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (!disabled),
"border-neutral-300/80": (variant === "standard" || variant === "outline") && (disabled), "border-secondary-light/80": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (disabled)
"border-neutral-500 hover:border-neutral-500 active:border-neutral-600": (variant === "outline-ghost") && (!disabled),
"border-neutral-300/80 ": (variant === "outline-ghost") && (disabled)
}, },
//Text //Text
{ {
"text-white": variant === "standard", "text-white": variant === "standard",
"text-neutral-500 hover:text-neutral-600 active:text-neutral-700": (variant === "outline" || variant === "icon") && (!disabled), "text-secondary active:text-secondary-dark": (variant === "outline" || variant === "icon") && (!disabled),
"text-neutral-300/80": (variant === "outline" || variant === "icon") && (disabled), "text-secondary-light/80": (variant === "outline" || variant === "icon" || variant === "ghost" || variant === "outline-ghost") && (disabled),
"text-neutral-500 hover:text-black active:text-black": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "text-secondary hover:text-white active:text-white": (variant === "ghost" || variant === "outline-ghost") && (!disabled)
"text-neutral-300/80 ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
} }
)} )}
{...buttonProps}
/> />
); );
} }

View File

@@ -1,46 +1,44 @@
import type { ButtonProps } from "$/types/Button"; import type { ButtonProps } from "$/types/ButtonTypes";
import clsx from "clsx"; import clsx from "clsx";
import Button from "./Button"; import Button from "./Button";
export default function SuccessButton(props: ButtonProps){ export default function SuccessButton({
const {
className, className,
variant = "standard", variant = "standard",
disabled disabled,
} = props; ...buttonProps
}: Readonly<ButtonProps>){
return ( return (
<Button <Button
{...props} data-testid="mattrixwv-success-button"
variant={variant}
disabled={disabled}
className={clsx( className={clsx(
"transition duration-300", "transition duration-300",
className, className,
//Background //Background
{ {
"bg-green-600 hover:bg-green-700 active:bg-green-800": (variant === "standard") && (!disabled), "bg-success active:bg-success-dark": (variant === "standard") && (!disabled),
"bg-green-400/80": (variant === "standard") && (disabled), "bg-success-light/80": (variant === "standard") && (disabled),
"bg-transparent": (variant === "outline" || variant === "icon"), "bg-transparent": (variant === "outline" || variant === "icon"),
"bg-transparent hover:bg-green-600 active:bg-green-700": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "bg-transparent hover:bg-success active:bg-success-dark": (variant === "ghost" || variant === "outline-ghost") && (!disabled),
"bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled) "bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
}, },
//Outline //Outline
{ {
"border-green-600 hover:border-green-700 active:border-green-800": (variant === "standard" || variant === "outline") && (!disabled), "border-success active:border-success-dark": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (!disabled),
"border-green-400/80": (variant === "standard" || variant === "outline") && (disabled), "border-success-light/80": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (disabled)
"border-green-600 hover:border-green-600 active:border-green-700": (variant === "outline-ghost") && (!disabled),
"border-green-400/80 ": (variant === "outline-ghost") && (disabled)
}, },
//Text //Text
{ {
"text-white": variant === "standard", "text-black": variant === "standard",
"text-green-600 hover:text-green-700 active:text-green-800": (variant === "outline" || variant === "icon") && (!disabled), "text-success active:text-success-dark": (variant === "outline" || variant === "icon") && (!disabled),
"text-green-400/80": (variant === "outline" || variant === "icon") && (disabled), "text-success-light/80": (variant === "outline" || variant === "icon" || variant === "ghost" || variant === "outline-ghost") && (disabled),
"text-green-600 hover:text-white active:text-white": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "text-success hover:text-black active:text-black": (variant === "ghost" || variant === "outline-ghost") && (!disabled)
"text-green-400/80 ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
} }
)} )}
{...buttonProps}
/> />
); );
} }

View File

@@ -1,46 +1,44 @@
import type { ButtonProps } from "$/types/Button"; import type { ButtonProps } from "$/types/ButtonTypes";
import clsx from "clsx"; import clsx from "clsx";
import Button from "./Button"; import Button from "./Button";
export default function TertiaryButton(props: ButtonProps){ export default function TertiaryButton({
const {
className, className,
variant = "standard", variant = "standard",
disabled disabled,
} = props; ...buttonProps
}: Readonly<ButtonProps>){
return ( return (
<Button <Button
{...props} data-testid="mattrixwv-tertiary-button"
variant={variant}
disabled={disabled}
className={clsx( className={clsx(
"transition duration-300", "transition duration-300",
className, className,
//Background //Background
{ {
"bg-purple-600 hover:bg-purple-700 active:bg-purple-800": (variant === "standard") && (!disabled), "bg-tertiary active:bg-tertiary-dark": (variant === "standard") && (!disabled),
"bg-purple-400/80": (variant === "standard") && (disabled), "bg-tertiary-light/80": (variant === "standard") && (disabled),
"bg-transparent": (variant === "outline" || variant === "icon"), "bg-transparent": (variant === "outline" || variant === "icon"),
"bg-transparent hover:bg-purple-600 active:bg-purple-700": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "bg-transparent hover:bg-tertiary active:bg-tertiary-dark": (variant === "ghost" || variant === "outline-ghost") && (!disabled),
"bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled) "bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
}, },
//Outline //Outline
{ {
"border-purple-600 hover:border-purple-700 active:border-purple-800": (variant === "standard" || variant === "outline") && (!disabled), "border-tertiary active:border-tertiary-dark": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (!disabled),
"border-purple-400/80": (variant === "standard" || variant === "outline") && (disabled), "border-tertiary-light/80": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (disabled)
"border-purple-600 hover:border-purple-600 active:border-purple-700": (variant === "outline-ghost") && (!disabled),
"border-purple-400/80 ": (variant === "outline-ghost") && (disabled)
}, },
//Text //Text
{ {
"text-white": variant === "standard", "text-white": variant === "standard",
"text-purple-600 hover:text-purple-700 active:text-purple-800": (variant === "outline" || variant === "icon") && (!disabled), "text-tertiary active:text-tertiary-dark": (variant === "outline" || variant === "icon") && (!disabled),
"text-purple-400/80": (variant === "outline" || variant === "icon") && (disabled), "text-tertiary-light/80": (variant === "outline" || variant === "icon" || variant === "ghost" || variant === "outline-ghost") && (disabled),
"text-purple-600 hover:text-white active:text-white": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "text-tertiary hover:text-white active:text-white": (variant === "ghost" || variant === "outline-ghost") && (!disabled)
"text-purple-400/80 ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
} }
)} )}
{...buttonProps}
/> />
); );
} }

View File

@@ -1,46 +1,44 @@
import type { ButtonProps } from "$/types/Button"; import type { ButtonProps } from "$/types/ButtonTypes";
import clsx from "clsx"; import clsx from "clsx";
import Button from "./Button"; import Button from "./Button";
export default function WarningButton(props: ButtonProps){ export default function WarningButton({
const {
className, className,
variant = "standard", variant = "standard",
disabled disabled,
} = props; ...buttonProps
}: Readonly<ButtonProps>){
return ( return (
<Button <Button
{...props} data-testid="mattrixwv-warning-button"
variant={variant}
disabled={disabled}
className={clsx( className={clsx(
"transition duration-300", "transition duration-300",
className, className,
//Background //Background
{ {
"bg-yellow-500 hover:bg-yellow-600 active:bg-yellow-700": (variant === "standard") && (!disabled), "bg-warning active:bg-warning-dark": (variant === "standard") && (!disabled),
"bg-yellow-300/80": (variant === "standard") && (disabled), "bg-warning-light/80": (variant === "standard") && (disabled),
"bg-transparent": (variant === "outline" || variant === "icon"), "bg-transparent": (variant === "outline" || variant === "icon"),
"bg-transparent hover:bg-yellow-500 active:bg-yellow-600": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "bg-transparent hover:bg-warning active:bg-warning-dark": (variant === "ghost" || variant === "outline-ghost") && (!disabled),
"bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled) "bg-transparent ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
}, },
//Outline //Outline
{ {
"border-yellow-500 hover:border-yellow-600 active:border-yellow-700": (variant === "standard" || variant === "outline") && (!disabled), "border-warning active:border-warning-dark": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (!disabled),
"border-yellow-300/80": (variant === "standard" || variant === "outline") && (disabled), "border-warning-light/80": (variant === "standard" || variant === "outline" || variant === "outline-ghost") && (disabled)
"border-yellow-500 hover:border-yellow-500 active:border-yellow-600": (variant === "outline-ghost") && (!disabled),
"border-yellow-300/80 ": (variant === "outline-ghost") && (disabled)
}, },
//Text //Text
{ {
"text-black": variant === "standard", "text-black": variant === "standard",
"text-yellow-500 hover:text-yellow-600 active:text-yellow-700": (variant === "outline" || variant === "icon") && (!disabled), "text-warning active:text-warning-dark": (variant === "outline" || variant === "icon") && (!disabled),
"text-yellow-300/80": (variant === "outline" || variant === "icon") && (disabled), "text-warning-light/80": (variant === "outline" || variant === "icon" || variant === "ghost" || variant === "outline-ghost") && (disabled),
"text-yellow-500 hover:text-black active:text-black": (variant === "ghost" || variant === "outline-ghost") && (!disabled), "text-warning hover:text-black active:text-black": (variant === "ghost" || variant === "outline-ghost") && (!disabled)
"text-yellow-300/80 ": (variant === "ghost" || variant === "outline-ghost") && (disabled)
} }
)} )}
{...buttonProps}
/> />
); );
} }

View File

@@ -0,0 +1,12 @@
export { default as Button } from "./Button";
export { default as DangerButton } from "./DangerButton";
export { default as DarkButton } from "./DarkButton";
export { default as InfoButton } from "./InfoButton";
export { default as LightButton } from "./LightButton";
export { default as MoltenButton } from "./MoltenButton";
export { default as PrimaryButton } from "./PrimaryButton";
export { default as SecondaryButton } from "./SecondaryButton";
export { default as SuccessButton } from "./SuccessButton";
export { default as TertiaryButton } from "./TertiaryButton";
export { default as WarningButton } from "./WarningButton";

View File

@@ -1,10 +0,0 @@
import OptionInput from "./input/OptionInput";
import SelectInput from "./input/SelectInput";
import TextInput from "./input/TextInput";
export {
OptionInput,
SelectInput,
TextInput
};

View File

@@ -1,41 +0,0 @@
import type { SelectInputProps } from "$/types/Input";
import { Listbox, ListboxButton, ListboxOptions } from "@headlessui/react";
import clsx from "clsx";
import { BsChevronDown } from "react-icons/bs";
export default function SelectInput(props: SelectInputProps){
const {
label,
value,
onChange,
children
} = props;
return (
<Listbox
value={value}
onChange={onChange}
>
<div
className="relative w-full"
>
<ListboxButton
className={clsx(
"flex flex-row items-center justify-between w-full",
"outline rounded px-2 py-1"
)}
>
<span>{label}</span>
<span><BsChevronDown size={22}/></span>
</ListboxButton>
<ListboxOptions
className="absolute w-full max-h-60 overflow-scroll z-10 outline-none"
>
{children}
</ListboxOptions>
</div>
</Listbox>
);
}

View File

@@ -1,57 +0,0 @@
import type { TextInputProps } from "$/types/Input";
import clsx from "clsx";
export default function TextInput(props: TextInputProps){
const {
id,
className,
inputClassName,
labelClassName,
placeholder,
defaultValue,
value,
onChange,
disabled
} = props;
return (
<div
className={clsx(
"flex flex-row items-center justify-center rounded-lg border-2",
className
)}
>
<div
className="relative flex flex-row items-center justify-center px-2 py-1"
>
<input
type="text"
id={id}
className={clsx(
"peer bg-transparent outline-none placeholder-transparent",
inputClassName
)}
placeholder={placeholder}
defaultValue={defaultValue}
value={value}
onChange={onChange}
disabled={disabled}
/>
<label
className={clsx(
"absolute ml-2 -top-3 left-0 text-sm rounded-md px-1 select-none cursor-default",
"peer-placeholder-shown:top-0 peer-placeholder-shown:-left-1 peer-placeholder-shown:text-inherit peer-placeholder-shown:text-base peer-placeholder-shown:bg-transparent peer-placeholder-shown:h-full peer-placeholder-shown:cursor-text",
"flex items-center",
labelClassName
)}
style={{ transitionProperty: "top,, left, font-size, line-height", transitionTimingFunction: "cubic-bezier(0.4 0, 0.2, 1)", transitionDuration: "250ms" }}
htmlFor={id}
>
{placeholder}
</label>
</div>
</div>
);
}

View File

@@ -0,0 +1,24 @@
import type { CheckboxProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvCheckbox from "./MattrixwvCheckbox";
export default function DangerCheckbox({
className,
showBox = true,
...props
}: Readonly<CheckboxProps>){
return (
<MattrixwvCheckbox
className={clsx(
className,
{
"group-data-checked:bg-danger group-data-checked:stroke-white": showBox,
"group-data-checked:stroke-danger": !showBox
}
)}
showBox={showBox}
{...props}
/>
);
}

View File

@@ -0,0 +1,24 @@
import type { CheckboxProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvCheckbox from "./MattrixwvCheckbox";
export default function DarkCheckbox({
className,
showBox = true,
...props
}: Readonly<CheckboxProps>){
return (
<MattrixwvCheckbox
className={clsx(
className,
{
"group-data-checked:bg-dark group-data-checked:stroke-light": showBox,
"group-data-checked:stroke-dark": !showBox
}
)}
showBox={showBox}
{...props}
/>
);
}

View File

@@ -0,0 +1,24 @@
import type { CheckboxProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvCheckbox from "./MattrixwvCheckbox";
export default function InfoCheckbox({
className,
showBox = true,
...props
}: Readonly<CheckboxProps>){
return (
<MattrixwvCheckbox
className={clsx(
className,
{
"group-data-checked:bg-info group-data-checked:stroke-white": showBox,
"group-data-checked:stroke-info": !showBox
}
)}
showBox={showBox}
{...props}
/>
);
}

View File

@@ -0,0 +1,24 @@
import type { CheckboxProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvCheckbox from "./MattrixwvCheckbox";
export default function LightCheckbox({
className,
showBox = true,
...props
}: Readonly<CheckboxProps>){
return (
<MattrixwvCheckbox
className={clsx(
className,
{
"group-data-checked:bg-light group-data-checked:stroke-dark": showBox,
"group-data-checked:stroke-light": !showBox
}
)}
showBox={showBox}
{...props}
/>
);
}

View File

@@ -0,0 +1,85 @@
import type { CheckboxProps } from "$/types/InputTypes";
import { Checkbox, Field, Label } from "@headlessui/react";
import clsx from "clsx";
export default function MattrixwvCheckbox({
className,
labelClassName,
name,
size = "sm",
showBox = true,
onChange,
checked,
defaultChecked,
strokeWidth = 2,
value,
disabled,
children
}: Readonly<CheckboxProps>){
return (
<Field
className="flex flex-row items-center justify-center gap-x-2"
>
<Checkbox
data-testid="mattrixwv-checkbox"
className={clsx(
"group",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
{
"cursor-pointer": !disabled,
"cursor-not-allowed": disabled
}
)}
name={name}
checked={checked}
defaultChecked={defaultChecked}
onChange={onChange}
value={value}
disabled={disabled}
>
{/* Checkbox */}
<div
data-testid="mattrixwv-checkbox-graphic"
className={clsx(
className,
{
"border rounded": showBox
},
{
"": size === "none",
"size-3": size === "xs",
"size-4": size === "sm",
"size-5": size === "md",
"size-6": size === "lg",
"size-7": size === "xl"
}
)}
>
<svg
viewBox="0 0 14 14"
fill="none"
aria-hidden="true"
>
<path
d="M3 8L6 11L11 3.5"
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</div>
</Checkbox>
{/* Label */}
{
children &&
<Label
data-testid="mattrixwv-checkbox-label"
className={labelClassName}
>
{children}
</Label>
}
</Field>
);
}

View File

@@ -0,0 +1,24 @@
import type { CheckboxProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvCheckbox from "./MattrixwvCheckbox";
export default function MoltenCheckbox({
className,
showBox = true,
...props
}: Readonly<CheckboxProps>){
return (
<MattrixwvCheckbox
className={clsx(
className,
{
"group-data-checked:bg-molten group-data-checked:stroke-white": showBox,
"group-data-checked:stroke-molten": !showBox
}
)}
showBox={showBox}
{...props}
/>
);
}

View File

@@ -0,0 +1,24 @@
import type { CheckboxProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvCheckbox from "./MattrixwvCheckbox";
export default function PrimaryCheckbox({
className,
showBox = true,
...props
}: Readonly<CheckboxProps>){
return (
<MattrixwvCheckbox
className={clsx(
className,
{
"group-data-checked:bg-primary group-data-checked:stroke-white": showBox,
"group-data-checked:stroke-primary": !showBox
}
)}
showBox={showBox}
{...props}
/>
);
}

View File

@@ -0,0 +1,24 @@
import type { CheckboxProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvCheckbox from "./MattrixwvCheckbox";
export default function SecondaryCheckbox({
className,
showBox = true,
...props
}: Readonly<CheckboxProps>){
return (
<MattrixwvCheckbox
className={clsx(
className,
{
"group-data-checked:bg-secondary group-data-checked:stroke-white": showBox,
"group-data-checked:stroke-secondary": !showBox
}
)}
showBox={showBox}
{...props}
/>
);
}

View File

@@ -0,0 +1,24 @@
import type { CheckboxProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvCheckbox from "./MattrixwvCheckbox";
export default function SuccessCheckbox({
className,
showBox = true,
...props
}: Readonly<CheckboxProps>){
return (
<MattrixwvCheckbox
className={clsx(
className,
{
"group-data-checked:bg-success group-data-checked:stroke-white": showBox,
"group-data-checked:stroke-success": !showBox
}
)}
showBox={showBox}
{...props}
/>
);
}

View File

@@ -0,0 +1,24 @@
import type { CheckboxProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvCheckbox from "./MattrixwvCheckbox";
export default function TertiaryCheckbox({
className,
showBox = true,
...props
}: Readonly<CheckboxProps>){
return (
<MattrixwvCheckbox
className={clsx(
className,
{
"group-data-checked:bg-tertiary group-data-checked:stroke-white": showBox,
"group-data-checked:stroke-tertiary": !showBox
}
)}
showBox={showBox}
{...props}
/>
);
}

View File

@@ -0,0 +1,24 @@
import type { CheckboxProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvCheckbox from "./MattrixwvCheckbox";
export default function WarningCheckbox({
className,
showBox = true,
...props
}: Readonly<CheckboxProps>){
return (
<MattrixwvCheckbox
className={clsx(
className,
{
"group-data-checked:bg-warning group-data-checked:stroke-white": showBox,
"group-data-checked:stroke-warning": !showBox
}
)}
showBox={showBox}
{...props}
/>
);
}

View File

@@ -0,0 +1,47 @@
import type { DateInputProps } from "$/types/InputTypes";
import clsx from "clsx";
import { type ChangeEvent } from "react";
export default function DateInput({
className,
value,
onChange,
...inputProps
}: Readonly<DateInputProps>){
//Used to translate the string from the input to a date for onChange
const changeDate = (e: ChangeEvent<HTMLInputElement>) => {
const [ year, month, day ] = e.target.value.split("-").map(Number);
const newDate = new Date(year, month - 1, day);
onChange(Number.isNaN(newDate.getTime()) ? undefined : newDate);
};
return (
<input
type="date"
className={clsx(
"border rounded-lg px-2 py-1",
className
)}
value={formatDate(value)}
onChange={changeDate}
{...inputProps}
/>
);
}
//Used to translate the date to a string for the input
function formatDate(date: Date | undefined): string{
if(!date || Number.isNaN(date.getTime())){
return "";
}
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}

View File

@@ -0,0 +1,68 @@
import type { DateInputProps } from "$/types/InputTypes";
import clsx from "clsx";
import type { ChangeEvent } from "react";
export default function DateTimeInput({
id,
className,
step = 60,
value,
onChange,
...inputProps
}: Readonly<DateInputProps>){
const changeDateTime = (e: ChangeEvent<HTMLInputElement>) => {
const match = new RegExp(/^(\d+)-(\d+)-(\d+)T(\d+):(\d+)(?::(\d+)(?:\.(\d+))?)?$/).exec(e.target.value);
if(!match){
onChange(undefined);
return;
}
const [ ,
y, mo, d,
h, mi, s = "0", ms = "0"
] = match;
const date = new Date(
Number(y), Number(mo) - 1, Number(d),
Number(h), Number(mi), Number(s), Number(ms)
);
onChange(Number.isNaN(date.getTime()) ? undefined : date);
}
return (
<input
type="datetime-local"
id={id}
className={clsx(
"border rounded-lg px-2 py-1 outline-none",
className
)}
value={formatDateTime(value)}
onChange={changeDateTime}
step={step}
{...inputProps}
/>
);
}
function formatDateTime(date: Date | undefined){
if(!date || Number.isNaN(date.getTime())){
return "";
}
const y = date.getFullYear();
const mo = String(date.getMonth() + 1).padStart(2, "0");
const d = String(date.getDate()).padStart(2, "0");
const h = String(date.getHours()).padStart(2, "0");
const mi = String(date.getMinutes()).padStart(2, "0");
const s = String(date.getSeconds()).padStart(2, "0");
const ms = String(date.getMilliseconds()).padStart(2, "0");
return `${y}-${mo}-${d}T${h}:${mi}:${s}.${ms}`;
}

View File

@@ -0,0 +1,67 @@
import type { DateInputProps } from "$/types/InputTypes";
import clsx from "clsx";
import type { ChangeEvent } from "react";
export default function TimeInput({
id,
className,
step = 60,
value,
onChange,
...inputProps
}: Readonly<DateInputProps>){
const changeTime = (e: ChangeEvent<HTMLInputElement>) => {
const match = new RegExp(/^(\d+):(\d+)(?::(\d+)(?:\.(\d+))?)?$/).exec(e.target.value);
if(!match){
onChange(undefined);
return;
}
const [ , h, m, s = "0", ms = "0"] = match;
const newDate = new Date();
newDate.setHours(Number(h), Number(m), Number(s), Number(ms));
onChange(Number.isNaN(newDate) ? undefined : newDate);
}
return (
<input
type="time"
id={id}
className={clsx(
"border rounded-lg px-2 py-1 outline-none",
className
)}
value={formatTime(value, step)}
onChange={changeTime}
step={step}
{...inputProps}
/>
);
}
function formatTime(date: Date | undefined, step: number){
if(!date || Number.isNaN(date.getTime())){
return "";
}
const h = String(date.getHours()).padStart(2, "0");
const m = String(date.getMinutes()).padStart(2, "0");
const s = String(date.getSeconds()).padStart(2, "0");
const ms = String(date.getMilliseconds()).padStart(3, "0");
let time = `${h}:${m}`;
if(step < 60){
time += `:${s}`;
}
if(step < 1){
time += `.${ms}`;
}
return time;
}

View File

@@ -0,0 +1,109 @@
import { DangerButton } from "$/component/button";
import type { FileInputProps } from "$/types/InputTypes";
import { humanReadableBytes } from "$/util/FileUtil";
import clsx from "clsx";
import { useRef, useState } from "react";
import { MdClose } from "react-icons/md";
export default function DragAndDropFileInput({
id,
className,
name,
ariaLabel,
minSize,
maxSize,
showFileName = true,
showSize = true,
onChange,
disabled,
children
}: Readonly<FileInputProps>){
const [ file, setFile ] = useState<File>();
const inputRef = useRef<HTMLInputElement>(null);
return (
<label
className={clsx(
"flex flex-col items-center justify-center border-2 rounded-lg cursor-pointer",
"data-drag:border-primary data-drag:text-primary",
className
)}
onDragOver={(e) => { e.preventDefault(); e.currentTarget.dataset.drag = "true"; }}
onDragLeave={(e) => { e.preventDefault(); delete e.currentTarget.dataset.drag; }}
onDrop={(e) => {
e.preventDefault();
delete e.currentTarget.dataset.drag;
const currentFile = e.dataTransfer.files[0];
setFile(currentFile);
if ((minSize && currentFile.size < minSize) || (maxSize && currentFile.size > maxSize)) return;
onChange?.(currentFile);
if(inputRef.current){ inputRef.current.files = e.dataTransfer.files; }
}}
aria-label={ariaLabel}
>
<input
ref={inputRef}
type="file"
id={id}
className="sr-only"
name={name}
onChange={(e) => {
const currentFile = e.target.files?.[0];
setFile(currentFile);
if ((minSize && currentFile && currentFile.size < minSize) || (maxSize && currentFile && currentFile.size > maxSize)) return;
onChange?.(currentFile);
}}
disabled={disabled}
/>
<div
className="flex flex-col items-center justify-between px-2"
>
<div className="flex flex-row items-center justify-center">
{children}
</div>
<div
className="flex flex-row items-center justify-between gap-x-2 w-full"
>
{
showFileName &&
<div className="flex flex-row items-center justify-center gap-x-2">
{file?.name}
</div>
}
{
file &&
<DangerButton
className="mr-4"
shape="square"
variant="icon"
onClick={(e) => { e.preventDefault(); setFile(undefined); onChange?.(undefined); }}
>
<MdClose size={22} className="fill-danger"/>
</DangerButton>
}
{
showSize &&
<div
className={clsx(
{
"text-red-600": minSize && file?.size && file?.size < minSize,
"text-red-600 ": maxSize && file?.size && file?.size > maxSize,
"text-green-600": minSize && !maxSize && file?.size && file?.size > minSize,
"text-green-600 ": !minSize && maxSize && file?.size && file?.size < maxSize,
" text-green-600": minSize && maxSize && file?.size && file?.size > minSize && file?.size < maxSize
}
)}
>
{humanReadableBytes(file?.size ?? 0)}
</div>
}
</div>
</div>
</label>
);
}

View File

@@ -0,0 +1,109 @@
import { DangerButton, SecondaryButton } from "$/component/button";
import type { FileInputProps } from "$/types/InputTypes";
import { humanReadableBytes } from "$/util/FileUtil";
import clsx from "clsx";
import { useRef, useState } from "react";
import { FaRegFolderOpen } from "react-icons/fa6";
import { MdClose } from "react-icons/md";
export default function FileInput({
id,
className,
name,
ariaLabel,
minSize,
maxSize,
showFileName = true,
showSize = true,
onChange,
disabled,
children
}: Readonly<FileInputProps>){
const inputRef = useRef<HTMLInputElement>(null);
const [ file, setFile ] = useState<File>();
return (
<div
className={clsx(
"flex flex-row items-center justify-between border-2 rounded-lg",
className
)}
>
<input
ref={inputRef}
id={id}
type="file"
className="sr-only"
name={name}
aria-label={ariaLabel}
onChange={(e) => {
const currentFile = e.target.files?.[0];
setFile(currentFile);
if ((minSize && currentFile && currentFile.size < minSize) || (maxSize && currentFile && currentFile.size > maxSize)) return;
onChange?.(currentFile);
}}
disabled={disabled}
/>
<div
className="flex flex-row items-center justify-between grow w-full px-2"
>
{
children && !showFileName &&
<div>{children}</div>
}
{
showFileName &&
<div>{file?.name}</div>
}
{
file &&
<DangerButton
shape="square"
variant="icon"
onClick={(e) => { e.preventDefault(); setFile(undefined); onChange?.(undefined); }}
>
<MdClose size={22} className="fill-danger"/>
</DangerButton>
}
{
!children && !showFileName &&
<>&nbsp;</>
}
{
showSize &&
<div
className={clsx(
"ml-4",
{
"text-danger": minSize && file?.size && file?.size < minSize,
"text-danger ": maxSize && file?.size && file?.size > maxSize,
"text-success": minSize && !maxSize && file?.size && file?.size > minSize,
"text-success ": !minSize && maxSize && file?.size && file?.size < maxSize,
" text-success": minSize && maxSize && file?.size && file?.size > minSize && file?.size < maxSize
}
)}
>
{humanReadableBytes(file?.size ?? 0)}
</div>
}
</div>
<div
className="border-l-2"
>
<SecondaryButton
className="text-nowrap rounded-r-lg"
rounding="none"
shape="square"
size="lg"
onClick={() => { inputRef.current?.click(); }}
disabled={disabled}
aria-label="Select File"
>
<FaRegFolderOpen />
</SecondaryButton>
</div>
</div>
);
}

View File

@@ -0,0 +1,53 @@
//? Checkboxes
export { default as DangerCheckbox } from "./checkbox/DangerCheckbox";
export { default as DarkCheckbox } from "./checkbox/DarkCheckbox";
export { default as InfoCheckbox } from "./checkbox/InfoCheckbox";
export { default as LightCheckbox } from "./checkbox/LightCheckbox";
export { default as MattrixwvCheckbox } from "./checkbox/MattrixwvCheckbox";
export { default as MoltenCheckbox } from "./checkbox/MoltenCheckbox";
export { default as PrimaryCheckbox } from "./checkbox/PrimaryCheckbox";
export { default as SecondaryCheckbox } from "./checkbox/SecondaryCheckbox";
export { default as SuccessCheckbox } from "./checkbox/SuccessCheckbox";
export { default as TertiaryCheckbox } from "./checkbox/TertiaryCheckbox";
export { default as WarningCheckbox } from "./checkbox/WarningCheckbox";
//? Radio Buttons
export { default as DangerRadioButton } from "./radio/DangerRadioButton";
export { default as DarkRadioButton } from "./radio/DarkRadioButton";
export { default as InfoRadioButton } from "./radio/InfoRadioButton";
export { default as LightRadioButton } from "./radio/LightRadioButton";
export { default as MoltenRadioButton } from "./radio/MoltenRadioButton";
export { default as PrimaryRadioButton } from "./radio/PrimaryRadioButton";
export { default as RadioButton } from "./radio/RadioButton";
export { default as RadioList } from "./radio/RadioList";
export { default as SecondaryRadioButton } from "./radio/SecondaryRadioButton";
export { default as SuccessRadioButton } from "./radio/SuccessRadioButton";
export { default as TertiaryRadioButton } from "./radio/TertiaryRadioButton";
export { default as WarningRadioButton } from "./radio/WarningRadioButton";
//? Switches
export { default as ButtonSwitch } from "./switch/ButtonSwitch";
export { default as DangerSwitch } from "./switch/DangerSwitch";
export { default as DarkSwitch } from "./switch/DarkSwitch";
export { default as LightSwitch } from "./switch/LightSwitch";
export { default as MattrixwvSwitch } from "./switch/MattrixwvSwitch";
export { default as PrimarySwitch } from "./switch/PrimarySwitch";
export { default as SecondarySwitch } from "./switch/SecondarySwitch";
export { default as SuccessDangerSwitch } from "./switch/SuccessDangerSwitch";
export { default as SuccessSwitch } from "./switch/SuccessSwitch";
export { default as TertiarySwitch } from "./switch/TertiarySwitch";
export { default as WarningSwitch } from "./switch/WarningSwitch";
//? Other imports
export { default as DateInput } from "./date/DateInput";
export { default as DateTimeInput } from "./date/DateTimeInput";
export { default as TimeInput } from "./date/TimeInput";
export { default as DragAndDropFileInput } from "./file/DragAndDropFileInput";
export { default as FileInput } from "./file/FileInput";
export { default as NumberInput } from "./number/NumberInput";
export { default as OptionInput } from "./text/OptionInput";
export { default as SelectInput } from "./text/SelectInput";
export { default as TextArea } from "./text/TextArea";
export { default as TextInput } from "./text/TextInput";

View File

@@ -0,0 +1,62 @@
import type { NumberInputProps } from "$/types/InputTypes";
import clsx from "clsx";
export default function NumberInput({
id,
className,
inputClassName,
labelClassName,
name,
min,
max,
step,
prefix,
suffix,
value,
onChange,
disabled,
children
}: Readonly<NumberInputProps>){
return (
<div
className={clsx(
"flex flex-row items-center justify-center rounded-lg border-2",
className
)}
>
<div className="relative flex flex-row items-center justify-center px-2 py-1 w-full">
<div className="flex flex-row items-center justify-start">
{ prefix && <span>{prefix}</span> }
<input
type="number"
id={id}
className={clsx(
"peer bg-transparent outline-none placeholder-transparent w-full",
"[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
inputClassName
)}
name={name}
min={min}
max={max}
step={step}
value={value}
onChange={(e) => onChange(e.target.valueAsNumber || 0)}
disabled={disabled}
/>
{ suffix && <span>{suffix}</span> }
</div>
<label
className={clsx(
"absolute ml-2 -top-3 left-0 text-sm rounded-md px-1 select-none cursor-default",
labelClassName
)}
style={{ transitionProperty: "top, left, font-size, line-height", transitionTimingFunction: "cubic-bezier(0.4 0, 0.2, 1)", transitionDuration: "250ms" }}
htmlFor={id}
>
{children}
</label>
</div>
</div>
);
}

View File

@@ -0,0 +1,39 @@
import type { NumberSliderProps } from "$/types/InputTypes";
import clsx from "clsx";
export default function NumberSlider({
id,
className,
name,
min,
max,
step,
value,
onChange,
disabled,
ariaLabel
}: Readonly<NumberSliderProps>){
return (
<input
type="range"
id={id}
className={clsx(
"appearance-none [-moz-range-thumb:background:#04AA6D]",
"h-5 px-0.5 rounded-full bg-primary",
className
)}
name={name}
min={min}
max={max}
step={step}
value={value}
onChange={(e) => onChange(e.target.valueAsNumber || 0)}
disabled={disabled}
aria-label={ariaLabel}
aria-valuemin={min}
aria-valuemax={max}
aria-valuenow={value}
/>
);
}

View File

@@ -0,0 +1,19 @@
import type { RadioButtonProps } from "$/types/InputTypes";
import clsx from "clsx";
import RadioButton from "./RadioButton";
export default function DangerRadioButton({
className,
...props
}: Readonly<RadioButtonProps>){
return (
<RadioButton
className={clsx(
"group-data-checked:bg-danger",
className
)}
{...props}
/>
);
}

View File

@@ -0,0 +1,19 @@
import type { RadioButtonProps } from "$/types/InputTypes";
import clsx from "clsx";
import RadioButton from "./RadioButton";
export default function DarkRadioButton({
className,
...props
}: Readonly<RadioButtonProps>){
return (
<RadioButton
className={clsx(
"group-data-checked:bg-dark",
className
)}
{...props}
/>
);
}

View File

@@ -0,0 +1,19 @@
import type { RadioButtonProps } from "$/types/InputTypes";
import clsx from "clsx";
import RadioButton from "./RadioButton";
export default function InfoRadioButton({
className,
...props
}: Readonly<RadioButtonProps>){
return (
<RadioButton
className={clsx(
"group-data-checked:bg-cyan-500",
className
)}
{...props}
/>
);
}

View File

@@ -0,0 +1,19 @@
import type { RadioButtonProps } from "$/types/InputTypes";
import clsx from "clsx";
import RadioButton from "./RadioButton";
export default function LightRadioButton({
className,
...props
}: Readonly<RadioButtonProps>){
return (
<RadioButton
className={clsx(
"group-data-checked:bg-light",
className
)}
{...props}
/>
);
}

View File

@@ -0,0 +1,19 @@
import type { RadioButtonProps } from "$/types/InputTypes";
import clsx from "clsx";
import RadioButton from "./RadioButton";
export default function MoltenRadioButton({
className,
...props
}: Readonly<RadioButtonProps>){
return (
<RadioButton
className={clsx(
"group-data-checked:bg-molten",
className
)}
{...props}
/>
);
}

View File

@@ -0,0 +1,19 @@
import type { RadioButtonProps } from "$/types/InputTypes";
import clsx from "clsx";
import RadioButton from "./RadioButton";
export default function PrimaryRadioButton({
className,
...props
}: Readonly<RadioButtonProps>){
return (
<RadioButton
className={clsx(
"group-data-checked:bg-primary",
className
)}
{...props}
/>
);
}

View File

@@ -0,0 +1,56 @@
import type { RadioButtonProps } from "$/types/InputTypes";
import { Radio } from "@headlessui/react";
import clsx from "clsx";
export default function RadioButton({
id = crypto.randomUUID().replaceAll("-", ""),
className,
labelClassName,
size = "sm",
value,
disabled,
children
}: Readonly<RadioButtonProps>){
return (
<Radio
id={id}
value={value}
className={clsx(
"group",
"flex flex-row items-center justify-center gap-x-2",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
{
"cursor-pointer": !disabled,
"cursor-not-allowed": disabled
}
)}
disabled={disabled}
aria-labelledby={`${id}Label`}
>
<div
className={clsx(
"rounded-full not-group-data-checked:border",
className,
{
"": size === "none",
"size-3": size === "xs",
"size-4": size === "sm",
"size-5": size === "md",
"size-6": size === "lg",
"size-7": size === "xl"
}
)}
/>
{
children &&
<span
id={`${id}Label`}
className={labelClassName}
>
{children}
</span>
}
</Radio>
);
}

View File

@@ -0,0 +1,35 @@
import type { RadioListProps } from "$/types/InputTypes";
import { RadioGroup } from "@headlessui/react";
import clsx from "clsx";
export default function RadioList({
id,
className,
name,
value,
onChange,
defaultValue,
direction = "horizontal",
children
}: Readonly<RadioListProps>){
return (
<RadioGroup
id={id}
className={clsx(
"flex",
className,
{
"flex-row items-center justify-center gap-x-8": direction === "horizontal",
"flex-col items-start justify-center gap-y-2": direction === "vertical"
}
)}
name={name}
value={value}
onChange={onChange}
defaultValue={defaultValue}
>
{children}
</RadioGroup>
);
}

View File

@@ -0,0 +1,19 @@
import type { RadioButtonProps } from "$/types/InputTypes";
import clsx from "clsx";
import RadioButton from "./RadioButton";
export default function SecondaryRadioButton({
className,
...props
}: Readonly<RadioButtonProps>){
return (
<RadioButton
className={clsx(
"group-data-checked:bg-secondary",
className
)}
{...props}
/>
);
}

View File

@@ -0,0 +1,19 @@
import type { RadioButtonProps } from "$/types/InputTypes";
import clsx from "clsx";
import RadioButton from "./RadioButton";
export default function SuccessRadioButton({
className,
...props
}: Readonly<RadioButtonProps>){
return (
<RadioButton
className={clsx(
"group-data-checked:bg-green-600",
className
)}
{...props}
/>
);
}

View File

@@ -0,0 +1,19 @@
import type { RadioButtonProps } from "$/types/InputTypes";
import clsx from "clsx";
import RadioButton from "./RadioButton";
export default function TertiaryRadioButton({
className,
...props
}: Readonly<RadioButtonProps>){
return (
<RadioButton
className={clsx(
"group-data-checked:bg-tertiary",
className
)}
{...props}
/>
);
}

View File

@@ -0,0 +1,19 @@
import type { RadioButtonProps } from "$/types/InputTypes";
import clsx from "clsx";
import RadioButton from "./RadioButton";
export default function WarningRadioButton({
className,
...props
}: Readonly<RadioButtonProps>){
return (
<RadioButton
className={clsx(
"group-data-checked:bg-warning",
className
)}
{...props}
/>
);
}

View File

@@ -0,0 +1,35 @@
import type { MattrixwvButtonSwitchProps } from "$/types/InputTypes";
import { Switch } from "@headlessui/react";
export default function ButtonSwitch({
id,
className,
name,
value,
defaultChecked,
checked,
onChange,
disabled,
onNode,
offNode
}: Readonly<MattrixwvButtonSwitchProps>){
return (
<Switch
id={id}
className={className}
name={name}
value={value}
defaultChecked={defaultChecked}
checked={checked}
onChange={onChange}
disabled={disabled}
>
{({ checked }) => (
<>
{checked ? onNode : offNode}
</>
)}
</Switch>
);
}

View File

@@ -1,7 +1,33 @@
export default function DangerSwitch(){ import type { MattrixwvSwitchProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvSwitch from "./MattrixwvSwitch";
export default function DangerSwitch({
className,
knobClassName,
disabled,
...props
}: Readonly<MattrixwvSwitchProps>){
return ( return (
<div> <MattrixwvSwitch
Danger Switch className={clsx(
</div> "bg-neutral-mid",
className,
{
"data-checked:bg-danger": !disabled,
"data-checked:bg-danger-light/80": disabled
}
)}
knobClassName={clsx(
knobClassName,
{
"bg-white": !disabled,
"bg-neutral-light": disabled
}
)}
disabled={disabled}
{...props}
/>
); );
} }

View File

@@ -1,7 +1,33 @@
export default function DarkSwitch(){ import type { MattrixwvSwitchProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvSwitch from "./MattrixwvSwitch";
export default function DarkSwitch({
className,
knobClassName,
disabled,
...props
}: Readonly<MattrixwvSwitchProps>){
return ( return (
<div> <MattrixwvSwitch
Dark Switch className={clsx(
</div> "bg-neutral-mid",
className,
{
"data-checked:bg-dark": !disabled,
"data-checked:bg-dark-mid/80": disabled
}
)}
knobClassName={clsx(
knobClassName,
{
"bg-white": !disabled,
"bg-neutral-light": disabled
}
)}
disabled={disabled}
{...props}
/>
); );
} }

View File

@@ -0,0 +1,33 @@
import type { MattrixwvSwitchProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvSwitch from "./MattrixwvSwitch";
export default function InfoSwitch({
className,
knobClassName,
disabled,
...props
}: Readonly<MattrixwvSwitchProps>){
return (
<MattrixwvSwitch
className={clsx(
"bg-gray-400",
className,
{
"data-checked:bg-info": !disabled,
"data-checked:bg-info-light/80": disabled
}
)}
knobClassName={clsx(
knobClassName,
{
"bg-white": !disabled,
"bg-neutral-light": disabled
}
)}
disabled={disabled}
{...props}
/>
);
}

View File

@@ -1,7 +1,33 @@
export default function LightSwitch(){ import type { MattrixwvSwitchProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvSwitch from "./MattrixwvSwitch";
export default function LightSwitch({
className,
knobClassName,
disabled,
...props
}: Readonly<MattrixwvSwitchProps>){
return ( return (
<div> <MattrixwvSwitch
Light Switch className={clsx(
</div> "bg-neutral-mid",
className,
{
"data-checked:bg-light": !disabled,
"data-checked:bg-light/80": disabled
}
)}
knobClassName={clsx(
knobClassName,
{
"bg-black": !disabled,
"bg-neutral-dark": disabled
}
)}
disabled={disabled}
{...props}
/>
); );
} }

View File

@@ -0,0 +1,108 @@
import type { MattrixwvSwitchProps } from "$/types/InputTypes";
import { Field, Label, Switch } from "@headlessui/react";
import clsx from "clsx";
import { Fragment } from "react/jsx-runtime";
export default function MattrixwvSwitch({
id,
className,
knobClassName,
size = "sm",
wide,
name,
value,
defaultChecked,
checked,
onChange,
disabled,
children,
offText,
onText
}: Readonly<MattrixwvSwitchProps>){
return (
<Field as={Fragment}>
<Switch
id={id}
className={clsx(
"relative group inline-flex items-center rounded-full transition",
className,
{
"cursor-pointer": !disabled,
"cursor-not-allowed": disabled
},
{
//Normal
"h-4 w-7": size === "xs" && !wide,
"h-5 w-9": size === "sm" && !wide,
"h-6 w-11": size === "md" && !wide,
"h-7 w-13": size === "lg" && !wide,
"h-8 w-15": size === "xl" && !wide,
//Wide
"h-4 w-9": size === "xs" && wide,
"h-5 w-11": size === "sm" && wide,
"h-6 w-13": size === "md" && wide,
"h-7 w-15": size === "lg" && wide,
"h-8 w-17": size === "xl" && wide
}
)}
name={name}
value={value}
defaultChecked={defaultChecked}
checked={checked}
onChange={onChange}
disabled={disabled}
>
{
offText &&
<span className="block group-data-checked:hidden absolute right-2">{offText}</span>
}
{
onText &&
<span className="hidden group-data-checked:block absolute left-2">{onText}</span>
}
<span
className={clsx(
"rounded-full transition",
knobClassName,
//Size
{
"size-2": size === "xs",
"size-3": size === "sm",
"size-4": size === "md",
"size-5": size === "lg",
"size-6": size === "xl"
},
//Transitions
{
//Normal
"translate-x-1 group-data-checked:translate-x-4": size === "xs" && !wide,
"translate-x-1 group-data-checked:translate-x-5": size === "sm" && !wide,
"translate-x-1 group-data-checked:translate-x-6": size === "md" && !wide,
"translate-x-1 group-data-checked:translate-x-7": size === "lg" && !wide,
"translate-x-1 group-data-checked:translate-x-8": size === "xl" && !wide,
//Wide
"translate-x-1 group-data-checked:translate-x-6 ": size === "xs" && wide,
"translate-x-1 group-data-checked:translate-x-7 ": size === "sm" && wide,
"translate-x-1 group-data-checked:translate-x-8 ": size === "md" && wide,
"translate-x-1 group-data-checked:translate-x-9 ": size === "lg" && wide,
"translate-x-1 group-data-checked:translate-x-10 ": size === "xl" && wide
}
)}
/>
</Switch>
<Label
className={clsx(
{
"cursor-pointer": !disabled,
"cursor-not-allowed": disabled
}
)}
>
{children}
</Label>
</Field>
);
}

View File

@@ -0,0 +1,33 @@
import type { MattrixwvSwitchProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvSwitch from "./MattrixwvSwitch";
export default function MoltenSwitch({
className,
knobClassName,
disabled,
...props
}: Readonly<MattrixwvSwitchProps>){
return (
<MattrixwvSwitch
className={clsx(
"bg-neutral-mid",
className,
{
"data-checked:bg-molten": !disabled,
"data-checked:bg-molten-light/80": disabled
}
)}
knobClassName={clsx(
knobClassName,
{
"bg-white": !disabled,
"bg-neutral-light": disabled
}
)}
disabled={disabled}
{...props}
/>
);
}

View File

@@ -1,7 +0,0 @@
export default function PlainSwitch(){
return (
<div>
Plain Switch
</div>
);
}

View File

@@ -1,7 +1,33 @@
export default function PrimarySwitch(){ import type { MattrixwvSwitchProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvSwitch from "./MattrixwvSwitch";
export default function PrimarySwitch({
className,
knobClassName,
disabled,
...props
}: Readonly<MattrixwvSwitchProps>){
return ( return (
<div> <MattrixwvSwitch
Primary Switch className={clsx(
</div> "bg-neutral-mid",
className,
{
"data-checked:bg-primary": !disabled,
"data-checked:bg-primary-light/80": disabled
}
)}
knobClassName={clsx(
knobClassName,
{
"bg-white": !disabled,
"bg-neutral-light": disabled
}
)}
disabled={disabled}
{...props}
/>
); );
} }

View File

@@ -1,7 +1,33 @@
export default function SecondarySwitch(){ import type { MattrixwvSwitchProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvSwitch from "./MattrixwvSwitch";
export default function SecondarySwitch({
className,
knobClassName,
disabled,
...props
}: Readonly<MattrixwvSwitchProps>){
return ( return (
<div> <MattrixwvSwitch
Secondary Switch className={clsx(
</div> "bg-neutral-mid",
className,
{
"data-checked:bg-secondary": !disabled,
"data-checked:bg-secondary-light/80": disabled
}
)}
knobClassName={clsx(
knobClassName,
{
"bg-white": !disabled,
"bg-neutral-light": disabled
}
)}
disabled={disabled}
{...props}
/>
); );
} }

View File

@@ -1,7 +1,32 @@
export default function SuccessDangerSwitch(){ import type { MattrixwvSwitchProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvSwitch from "./MattrixwvSwitch";
export default function SuccessDangerSwitch({
className,
knobClassName,
disabled,
...props
}: Readonly<MattrixwvSwitchProps>){
return ( return (
<div> <MattrixwvSwitch
Success Danger Switch className={clsx(
</div> className,
{
"bg-danger data-checked:bg-success": !disabled,
"bg-danger-light/80 data-checked:bg-success-light/80": disabled
}
)}
knobClassName={clsx(
knobClassName,
{
"bg-white": !disabled,
"bg-neutral-light": disabled
}
)}
disabled={disabled}
{...props}
/>
); );
} }

View File

@@ -1,7 +1,33 @@
export default function SuccessSwitch(){ import type { MattrixwvSwitchProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvSwitch from "./MattrixwvSwitch";
export default function SuccessSwitch({
className,
knobClassName,
disabled,
...props
}: Readonly<MattrixwvSwitchProps>){
return ( return (
<div> <MattrixwvSwitch
Success Switch className={clsx(
</div> "bg-neutral-mid",
className,
{
"data-checked:bg-success": !disabled,
"data-checked:bg-success-light/80": disabled
}
)}
knobClassName={clsx(
knobClassName,
{
"bg-white": !disabled,
"bg-neutral-light": disabled
}
)}
disabled={disabled}
{...props}
/>
); );
} }

View File

@@ -1,7 +0,0 @@
export default function Switch(){
return (
<div>
Switch
</div>
);
}

View File

@@ -1,7 +1,33 @@
export default function TertiarySwitch(){ import type { MattrixwvSwitchProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvSwitch from "./MattrixwvSwitch";
export default function TertiarySwitch({
className,
knobClassName,
disabled,
...props
}: Readonly<MattrixwvSwitchProps>){
return ( return (
<div> <MattrixwvSwitch
Tertiary Switch className={clsx(
</div> "bg-gray-400",
className,
{
"data-checked:bg-tertiary": !disabled,
"data-checked:bg-tertiary-light/80": disabled
}
)}
knobClassName={clsx(
knobClassName,
{
"bg-white": !disabled,
"bg-neutral-light": disabled
}
)}
disabled={disabled}
{...props}
/>
); );
} }

View File

@@ -1,7 +1,33 @@
export default function WarningSwitch(){ import type { MattrixwvSwitchProps } from "$/types/InputTypes";
import clsx from "clsx";
import MattrixwvSwitch from "./MattrixwvSwitch";
export default function WarningSwitch({
className,
knobClassName,
disabled,
...props
}: Readonly<MattrixwvSwitchProps>){
return ( return (
<div> <MattrixwvSwitch
Warning Switch className={clsx(
</div> "bg-neutral-mid",
className,
{
"data-checked:bg-warning": !disabled,
"data-checked:bg-warning-light/80": disabled
}
)}
knobClassName={clsx(
knobClassName,
{
"bg-white": !disabled,
"bg-neutral-light": disabled
}
)}
disabled={disabled}
{...props}
/>
); );
} }

View File

@@ -1,17 +1,15 @@
import type { OptionInputProps } from "$/types/Input"; import type { OptionInputProps } from "$/types/InputTypes";
import { ListboxOption } from "@headlessui/react"; import { ListboxOption } from "@headlessui/react";
import clsx from "clsx"; import clsx from "clsx";
export default function OptionInput(props: OptionInputProps){ export default function OptionInput({
const {
id, id,
className, className,
value, value,
disabled,
children children
} = props; }: Readonly<OptionInputProps>){
return ( return (
<ListboxOption <ListboxOption
id={id} id={id}
@@ -20,6 +18,7 @@ export default function OptionInput(props: OptionInputProps){
className className
)} )}
value={value} value={value}
disabled={disabled}
> >
{children} {children}
</ListboxOption> </ListboxOption>

View File

@@ -0,0 +1,39 @@
import type { SelectInputProps } from "$/types/InputTypes";
import { Listbox, ListboxButton, ListboxOptions } from "@headlessui/react";
import clsx from "clsx";
import { BsChevronDown } from "react-icons/bs";
export default function SelectInput({
placeholder,
value,
onChange,
disabled,
children
}: Readonly<SelectInputProps>){
return (
<Listbox
value={value}
onChange={onChange}
disabled={disabled}
>
<ListboxButton
className={clsx(
"group relative flex flex-row items-center justify-between w-full",
"border-2 px-2 py-1 rounded-lg",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2"
//"not-data-open:rounded-lg data-open:rounded-t-lg"
)}
>
<span>{placeholder}</span>
<span className="block group-data-open:rotate-180 transition-transform duration-250"><BsChevronDown size={22}/></span>
</ListboxButton>
<ListboxOptions
className="w-(--button-width) max-h-60! overflow-hidden z-10 outline-none rounded-lg"
anchor="bottom"
>
{children}
</ListboxOptions>
</Listbox>
);
}

View File

@@ -0,0 +1,67 @@
import type { TextAreaProps } from "$/types/InputTypes";
import clsx from "clsx";
import { useId } from "react";
export default function TextArea({
id,
className,
inputClassName,
labelClassName,
name,
maxLength,
rows = 3,
cols,
spellCheck,
placeholder,
value,
onChange,
disabled
}: Readonly<TextAreaProps>){
const componentId = useId();
const activeId = id ?? componentId;
return (
<div
className={clsx(
"flex flex-row items-center justify-center rounded-lg border-2 w-full",
className
)}
>
<div
className="relative flex flex-row items-center justify-center px-2 py-1 w-full"
>
<textarea
id={activeId}
className={clsx(
"peer bg-transparent outline-none placeholder-transparent w-full",
inputClassName
)}
placeholder={placeholder}
name={name}
maxLength={maxLength}
rows={rows}
cols={cols}
value={value}
onChange={onChange}
disabled={disabled}
spellCheck={spellCheck}
/>
<label
className={clsx(
"absolute ml-2 -top-3 left-0 text-sm rounded-md px-1 select-none cursor-default",
"peer-placeholder-shown:top-0 peer-placeholder-shown:-left-1 peer-placeholder-shown:text-inherit peer-placeholder-shown:text-base peer-placeholder-shown:bg-transparent peer-placeholder-shown:cursor-text peer-placeholder-shown:w-[99%]",
"peer-focus:-top-3 peer-focus:left-0 peer-focus:text-sm peer-focus:w-auto peer-focus:h-auto",
"flex items-center",
labelClassName
)}
style={{ transitionProperty: "top, left, font-size, line-height", transitionTimingFunction: "cubic-bezier(0.4 0, 0.2, 1)", transitionDuration: "250ms" }}
htmlFor={activeId}
>
{placeholder}
</label>
</div>
</div>
);
}

View File

@@ -0,0 +1,64 @@
import type { TextInputProps } from "$/types/InputTypes";
import clsx from "clsx";
import { useId } from "react";
export default function TextInput({
id,
className,
inputClassName,
labelClassName,
name,
maxLength,
spellCheck,
placeholder,
value,
onChange,
disabled
}: Readonly<TextInputProps>){
const componentId = useId();
const activeId = id ?? componentId;
return (
<div
className={clsx(
"flex flex-row items-center justify-center rounded-lg border-2 w-full",
className
)}
>
<div
className="relative flex flex-row items-center justify-center px-2 py-1 w-full"
>
<input
type="text"
id={activeId}
className={clsx(
"peer bg-transparent outline-none placeholder-transparent w-full",
inputClassName
)}
name={name}
placeholder={placeholder}
maxLength={maxLength}
value={value}
onChange={onChange}
disabled={disabled}
spellCheck={spellCheck}
/>
<label
className={clsx(
"absolute ml-2 -top-3 left-0 text-sm rounded-md px-1 select-none cursor-default",
"peer-placeholder-shown:top-0 peer-placeholder-shown:-left-1 peer-placeholder-shown:text-inherit peer-placeholder-shown:text-base peer-placeholder-shown:bg-transparent peer-placeholder-shown:h-full peer-placeholder-shown:cursor-text peer-placeholder-shown:w-[99%]",
"peer-focus:-top-3 peer-focus:left-0 peer-focus:text-sm peer-focus:w-auto peer-focus:h-auto",
"flex items-center",
labelClassName
)}
style={{ transitionProperty: "top, left, font-size, line-height", transitionTimingFunction: "cubic-bezier(0.4 0, 0.2, 1)", transitionDuration: "250ms" }}
htmlFor={activeId}
>
{placeholder}
</label>
</div>
</div>
);
}

View File

@@ -1,90 +0,0 @@
import CenterGrowingBars from "./loading/bar/CenterGrowingBars";
import CircleBars from "./loading/bar/CircleBars";
import FadingBars from "./loading/bar/FadingBars";
import FadingGrowingBars from "./loading/bar/FadingGrowingBars";
import GrowingBars from "./loading/bar/GrowingBars";
import PulsingBlocks from "./loading/block/PulsingBlocks";
import SlidingBlocks2 from "./loading/block/SlidingBlocks2";
import SlidingBlocks3 from "./loading/block/SlidingBlocks3";
import WaveBlocks from "./loading/block/WaveBlocks";
import BouncingDots from "./loading/dot/BouncingDots";
import CircleCenterDots from "./loading/dot/CircleCenterDots";
import CircleFadingDots from "./loading/dot/CircleFadingDots";
import CirclePulsingDots from "./loading/dot/CirclePulsingDots";
import CircleRotatingDots from "./loading/dot/CircleRotatingDots";
import CircleShrinkingDots from "./loading/dot/CircleShrinkingDots";
import CircleSpinningDot from "./loading/dot/CircleSpinningDot";
import CyclingDots from "./loading/dot/CyclingDots";
import FadingDots from "./loading/dot/FadingDots";
import PulsingDots from "./loading/dot/PulsingDots";
import RotatingDots from "./loading/dot/RotatingDots";
import SwellingDots from "./loading/dot/SwellingDots";
import DoubleDrop from "./loading/drop/DoubleDrop";
import DoubleWaveDrop from "./loading/drop/DoubleWaveDrop";
import Drop from "./loading/drop/Drop";
import QuickDrop from "./loading/drop/QuickDrop";
import QuickWaveDrop from "./loading/drop/QuickWaveDrop";
import TripleDrop from "./loading/drop/TripleDrop";
import TripleWaveDrop from "./loading/drop/TripleWaveDrop";
import WaveDrop from "./loading/drop/WaveDrop";
import HalfSpinner from "./loading/spinner/HalfSpinner";
import QuarterSpinner from "./loading/spinner/QuarterSpinner";
import RubberSpinner from "./loading/spinner/RubberSpinner";
import ThreeQuarterSpinner from "./loading/spinner/ThreeQuarterSpinner";
import BouncingDot from "./loading/various/BouncingDot";
import PulsingLine from "./loading/various/PulsingLine";
import SpinningBinary from "./loading/various/SpinningBinary";
import SpinningClock from "./loading/various/SpinningClock";
import SpinningEclipse from "./loading/various/SpinningEclipse";
import SpinningEclipseHalf from "./loading/various/SpinningEclipseHalf";
import SpinningGalaxy from "./loading/various/SpinningGalaxy";
import SpinningTadpole from "./loading/various/SpinningTadpole";
import Wifi from "./loading/various/Wifi";
export {
BouncingDot,
BouncingDots,
CenterGrowingBars,
CircleBars,
CircleCenterDots,
CircleFadingDots,
CirclePulsingDots,
CircleRotatingDots,
CircleShrinkingDots,
CircleSpinningDot,
CyclingDots,
DoubleDrop,
DoubleWaveDrop,
Drop,
FadingBars,
FadingDots,
FadingGrowingBars,
GrowingBars,
HalfSpinner,
PulsingBlocks,
PulsingDots,
PulsingLine,
QuarterSpinner,
QuickDrop,
QuickWaveDrop,
RotatingDots,
RubberSpinner,
SlidingBlocks2,
SlidingBlocks3,
SpinningBinary,
SpinningClock,
SpinningEclipse,
SpinningEclipseHalf,
SpinningGalaxy,
SpinningTadpole,
SwellingDots,
ThreeQuarterSpinner,
TripleDrop,
TripleWaveDrop,
WaveBlocks,
WaveDrop,
Wifi
};

View File

@@ -1,22 +1,32 @@
import type { LoadingBarsProps } from "$/types/Loading"; import type { LoadingBarsProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function CenterGrowingBars({ export default function CenterGrowingBars({
size,
width, width,
height, height,
className, className,
animationDuration = 0.6, animationDuration = 600,
color,
stroke, stroke,
fill fill,
}: LoadingBarsProps){ ariaLabel = "Loading"
}: Readonly<LoadingBarsProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/bars-scale-middle.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/bars-scale-middle.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -26,22 +36,22 @@ export default function CenterGrowingBars({
width="2.8" width="2.8"
height="12" height="12"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle3_${id}.begin+${animationDuration * 2 / 3}s`} begin={`rectangle3_${id}.begin+${dur * 2 / 3}s`}
attributeName="y" attributeName="y"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="6;1;6" values="6;1;6"
keySplines=".14,.73,.34,1;.65,.26,.82,.45" keySplines=".14,.73,.34,1;.65,.26,.82,.45"
/> />
<animate <animate
begin={`rectangle3_${id}.begin+${animationDuration * 2 / 3}s`} begin={`rectangle3_${id}.begin+${dur * 2 / 3}s`}
attributeName="height" attributeName="height"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="12;22;12" values="12;22;12"
keySplines=".14,.73,.34,1;.65,.26,.82,.45" keySplines=".14,.73,.34,1;.65,.26,.82,.45"
/> />
@@ -52,22 +62,22 @@ export default function CenterGrowingBars({
width="2.8" width="2.8"
height="12" height="12"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle3_${id}.begin+${animationDuration / 3}s`} begin={`rectangle3_${id}.begin+${dur / 3}s`}
attributeName="y" attributeName="y"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="6;1;6" values="6;1;6"
keySplines=".14,.73,.34,1;.65,.26,.82,.45" keySplines=".14,.73,.34,1;.65,.26,.82,.45"
/> />
<animate <animate
begin={`rectangle3_${id}.begin+${animationDuration / 3}s`} begin={`rectangle3_${id}.begin+${dur / 3}s`}
attributeName="height" attributeName="height"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="12;22;12" values="12;22;12"
keySplines=".14,.73,.34,1;.65,.26,.82,.45" keySplines=".14,.73,.34,1;.65,.26,.82,.45"
/> />
@@ -78,23 +88,23 @@ export default function CenterGrowingBars({
width="2.8" width="2.8"
height="12" height="12"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle3_${id}`} id={`rectangle3_${id}`}
begin={`0;rectangle5_${id}.end-${animationDuration / 6}s`} begin={`0;rectangle5_${id}.end-${dur / 6}s`}
attributeName="y" attributeName="y"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="6;1;6" values="6;1;6"
keySplines=".14,.73,.34,1;.65,.26,.82,.45" keySplines=".14,.73,.34,1;.65,.26,.82,.45"
/> />
<animate <animate
begin={`0;rectangle5_${id}.end-${animationDuration / 6}s`} begin={`0;rectangle5_${id}.end-${dur / 6}s`}
attributeName="height" attributeName="height"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="12;22;12" values="12;22;12"
keySplines=".14,.73,.34,1;.65,.26,.82,.45" keySplines=".14,.73,.34,1;.65,.26,.82,.45"
/> />
@@ -105,22 +115,22 @@ export default function CenterGrowingBars({
width="2.8" width="2.8"
height="12" height="12"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle3_${id}.begin+${animationDuration / 3}s`} begin={`rectangle3_${id}.begin+${dur / 3}s`}
attributeName="y" attributeName="y"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="6;1;6" values="6;1;6"
keySplines=".14,.73,.34,1;.65,.26,.82,.45" keySplines=".14,.73,.34,1;.65,.26,.82,.45"
/> />
<animate <animate
begin={`rectangle3_${id}.begin+${animationDuration / 3}s`} begin={`rectangle3_${id}.begin+${dur / 3}s`}
attributeName="height" attributeName="height"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="12;22;12" values="12;22;12"
keySplines=".14,.73,.34,1;.65,.26,.82,.45" keySplines=".14,.73,.34,1;.65,.26,.82,.45"
/> />
@@ -131,23 +141,23 @@ export default function CenterGrowingBars({
width="2.8" width="2.8"
height="12" height="12"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle5_${id}`} id={`rectangle5_${id}`}
begin={`rectangle3_${id}.begin+${animationDuration * 2 / 3}s`} begin={`rectangle3_${id}.begin+${dur * 2 / 3}s`}
attributeName="y" attributeName="y"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="6;1;6" values="6;1;6"
keySplines=".14,.73,.34,1;.65,.26,.82,.45" keySplines=".14,.73,.34,1;.65,.26,.82,.45"
/> />
<animate <animate
begin={`rectangle3_${id}.begin+${animationDuration * 2 / 3}s`} begin={`rectangle3_${id}.begin+${dur * 2 / 3}s`}
attributeName="height" attributeName="height"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="12;22;12" values="12;22;12"
keySplines=".14,.73,.34,1;.65,.26,.82,.45" keySplines=".14,.73,.34,1;.65,.26,.82,.45"
/> />

View File

@@ -1,21 +1,30 @@
import type { LoadingBarsProps } from "$/types/Loading"; import type { LoadingBarsProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
export default function CircleBars({ export default function CircleBars({
size,
width, width,
height, height,
className, className,
animationDuration = 0.75, animationDuration = 750,
color,
stroke, stroke,
fill fill,
}: LoadingBarsProps){ ariaLabel = "Loading"
}: Readonly<LoadingBarsProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/bars-rotate-fade.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/bars-rotate-fade.svg
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -27,8 +36,8 @@ export default function CircleBars({
height="5" height="5"
opacity=".14" opacity=".14"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<rect <rect
x="11" x="11"
@@ -38,8 +47,8 @@ export default function CircleBars({
transform="rotate(30 12 12)" transform="rotate(30 12 12)"
opacity=".29" opacity=".29"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<rect <rect
x="11" x="11"
@@ -49,8 +58,8 @@ export default function CircleBars({
transform="rotate(60 12 12)" transform="rotate(60 12 12)"
opacity=".43" opacity=".43"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<rect <rect
x="11" x="11"
@@ -60,8 +69,8 @@ export default function CircleBars({
transform="rotate(90 12 12)" transform="rotate(90 12 12)"
opacity=".57" opacity=".57"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<rect <rect
x="11" x="11"
@@ -71,8 +80,8 @@ export default function CircleBars({
transform="rotate(120 12 12)" transform="rotate(120 12 12)"
opacity=".71" opacity=".71"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<rect <rect
x="11" x="11"
@@ -82,8 +91,8 @@ export default function CircleBars({
transform="rotate(150 12 12)" transform="rotate(150 12 12)"
opacity=".86" opacity=".86"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<rect <rect
x="11" x="11"
@@ -92,14 +101,14 @@ export default function CircleBars({
height="5" height="5"
transform="rotate(180 12 12)" transform="rotate(180 12 12)"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<animateTransform <animateTransform
attributeName="transform" attributeName="transform"
type="rotate" type="rotate"
calcMode="discrete" calcMode="discrete"
dur={animationDuration} dur={dur}
values="0 12 12;30 12 12;60 12 12;90 12 12;120 12 12;150 12 12;180 12 12;210 12 12;240 12 12;270 12 12;300 12 12;330 12 12" values="0 12 12;30 12 12;60 12 12;90 12 12;120 12 12;150 12 12;180 12 12;210 12 12;240 12 12;270 12 12;300 12 12;330 12 12"
repeatCount="indefinite" repeatCount="indefinite"
/> />

View File

@@ -1,22 +1,32 @@
import type { LoadingBarsProps } from "$/types/Loading"; import type { LoadingBarsProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function FadingBars({ export default function FadingBars({
size,
width, width,
height, height,
className, className,
animationDuration = 0.75, animationDuration = 750,
color,
stroke, stroke,
fill fill,
}: LoadingBarsProps){ ariaLabel = "Loading"
}: Readonly<LoadingBarsProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/bars-fade.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/bars-fade.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -27,14 +37,14 @@ export default function FadingBars({
height="14" height="14"
opacity="1" opacity="1"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle1_${id}`} id={`rectangle1_${id}`}
begin={`0;rectangle3_${id}.end-${animationDuration / 3}s`} begin={`0;rectangle3_${id}.end-${dur / 3}s`}
attributeName="opacity" attributeName="opacity"
dur={animationDuration} dur={dur}
values="1;.2" values="1;.2"
fill="freeze" fill="freeze"
/> />
@@ -46,13 +56,13 @@ export default function FadingBars({
height="14" height="14"
opacity=".4" opacity=".4"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 5}s`} begin={`rectangle1_${id}.begin+${dur / 5}s`}
attributeName="opacity" attributeName="opacity"
dur={animationDuration} dur={dur}
values="1;.2" values="1;.2"
fill="freeze" fill="freeze"
/> />
@@ -64,14 +74,14 @@ export default function FadingBars({
height="14" height="14"
opacity=".3" opacity=".3"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle3_${id}`} id={`rectangle3_${id}`}
begin={`rectangle1_${id}.begin+${animationDuration * 2 / 5}s`} begin={`rectangle1_${id}.begin+${dur * 2 / 5}s`}
attributeName="opacity" attributeName="opacity"
dur={animationDuration} dur={dur}
values="1;.2" values="1;.2"
fill="freeze" fill="freeze"
/> />

View File

@@ -1,22 +1,32 @@
import type { LoadingBarsProps } from "$/types/Loading"; import type { LoadingBarsProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function FadingGrowingBars({ export default function FadingGrowingBars({
size,
width, width,
height, height,
className, className,
animationDuration = 0.75, animationDuration = 750,
color,
stroke, stroke,
fill fill,
}: LoadingBarsProps){ ariaLabel = "Loading"
}: Readonly<LoadingBarsProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/bars-scale-fade.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/bars-scale-fade.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -27,28 +37,28 @@ export default function FadingGrowingBars({
height="14" height="14"
opacity="1" opacity="1"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle1_${id}`} id={`rectangle1_${id}`}
begin={`0;rectangle3_${id}.end-${animationDuration / 3}s`} begin={`0;rectangle3_${id}.end-${dur / 3}s`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="1;5" values="1;5"
fill="freeze" fill="freeze"
/> />
<animate <animate
begin={`0;rectangle3_${id}.end-${animationDuration / 3}s`} begin={`0;rectangle3_${id}.end-${dur / 3}s`}
attributeName="height" attributeName="height"
dur={animationDuration} dur={dur}
values="22;14" values="22;14"
fill="freeze" fill="freeze"
/> />
<animate <animate
begin={`0;rectangle3_${id}.end-${animationDuration / 3}s`} begin={`0;rectangle3_${id}.end-${dur / 3}s`}
attributeName="opacity" attributeName="opacity"
dur={animationDuration} dur={dur}
values="1;.2" values="1;.2"
fill="freeze" fill="freeze"
/> />
@@ -60,27 +70,27 @@ export default function FadingGrowingBars({
height="14" height="14"
opacity=".4" opacity=".4"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 5}s`} begin={`rectangle1_${id}.begin+${dur / 5}s`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="1;5" values="1;5"
fill="freeze" fill="freeze"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 5}s`} begin={`rectangle1_${id}.begin+${dur / 5}s`}
attributeName="height" attributeName="height"
dur={animationDuration} dur={dur}
values="22;14" values="22;14"
fill="freeze" fill="freeze"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 5}s`} begin={`rectangle1_${id}.begin+${dur / 5}s`}
attributeName="opacity" attributeName="opacity"
dur={animationDuration} dur={dur}
values="1;.2" values="1;.2"
fill="freeze" fill="freeze"
/> />
@@ -92,28 +102,28 @@ export default function FadingGrowingBars({
height="14" height="14"
opacity=".3" opacity=".3"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle3_${id}`} id={`rectangle3_${id}`}
begin={`rectangle1_${id}.begin+${animationDuration * 2 / 5}s`} begin={`rectangle1_${id}.begin+${dur * 2 / 5}s`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="1;5" values="1;5"
fill="freeze" fill="freeze"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration * 2 / 5}s`} begin={`rectangle1_${id}.begin+${dur * 2 / 5}s`}
attributeName="height" attributeName="height"
dur={animationDuration} dur={dur}
values="22;14" values="22;14"
fill="freeze" fill="freeze"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration * 2 / 5}s`} begin={`rectangle1_${id}.begin+${dur * 2 / 5}s`}
attributeName="opacity" attributeName="opacity"
dur={animationDuration} dur={dur}
values="1;.2" values="1;.2"
fill="freeze" fill="freeze"
/> />

View File

@@ -1,22 +1,32 @@
import type { LoadingBarsProps } from "$/types/Loading"; import type { LoadingBarsProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function GrowingBars({ export default function GrowingBars({
size,
width, width,
height, height,
className, className,
animationDuration = 0.6, animationDuration = 600,
color,
stroke, stroke,
fill fill,
}: LoadingBarsProps){ ariaLabel = "Loading"
}: Readonly<LoadingBarsProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/bars-scale.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/bars-scale.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -26,23 +36,23 @@ export default function GrowingBars({
width="2.8" width="2.8"
height="12" height="12"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle1_${id}`} id={`rectangle1_${id}`}
begin={`0;rectangle5_${id}.end-${animationDuration / 6}s`} begin={`0;rectangle5_${id}.end-${dur / 6}s`}
attributeName="y" attributeName="y"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="6;1;6" values="6;1;6"
keySplines=".36,.61,.3,.98;.36,.61,.3,.98" keySplines=".36,.61,.3,.98;.36,.61,.3,.98"
/> />
<animate <animate
begin={`0;rectangle5_${id}.end-${animationDuration / 6}s`} begin={`0;rectangle5_${id}.end-${dur / 6}s`}
attributeName="height" attributeName="height"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="12;22;12" values="12;22;12"
keySplines=".36,.61,.3,.98;.36,.61,.3,.98" keySplines=".36,.61,.3,.98;.36,.61,.3,.98"
/> />
@@ -53,22 +63,22 @@ export default function GrowingBars({
width="2.8" width="2.8"
height="12" height="12"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 6}s`} begin={`rectangle1_${id}.begin+${dur / 6}s`}
attributeName="y" attributeName="y"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="6;1;6" values="6;1;6"
keySplines=".36,.61,.3,.98;.36,.61,.3,.98" keySplines=".36,.61,.3,.98;.36,.61,.3,.98"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 6}s`} begin={`rectangle1_${id}.begin+${dur / 6}s`}
attributeName="height" attributeName="height"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="12;22;12" values="12;22;12"
keySplines=".36,.61,.3,.98;.36,.61,.3,.98" keySplines=".36,.61,.3,.98;.36,.61,.3,.98"
/> />
@@ -79,22 +89,22 @@ export default function GrowingBars({
width="2.8" width="2.8"
height="12" height="12"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 3}s`} begin={`rectangle1_${id}.begin+${dur / 3}s`}
attributeName="y" attributeName="y"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="6;1;6" values="6;1;6"
keySplines=".36,.61,.3,.98;.36,.61,.3,.98" keySplines=".36,.61,.3,.98;.36,.61,.3,.98"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 3}s`} begin={`rectangle1_${id}.begin+${dur / 3}s`}
attributeName="height" attributeName="height"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="12;22;12" values="12;22;12"
keySplines=".36,.61,.3,.98;.36,.61,.3,.98" keySplines=".36,.61,.3,.98;.36,.61,.3,.98"
/> />
@@ -105,22 +115,22 @@ export default function GrowingBars({
width="2.8" width="2.8"
height="12" height="12"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 2}s`} begin={`rectangle1_${id}.begin+${dur / 2}s`}
attributeName="y" attributeName="y"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="6;1;6" values="6;1;6"
keySplines=".36,.61,.3,.98;.36,.61,.3,.98" keySplines=".36,.61,.3,.98;.36,.61,.3,.98"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 2}s`} begin={`rectangle1_${id}.begin+${dur / 2}s`}
attributeName="height" attributeName="height"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="12;22;12" values="12;22;12"
keySplines=".36,.61,.3,.98;.36,.61,.3,.98" keySplines=".36,.61,.3,.98;.36,.61,.3,.98"
/> />
@@ -131,23 +141,23 @@ export default function GrowingBars({
width="2.8" width="2.8"
height="12" height="12"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle5_${id}`} id={`rectangle5_${id}`}
begin={`rectangle1_${id}.begin+${animationDuration * 2 / 3}s`} begin={`rectangle1_${id}.begin+${dur * 2 / 3}s`}
attributeName="y" attributeName="y"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="6;1;6" values="6;1;6"
keySplines=".36,.61,.3,.98;.36,.61,.3,.98" keySplines=".36,.61,.3,.98;.36,.61,.3,.98"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration * 2 / 3}s`} begin={`rectangle1_${id}.begin+${dur * 2 / 3}s`}
attributeName="height" attributeName="height"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="12;22;12" values="12;22;12"
keySplines=".36,.61,.3,.98;.36,.61,.3,.98" keySplines=".36,.61,.3,.98;.36,.61,.3,.98"
/> />

View File

@@ -1,22 +1,32 @@
import type { LoadingBlocksProps } from "$/types/Loading"; import type { LoadingBlocksProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function PulsingBlocks({ export default function PulsingBlocks({
size,
width, width,
height, height,
className, className,
animationDuration = 0.6, animationDuration = 600,
color,
stroke, stroke,
fill fill,
}: LoadingBlocksProps){ ariaLabel = "Loading"
}: Readonly<LoadingBlocksProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/blocks-scale.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/blocks-scale.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -27,35 +37,35 @@ export default function PulsingBlocks({
width="9" width="9"
height="9" height="9"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle1_${id}`} id={`rectangle1_${id}`}
begin={`0;rectangle4_${id}.end+${animationDuration / 4}s`} begin={`0;rectangle4_${id}.end+${dur / 4}s`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="1.5;.5;1.5" values="1.5;.5;1.5"
keyTimes="0;.2;1" keyTimes="0;.2;1"
/> />
<animate <animate
begin={`0;rectangle4_${id}.end+${animationDuration / 4}s`} begin={`0;rectangle4_${id}.end+${dur / 4}s`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="1.5;.5;1.5" values="1.5;.5;1.5"
keyTimes="0;.2;1" keyTimes="0;.2;1"
/> />
<animate <animate
begin={`0;rectangle4_${id}.end+${animationDuration / 4}s`} begin={`0;rectangle4_${id}.end+${dur / 4}s`}
attributeName="width" attributeName="width"
dur={animationDuration} dur={dur}
values="9;11;9" values="9;11;9"
keyTimes="0;.2;1" keyTimes="0;.2;1"
/> />
<animate <animate
begin={`0;rectangle4_${id}.end+${animationDuration / 4}s`} begin={`0;rectangle4_${id}.end+${dur / 4}s`}
attributeName="height" attributeName="height"
dur={animationDuration} dur={dur}
values="9;11;9" values="9;11;9"
keyTimes="0;.2;1" keyTimes="0;.2;1"
/> />
@@ -67,34 +77,34 @@ export default function PulsingBlocks({
width="9" width="9"
height="9" height="9"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 4}s`} begin={`rectangle1_${id}.begin+${dur / 4}s`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="13.5;12.5;13.5" values="13.5;12.5;13.5"
keyTimes="0;.2;1" keyTimes="0;.2;1"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 4}s`} begin={`rectangle1_${id}.begin+${dur / 4}s`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="1.5;.5;1.5" values="1.5;.5;1.5"
keyTimes="0;.2;1" keyTimes="0;.2;1"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 4}s`} begin={`rectangle1_${id}.begin+${dur / 4}s`}
attributeName="width" attributeName="width"
dur={animationDuration} dur={dur}
values="9;11;9" values="9;11;9"
keyTimes="0;.2;1" keyTimes="0;.2;1"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 4}s`} begin={`rectangle1_${id}.begin+${dur / 4}s`}
attributeName="height" attributeName="height"
dur={animationDuration} dur={dur}
values="9;11;9" values="9;11;9"
keyTimes="0;.2;1" keyTimes="0;.2;1"
/> />
@@ -106,34 +116,34 @@ export default function PulsingBlocks({
width="9" width="9"
height="9" height="9"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 2}s`} begin={`rectangle1_${id}.begin+${dur / 2}s`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="13.5;12.5;13.5" values="13.5;12.5;13.5"
keyTimes="0;.2;1" keyTimes="0;.2;1"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 2}s`} begin={`rectangle1_${id}.begin+${dur / 2}s`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="13.5;12.5;13.5" values="13.5;12.5;13.5"
keyTimes="0;.2;1" keyTimes="0;.2;1"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 2}s`} begin={`rectangle1_${id}.begin+${dur / 2}s`}
attributeName="width" attributeName="width"
dur={animationDuration} dur={dur}
values="9;11;9" values="9;11;9"
keyTimes="0;.2;1" keyTimes="0;.2;1"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration / 2}s`} begin={`rectangle1_${id}.begin+${dur / 2}s`}
attributeName="height" attributeName="height"
dur={animationDuration} dur={dur}
values="9;11;9" values="9;11;9"
keyTimes="0;.2;1" keyTimes="0;.2;1"
/> />
@@ -145,35 +155,35 @@ export default function PulsingBlocks({
width="9" width="9"
height="9" height="9"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle4_${id}`} id={`rectangle4_${id}`}
begin={`rectangle1_${id}.begin+${animationDuration * 3 / 4}s`} begin={`rectangle1_${id}.begin+${dur * 3 / 4}s`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="1.5;.5;1.5" values="1.5;.5;1.5"
keyTimes="0;.2;1" keyTimes="0;.2;1"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration * 3 / 4}s`} begin={`rectangle1_${id}.begin+${dur * 3 / 4}s`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="13.5;12.5;13.5" values="13.5;12.5;13.5"
keyTimes="0;.2;1" keyTimes="0;.2;1"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration * 3 / 4}s`} begin={`rectangle1_${id}.begin+${dur * 3 / 4}s`}
attributeName="width" attributeName="width"
dur={animationDuration} dur={dur}
values="9;11;9" values="9;11;9"
keyTimes="0;.2;1" keyTimes="0;.2;1"
/> />
<animate <animate
begin={`rectangle1_${id}.begin+${animationDuration * 3 / 4}s`} begin={`rectangle1_${id}.begin+${dur * 3 / 4}s`}
attributeName="height" attributeName="height"
dur={animationDuration} dur={dur}
values="9;11;9" values="9;11;9"
keyTimes="0;.2;1" keyTimes="0;.2;1"
/> />

View File

@@ -1,22 +1,32 @@
import type { LoadingBlocksProps } from "$/types/Loading"; import type { LoadingBlocksProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function SlidingBlocks2({ export default function SlidingBlocks2({
size,
width, width,
height, height,
className, className,
animationDuration = 0.2, animationDuration = 200,
color,
stroke, stroke,
fill fill,
}: LoadingBlocksProps){ ariaLabel = "Loading"
}: Readonly<LoadingBlocksProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/blocks-shuffle-2.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/blocks-shuffle-2.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -27,14 +37,14 @@ export default function SlidingBlocks2({
width="10" width="10"
height="10" height="10"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle1_1_${id}`} id={`rectangle1_1_${id}`}
begin={`0;rectangle2_4_${id}.end`} begin={`0;rectangle2_4_${id}.end`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="1;13" values="1;13"
fill="freeze" fill="freeze"
/> />
@@ -42,7 +52,7 @@ export default function SlidingBlocks2({
id={`rectangle1_2_${id}`} id={`rectangle1_2_${id}`}
begin={`rectangle2_1_${id}.end`} begin={`rectangle2_1_${id}.end`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="1;13" values="1;13"
fill="freeze" fill="freeze"
/> />
@@ -50,7 +60,7 @@ export default function SlidingBlocks2({
id={`rectangle1_3_${id}`} id={`rectangle1_3_${id}`}
begin={`rectangle2_2_${id}.end`} begin={`rectangle2_2_${id}.end`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="13;1" values="13;1"
fill="freeze" fill="freeze"
/> />
@@ -58,7 +68,7 @@ export default function SlidingBlocks2({
id={`rectangle1_4_${id}`} id={`rectangle1_4_${id}`}
begin={`rectangle2_3_${id}.end`} begin={`rectangle2_3_${id}.end`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="13;1" values="13;1"
fill="freeze" fill="freeze"
/> />
@@ -70,14 +80,14 @@ export default function SlidingBlocks2({
width="10" width="10"
height="10" height="10"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle2_1_${id}`} id={`rectangle2_1_${id}`}
begin={`rectangle1_1_${id}.end`} begin={`rectangle1_1_${id}.end`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="13;1" values="13;1"
fill="freeze" fill="freeze"
/> />
@@ -85,7 +95,7 @@ export default function SlidingBlocks2({
id={`rectangle2_2_${id}`} id={`rectangle2_2_${id}`}
begin={`rectangle1_2_${id}.end`} begin={`rectangle1_2_${id}.end`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="1;13" values="1;13"
fill="freeze" fill="freeze"
/> />
@@ -93,7 +103,7 @@ export default function SlidingBlocks2({
id={`rectangle2_3_${id}`} id={`rectangle2_3_${id}`}
begin={`rectangle1_3_${id}.end`} begin={`rectangle1_3_${id}.end`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="1;13" values="1;13"
fill="freeze" fill="freeze"
/> />
@@ -101,7 +111,7 @@ export default function SlidingBlocks2({
id={`rectangle2_4_${id}`} id={`rectangle2_4_${id}`}
begin={`rectangle1_4_${id}.end`} begin={`rectangle1_4_${id}.end`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="13;1" values="13;1"
fill="freeze" fill="freeze"
/> />

View File

@@ -1,22 +1,32 @@
import type { LoadingBlocksProps } from "$/types/Loading"; import type { LoadingBlocksProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function SlidingBlocks3({ export default function SlidingBlocks3({
size,
width, width,
height, height,
className, className,
animationDuration = 0.2, animationDuration = 200,
color,
stroke, stroke,
fill fill,
}: LoadingBlocksProps){ ariaLabel = "Loading"
}: Readonly<LoadingBlocksProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/blocks-shuffle-3.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/blocks-shuffle-3.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -27,14 +37,14 @@ export default function SlidingBlocks3({
width="10" width="10"
height="10" height="10"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle1_1_${id}`} id={`rectangle1_1_${id}`}
begin={`0;rectangle3_4_${id}.end`} begin={`0;rectangle3_4_${id}.end`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="1;13" values="1;13"
fill="freeze" fill="freeze"
/> />
@@ -42,7 +52,7 @@ export default function SlidingBlocks3({
id={`rectangle1_2_${id}`} id={`rectangle1_2_${id}`}
begin={`rectangle3_1_${id}.end`} begin={`rectangle3_1_${id}.end`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="1;13" values="1;13"
fill="freeze" fill="freeze"
/> />
@@ -50,7 +60,7 @@ export default function SlidingBlocks3({
id={`rectangle1_3_${id}`} id={`rectangle1_3_${id}`}
begin={`rectangle3_2_${id}.end`} begin={`rectangle3_2_${id}.end`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="13;1" values="13;1"
fill="freeze" fill="freeze"
/> />
@@ -58,7 +68,7 @@ export default function SlidingBlocks3({
id={`rectangle1_4_${id}`} id={`rectangle1_4_${id}`}
begin={`rectangle3_3_${id}.end`} begin={`rectangle3_3_${id}.end`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="13;1" values="13;1"
fill="freeze" fill="freeze"
/> />
@@ -70,14 +80,14 @@ export default function SlidingBlocks3({
width="10" width="10"
height="10" height="10"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle2_1_${id}`} id={`rectangle2_1_${id}`}
begin={`rectangle1_1_${id}.end`} begin={`rectangle1_1_${id}.end`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="13;1" values="13;1"
fill="freeze" fill="freeze"
/> />
@@ -85,7 +95,7 @@ export default function SlidingBlocks3({
id={`rectangle2_2_${id}`} id={`rectangle2_2_${id}`}
begin={`rectangle1_2_${id}.end`} begin={`rectangle1_2_${id}.end`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="1;13" values="1;13"
fill="freeze" fill="freeze"
/> />
@@ -93,7 +103,7 @@ export default function SlidingBlocks3({
id={`rectangle2_3_${id}`} id={`rectangle2_3_${id}`}
begin={`rectangle1_3_${id}.end`} begin={`rectangle1_3_${id}.end`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="1;13" values="1;13"
fill="freeze" fill="freeze"
/> />
@@ -101,7 +111,7 @@ export default function SlidingBlocks3({
id={`rectangle2_4_${id}`} id={`rectangle2_4_${id}`}
begin={`rectangle1_4_${id}.end`} begin={`rectangle1_4_${id}.end`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="13;1" values="13;1"
fill="freeze" fill="freeze"
/> />
@@ -113,14 +123,14 @@ export default function SlidingBlocks3({
width="10" width="10"
height="10" height="10"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle3_1_${id}`} id={`rectangle3_1_${id}`}
begin={`rectangle2_1_${id}.end`} begin={`rectangle2_1_${id}.end`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="13;1" values="13;1"
fill="freeze" fill="freeze"
/> />
@@ -128,7 +138,7 @@ export default function SlidingBlocks3({
id={`rectangle3_2_${id}`} id={`rectangle3_2_${id}`}
begin={`rectangle2_2_${id}.end`} begin={`rectangle2_2_${id}.end`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="13;1" values="13;1"
fill="freeze" fill="freeze"
/> />
@@ -136,7 +146,7 @@ export default function SlidingBlocks3({
id={`rectangle3_3_${id}`} id={`rectangle3_3_${id}`}
begin={`rectangle2_3_${id}.end`} begin={`rectangle2_3_${id}.end`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="1;13" values="1;13"
fill="freeze" fill="freeze"
/> />
@@ -144,7 +154,7 @@ export default function SlidingBlocks3({
id={`rectangle3_4_${id}`} id={`rectangle3_4_${id}`}
begin={`rectangle2_4_${id}.end`} begin={`rectangle2_4_${id}.end`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="1;13" values="1;13"
fill="freeze" fill="freeze"
/> />

View File

@@ -1,22 +1,32 @@
import type { LoadingBlocksProps } from "$/types/Loading"; import type { LoadingBlocksProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function WaveBlocks({ export default function WaveBlocks({
size,
width, width,
height, height,
className, className,
animationDuration = 0.6, animationDuration = 600,
color,
stroke, stroke,
fill fill,
}: LoadingBlocksProps){ ariaLabel = "Loading"
}: Readonly<LoadingBlocksProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/blocks-wave.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/blocks-wave.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -26,32 +36,32 @@ export default function WaveBlocks({
width="7.33" width="7.33"
height="7.33" height="7.33"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle1_1_${id}`} id={`rectangle1_1_${id}`}
begin={`0;rectangle9_1_${id}.end+${animationDuration / 3}s`} begin={`0;rectangle9_1_${id}.end+${dur / 3}s`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="1;4;1" values="1;4;1"
/> />
<animate <animate
begin={`0;rectangle9_1_${id}.end+${animationDuration / 3}s`} begin={`0;rectangle9_1_${id}.end+${dur / 3}s`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="1;4;1" values="1;4;1"
/> />
<animate <animate
begin={`0;rectangle9_1_${id}.end+${animationDuration / 3}s`} begin={`0;rectangle9_1_${id}.end+${dur / 3}s`}
attributeName="width" attributeName="width"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
<animate <animate
begin={`0;rectangle9_1_${id}.end+${animationDuration / 3}s`} begin={`0;rectangle9_1_${id}.end+${dur / 3}s`}
attributeName="height" attributeName="height"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
</rect> </rect>
@@ -61,31 +71,31 @@ export default function WaveBlocks({
width="7.33" width="7.33"
height="7.33" height="7.33"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 6}s`} begin={`rectangle1_1_${id}.begin+${dur / 6}s`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="8.33;11.33;8.33" values="8.33;11.33;8.33"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 6}s`} begin={`rectangle1_1_${id}.begin+${dur / 6}s`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="1;4;1" values="1;4;1"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 6}s`} begin={`rectangle1_1_${id}.begin+${dur / 6}s`}
attributeName="width" attributeName="width"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 6}s`} begin={`rectangle1_1_${id}.begin+${dur / 6}s`}
attributeName="height" attributeName="height"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
</rect> </rect>
@@ -95,31 +105,31 @@ export default function WaveBlocks({
width="7.33" width="7.33"
height="7.33" height="7.33"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 6}s`} begin={`rectangle1_1_${id}.begin+${dur / 6}s`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="1;4;1" values="1;4;1"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 6}s`} begin={`rectangle1_1_${id}.begin+${dur / 6}s`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="8.33;11.33;8.33" values="8.33;11.33;8.33"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 6}s`} begin={`rectangle1_1_${id}.begin+${dur / 6}s`}
attributeName="width" attributeName="width"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 6}s`} begin={`rectangle1_1_${id}.begin+${dur / 6}s`}
attributeName="height" attributeName="height"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
</rect> </rect>
@@ -129,31 +139,31 @@ export default function WaveBlocks({
width="7.33" width="7.33"
height="7.33" height="7.33"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 3}s`} begin={`rectangle1_1_${id}.begin+${dur / 3}s`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="15.66;18.66;15.66" values="15.66;18.66;15.66"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 3}s`} begin={`rectangle1_1_${id}.begin+${dur / 3}s`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="1;4;1" values="1;4;1"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 3}s`} begin={`rectangle1_1_${id}.begin+${dur / 3}s`}
attributeName="width" attributeName="width"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 3}s`} begin={`rectangle1_1_${id}.begin+${dur / 3}s`}
attributeName="height" attributeName="height"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
</rect> </rect>
@@ -163,31 +173,31 @@ export default function WaveBlocks({
width="7.33" width="7.33"
height="7.33" height="7.33"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 3}s`} begin={`rectangle1_1_${id}.begin+${dur / 3}s`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="8.33;11.33;8.33" values="8.33;11.33;8.33"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 3}s`} begin={`rectangle1_1_${id}.begin+${dur / 3}s`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="8.33;11.33;8.33" values="8.33;11.33;8.33"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 3}s`} begin={`rectangle1_1_${id}.begin+${dur / 3}s`}
attributeName="width" attributeName="width"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 3}s`} begin={`rectangle1_1_${id}.begin+${dur / 3}s`}
attributeName="height" attributeName="height"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
</rect> </rect>
@@ -197,31 +207,31 @@ export default function WaveBlocks({
width="7.33" width="7.33"
height="7.33" height="7.33"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 3}s`} begin={`rectangle1_1_${id}.begin+${dur / 3}s`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="1;4;1" values="1;4;1"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 3}s`} begin={`rectangle1_1_${id}.begin+${dur / 3}s`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="15.66;18.66;15.66" values="15.66;18.66;15.66"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 3}s`} begin={`rectangle1_1_${id}.begin+${dur / 3}s`}
attributeName="width" attributeName="width"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 3}s`} begin={`rectangle1_1_${id}.begin+${dur / 3}s`}
attributeName="height" attributeName="height"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
</rect> </rect>
@@ -231,31 +241,31 @@ export default function WaveBlocks({
width="7.33" width="7.33"
height="7.33" height="7.33"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 2}s`} begin={`rectangle1_1_${id}.begin+${dur / 2}s`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="15.66;18.66;15.66" values="15.66;18.66;15.66"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 2}s`} begin={`rectangle1_1_${id}.begin+${dur / 2}s`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="8.33;11.33;8.33" values="8.33;11.33;8.33"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 2}s`} begin={`rectangle1_1_${id}.begin+${dur / 2}s`}
attributeName="width" attributeName="width"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 2}s`} begin={`rectangle1_1_${id}.begin+${dur / 2}s`}
attributeName="height" attributeName="height"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
</rect> </rect>
@@ -265,31 +275,31 @@ export default function WaveBlocks({
width="7.33" width="7.33"
height="7.33" height="7.33"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 2}s`} begin={`rectangle1_1_${id}.begin+${dur / 2}s`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="8.33;11.33;8.33" values="8.33;11.33;8.33"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 2}s`} begin={`rectangle1_1_${id}.begin+${dur / 2}s`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="15.66;18.66;15.66" values="15.66;18.66;15.66"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 2}s`} begin={`rectangle1_1_${id}.begin+${dur / 2}s`}
attributeName="width" attributeName="width"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration / 2}s`} begin={`rectangle1_1_${id}.begin+${dur / 2}s`}
attributeName="height" attributeName="height"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
</rect> </rect>
@@ -299,32 +309,32 @@ export default function WaveBlocks({
width="7.33" width="7.33"
height="7.33" height="7.33"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`rectangle9_1_${id}`} id={`rectangle9_1_${id}`}
begin={`rectangle1_1_${id}.begin+${animationDuration * 2 / 3}s`} begin={`rectangle1_1_${id}.begin+${dur * 2 / 3}s`}
attributeName="x" attributeName="x"
dur={animationDuration} dur={dur}
values="15.66;18.66;15.66" values="15.66;18.66;15.66"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration * 2 / 3}s`} begin={`rectangle1_1_${id}.begin+${dur * 2 / 3}s`}
attributeName="y" attributeName="y"
dur={animationDuration} dur={dur}
values="15.66;18.66;15.66" values="15.66;18.66;15.66"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration * 2 / 3}s`} begin={`rectangle1_1_${id}.begin+${dur * 2 / 3}s`}
attributeName="width" attributeName="width"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
<animate <animate
begin={`rectangle1_1_${id}.begin+${animationDuration * 2 / 3}s`} begin={`rectangle1_1_${id}.begin+${dur * 2 / 3}s`}
attributeName="height" attributeName="height"
dur={animationDuration} dur={dur}
values="7.33;1.33;7.33" values="7.33;1.33;7.33"
/> />
</rect> </rect>

View File

@@ -1,22 +1,32 @@
import type { LoadingDotsProps } from "$/types/Loading"; import type { LoadingDotsProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function BouncingDots({ export default function BouncingDots({
size,
width, width,
height, height,
className, className,
animationDuration = 0.6, animationDuration = 600,
color,
stroke, stroke,
fill fill,
}: LoadingDotsProps){ ariaLabel = "Loading"
}: Readonly<LoadingDotsProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/3-dots-bounce.svg?short_path=50864c0 //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/3-dots-bounce.svg?short_path=50864c0
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -25,14 +35,15 @@ export default function BouncingDots({
cy="12" cy="12"
r="3" r="3"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={color ?? fill}
> >
<animate <animate
id={`firstBouncingDots_${id}`} id={`firstBouncingDots_${id}`}
begin={`0;lastBouncingDots_${id}.end+${animationDuration / 2}s`} begin={`0;lastBouncingDots_${id}.end+${dur / 2}s`}
attributeName="cy" attributeName="cy"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="12;6;12" values="12;6;12"
keySplines=".33,.66,.66,1;.33,0,.66,.33" keySplines=".33,.66,.66,1;.33,0,.66,.33"
/> />
@@ -42,14 +53,15 @@ export default function BouncingDots({
cy="12" cy="12"
r="3" r="3"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={color ?? fill}
> >
<animate <animate
id={`secondBouncingDots_${id}`} id={`secondBouncingDots_${id}`}
begin={`firstBouncingDots_${id}.begin+${animationDuration / 5}s`} begin={`firstBouncingDots_${id}.begin+${dur / 5}s`}
attributeName="cy" attributeName="cy"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="12;6;12" values="12;6;12"
keySplines=".33,.66,.66,1;.33,0,.66,.33" keySplines=".33,.66,.66,1;.33,0,.66,.33"
/> />
@@ -59,14 +71,15 @@ export default function BouncingDots({
cy="12" cy="12"
r="3" r="3"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={color ?? fill}
> >
<animate <animate
id={`lastBouncingDots_${id}`} id={`lastBouncingDots_${id}`}
begin={`secondBouncingDots_${id}.begin+${animationDuration / 5}s`} begin={`secondBouncingDots_${id}.begin+${dur / 5}s`}
attributeName="cy" attributeName="cy"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="12;6;12" values="12;6;12"
keySplines=".33,.66,.66,1;.33,0,.66,.33" keySplines=".33,.66,.66,1;.33,0,.66,.33"
/> />

View File

@@ -1,22 +1,32 @@
import type { LoadingDotsProps } from "$/types/Loading"; import type { LoadingDotsProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function CircleCenterDots({ export default function CircleCenterDots({
size,
width, width,
height, height,
className, className,
animationDuration = 0.6, animationDuration = 600,
color,
stroke, stroke,
fill fill,
}: LoadingDotsProps){ ariaLabel = "Loading"
}: Readonly<LoadingDotsProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/6-dots-scale-middle.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/6-dots-scale-middle.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -25,15 +35,15 @@ export default function CircleCenterDots({
cy="3" cy="3"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle1_${id}`} id={`circle1_${id}`}
begin={`0;circle3_${id}.end-${animationDuration * 5 / 6}s`} begin={`0;circle3_${id}.end-${dur * 5 / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -43,15 +53,15 @@ export default function CircleCenterDots({
cy="4.21" cy="4.21"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle2_${id}`} id={`circle2_${id}`}
begin={`circle1_${id}.begin+${animationDuration / 6}s`} begin={`circle1_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -61,15 +71,15 @@ export default function CircleCenterDots({
cy="4.21" cy="4.21"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle3_${id}`} id={`circle3_${id}`}
begin={`circle5_${id}.begin+${animationDuration / 6}s`} begin={`circle5_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -79,15 +89,15 @@ export default function CircleCenterDots({
cy="7.50" cy="7.50"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle4_${id}`} id={`circle4_${id}`}
begin={`circle2_${id}.begin+${animationDuration / 6}s`} begin={`circle2_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -97,15 +107,15 @@ export default function CircleCenterDots({
cy="7.50" cy="7.50"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle5_${id}`} id={`circle5_${id}`}
begin={`circle7_${id}.begin+${animationDuration / 6}s`} begin={`circle7_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -115,14 +125,14 @@ export default function CircleCenterDots({
cy="12.00" cy="12.00"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
> >
<animate <animate
id={`circle6_${id}`} id={`circle6_${id}`}
begin={`circle4_${id}.begin+${animationDuration / 6}s`} begin={`circle4_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -132,15 +142,15 @@ export default function CircleCenterDots({
cy="12.00" cy="12.00"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle7_${id}`} id={`circle7_${id}`}
begin={`circle9_${id}.begin+${animationDuration / 6}s`} begin={`circle9_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -150,15 +160,15 @@ export default function CircleCenterDots({
cy="16.50" cy="16.50"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle8_${id}`} id={`circle8_${id}`}
begin={`circle6_${id}.begin+${animationDuration / 6}s`} begin={`circle6_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -168,15 +178,15 @@ export default function CircleCenterDots({
cy="16.50" cy="16.50"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle9_${id}`} id={`circle9_${id}`}
begin={`circle11_${id}.begin+${animationDuration / 6}s`} begin={`circle11_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -186,15 +196,15 @@ export default function CircleCenterDots({
cy="19.79" cy="19.79"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle10_${id}`} id={`circle10_${id}`}
begin={`circle8_${id}.begin+${animationDuration / 6}s`} begin={`circle8_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -204,15 +214,15 @@ export default function CircleCenterDots({
cy="19.79" cy="19.79"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle11_${id}`} id={`circle11_${id}`}
begin={`circle12_${id}.begin+${animationDuration / 6}s`} begin={`circle12_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -221,16 +231,16 @@ export default function CircleCenterDots({
cx="12" cx="12"
cy="21" cy="21"
r="0" r="0"
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
className={className} className={className}
> >
<animate <animate
id={`circle12_${id}`} id={`circle12_${id}`}
begin={`circle10_${id}.begin+${animationDuration / 6}s`} begin={`circle10_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />

View File

@@ -1,19 +1,30 @@
import type { LoadingDotsProps } from "$/types/Loading"; import type { LoadingDotsProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
export default function CircleFadingDots({ export default function CircleFadingDots({
size,
width, width,
height, height,
className, className,
animationDuration = 0.75, animationDuration = 750,
color,
stroke, stroke,
fill fill,
}: LoadingDotsProps){ ariaLabel = "Loading"
}: Readonly<LoadingDotsProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/6-dots-rotate.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/6-dots-rotate.svg
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -24,8 +35,8 @@ export default function CircleFadingDots({
r="1.5" r="1.5"
opacity=".14" opacity=".14"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<circle <circle
cx="16.75" cx="16.75"
@@ -33,8 +44,8 @@ export default function CircleFadingDots({
r="1.5" r="1.5"
opacity=".29" opacity=".29"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<circle <circle
cx="20.23" cx="20.23"
@@ -42,8 +53,8 @@ export default function CircleFadingDots({
r="1.5" r="1.5"
opacity=".43" opacity=".43"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<circle <circle
cx="21.50" cx="21.50"
@@ -51,8 +62,8 @@ export default function CircleFadingDots({
r="1.5" r="1.5"
opacity=".57" opacity=".57"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<circle <circle
cx="20.23" cx="20.23"
@@ -60,8 +71,8 @@ export default function CircleFadingDots({
r="1.5" r="1.5"
opacity=".71" opacity=".71"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<circle <circle
cx="16.75" cx="16.75"
@@ -69,22 +80,22 @@ export default function CircleFadingDots({
r="1.5" r="1.5"
opacity=".86" opacity=".86"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<circle <circle
cx="12" cx="12"
cy="21.5" cy="21.5"
r="1.5" r="1.5"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<animateTransform <animateTransform
attributeName="transform" attributeName="transform"
type="rotate" type="rotate"
calcMode="discrete" calcMode="discrete"
dur={animationDuration} dur={dur}
values="0 12 12;30 12 12;60 12 12;90 12 12;120 12 12;150 12 12;180 12 12;210 12 12;240 12 12;270 12 12;300 12 12;330 12 12;360 12 12" values="0 12 12;30 12 12;60 12 12;90 12 12;120 12 12;150 12 12;180 12 12;210 12 12;240 12 12;270 12 12;300 12 12;330 12 12;360 12 12"
repeatCount="indefinite" repeatCount="indefinite"
/> />

View File

@@ -1,23 +1,34 @@
import type { CirclePulsingDotsProps } from "$/types/Loading"; import type { CirclePulsingDotsProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function CirclePulsingDots({ export default function CirclePulsingDots({
size,
width, width,
height, height,
className, className,
rotationAnimationDuration = 6, rotationAnimationDuration = 6000,
growingAnimationDuration = 0.6, growingAnimationDuration = 600,
color,
stroke, stroke,
fill fill,
}: CirclePulsingDotsProps){ ariaLabel = "Loading"
}: Readonly<CirclePulsingDotsProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/12-dots-scale-rotate.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/12-dots-scale-rotate.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const rotationAnimationDur = reducedMotion ? rotationAnimationDuration / 100 : rotationAnimationDuration / 1000;
const growingAnimationDur = reducedMotion ? growingAnimationDuration / 100 : growingAnimationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -27,15 +38,15 @@ export default function CirclePulsingDots({
cy="3" cy="3"
r="1" r="1"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle1_${id}`} id={`circle1_${id}`}
begin={`0;circle3_${id}.end-${growingAnimationDuration * 5 / 6}s`} begin={`0;circle3_${id}.end-${growingAnimationDur * 5 / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={growingAnimationDuration} dur={growingAnimationDur}
values="1;2;1" values="1;2;1"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -45,15 +56,15 @@ export default function CirclePulsingDots({
cy="4.21" cy="4.21"
r="1" r="1"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle2_${id}`} id={`circle2_${id}`}
begin={`circle1_${id}.begin+${growingAnimationDuration / 6}s`} begin={`circle1_${id}.begin+${growingAnimationDur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={growingAnimationDuration} dur={growingAnimationDur}
values="1;2;1" values="1;2;1"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -63,15 +74,15 @@ export default function CirclePulsingDots({
cy="4.21" cy="4.21"
r="1" r="1"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle3_${id}`} id={`circle3_${id}`}
begin={`circle5_${id}.begin+${growingAnimationDuration / 6}s`} begin={`circle5_${id}.begin+${growingAnimationDur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={growingAnimationDuration} dur={growingAnimationDur}
values="1;2;1" values="1;2;1"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -81,15 +92,15 @@ export default function CirclePulsingDots({
cy="7.50" cy="7.50"
r="1" r="1"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle4_${id}`} id={`circle4_${id}`}
begin={`circle2_${id}.begin+${growingAnimationDuration / 6}s`} begin={`circle2_${id}.begin+${growingAnimationDur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={growingAnimationDuration} dur={growingAnimationDur}
values="1;2;1" values="1;2;1"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -99,15 +110,15 @@ export default function CirclePulsingDots({
cy="7.50" cy="7.50"
r="1" r="1"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle5_${id}`} id={`circle5_${id}`}
begin={`circle7_${id}.begin+${growingAnimationDuration / 6}s`} begin={`circle7_${id}.begin+${growingAnimationDur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={growingAnimationDuration} dur={growingAnimationDur}
values="1;2;1" values="1;2;1"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -117,15 +128,15 @@ export default function CirclePulsingDots({
cy="12.00" cy="12.00"
r="1" r="1"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle6_${id}`} id={`circle6_${id}`}
begin={`circle4_${id}.begin+${growingAnimationDuration / 6}s`} begin={`circle4_${id}.begin+${growingAnimationDur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={growingAnimationDuration} dur={growingAnimationDur}
values="1;2;1" values="1;2;1"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -135,15 +146,15 @@ export default function CirclePulsingDots({
cy="12.00" cy="12.00"
r="1" r="1"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle7_${id}`} id={`circle7_${id}`}
begin={`circle9_${id}.begin+${growingAnimationDuration / 6}s`} begin={`circle9_${id}.begin+${growingAnimationDur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={growingAnimationDuration} dur={growingAnimationDur}
values="1;2;1" values="1;2;1"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -153,15 +164,15 @@ export default function CirclePulsingDots({
cy="16.50" cy="16.50"
r="1" r="1"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle8_${id}`} id={`circle8_${id}`}
begin={`circle6_${id}.begin+${growingAnimationDuration / 6}s`} begin={`circle6_${id}.begin+${growingAnimationDur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={growingAnimationDuration} dur={growingAnimationDur}
values="1;2;1" values="1;2;1"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -171,15 +182,15 @@ export default function CirclePulsingDots({
cy="16.50" cy="16.50"
r="1" r="1"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle9_${id}`} id={`circle9_${id}`}
begin={`circle11_${id}.begin+${growingAnimationDuration / 6}s`} begin={`circle11_${id}.begin+${growingAnimationDur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={growingAnimationDuration} dur={growingAnimationDur}
values="1;2;1" values="1;2;1"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -189,15 +200,15 @@ export default function CirclePulsingDots({
cy="19.79" cy="19.79"
r="1" r="1"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle10_${id}`} id={`circle10_${id}`}
begin={`circle8_${id}.begin+${growingAnimationDuration / 6}s`} begin={`circle8_${id}.begin+${growingAnimationDur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={growingAnimationDuration} dur={growingAnimationDur}
values="1;2;1" values="1;2;1"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -207,15 +218,15 @@ export default function CirclePulsingDots({
cy="19.79" cy="19.79"
r="1" r="1"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle11_${id}`} id={`circle11_${id}`}
begin={`circle12_${id}.begin+${growingAnimationDuration / 6}s`} begin={`circle12_${id}.begin+${growingAnimationDur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={growingAnimationDuration} dur={growingAnimationDur}
values="1;2;1" values="1;2;1"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -225,15 +236,15 @@ export default function CirclePulsingDots({
cy="21" cy="21"
r="1" r="1"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`circle12_${id}`} id={`circle12_${id}`}
begin={`circle10_${id}.begin+${growingAnimationDuration / 6}s`} begin={`circle10_${id}.begin+${growingAnimationDur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={growingAnimationDuration} dur={growingAnimationDur}
values="1;2;1" values="1;2;1"
keySplines=".27,.42,.37,.99;.53,0,.61,.73" keySplines=".27,.42,.37,.99;.53,0,.61,.73"
/> />
@@ -241,7 +252,7 @@ export default function CirclePulsingDots({
<animateTransform <animateTransform
attributeName="transform" attributeName="transform"
type="rotate" type="rotate"
dur={rotationAnimationDuration} dur={rotationAnimationDur}
values="360 12 12;0 12 12" values="360 12 12;0 12 12"
repeatCount="indefinite" repeatCount="indefinite"
/> />

View File

@@ -1,21 +1,30 @@
import type { LoadingDotsProps } from "$/types/Loading"; import type { LoadingDotsProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
export default function CircleRotatingDots({ export default function CircleRotatingDots({
size,
width, width,
height, height,
className, className,
animationDuration = 1.5, animationDuration = 1500,
color,
stroke, stroke,
fill fill,
}: LoadingDotsProps){ ariaLabel = "Loading"
}: Readonly<LoadingDotsProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/8-dots-rotate.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/8-dots-rotate.svg
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -25,69 +34,69 @@ export default function CircleRotatingDots({
cy="12" cy="12"
r="2" r="2"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<circle <circle
cx="21" cx="21"
cy="12" cy="12"
r="2" r="2"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<circle <circle
cx="12" cx="12"
cy="21" cy="21"
r="2" r="2"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<circle <circle
cx="12" cx="12"
cy="3" cy="3"
r="2" r="2"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<circle <circle
cx="5.64" cx="5.64"
cy="5.64" cy="5.64"
r="2" r="2"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<circle <circle
cx="18.36" cx="18.36"
cy="18.36" cy="18.36"
r="2" r="2"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<circle <circle
cx="5.64" cx="5.64"
cy="18.36" cy="18.36"
r="2" r="2"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<circle <circle
cx="18.36" cx="18.36"
cy="5.64" cy="5.64"
r="2" r="2"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<animateTransform <animateTransform
attributeName="transform" attributeName="transform"
type="rotate" type="rotate"
dur={animationDuration} dur={dur}
values="0 12 12;360 12 12" values="0 12 12;360 12 12"
repeatCount="indefinite" repeatCount="indefinite"
/> />

View File

@@ -1,22 +1,32 @@
import type { LoadingDotsProps } from "$/types/Loading"; import type { LoadingDotsProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function CircleShrinkingDots({ export default function CircleShrinkingDots({
size,
width, width,
height, height,
className, className,
animationDuration = 0.6, animationDuration = 600,
color,
stroke, stroke,
fill fill,
}: LoadingDotsProps){ ariaLabel = "Loading"
}: Readonly<LoadingDotsProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/6-dots-scale.svg?short_path=17d1946 //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/6-dots-scale.svg?short_path=17d1946
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -25,15 +35,15 @@ export default function CircleShrinkingDots({
cy="3" cy="3"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`firstShrinkingDot_${id}`} id={`firstShrinkingDot_${id}`}
begin={`0;thirdShrinkingDot_${id}.end-${animationDuration * 4 / 5}s`} begin={`0;thirdShrinkingDot_${id}.end-${dur * 4 / 5}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keyTimes="0;.2;1" keyTimes="0;.2;1"
keySplines="0,1,0,1;.53,0,.61,.73" keySplines="0,1,0,1;.53,0,.61,.73"
@@ -45,15 +55,15 @@ export default function CircleShrinkingDots({
cy="4.21" cy="4.21"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`secondShrinkingDot_${id}`} id={`secondShrinkingDot_${id}`}
begin={`firstShrinkingDot_${id}.begin+${animationDuration / 6}s`} begin={`firstShrinkingDot_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keyTimes="0;.2;1" keyTimes="0;.2;1"
keySplines="0,1,0,1;.53,0,.61,.73" keySplines="0,1,0,1;.53,0,.61,.73"
@@ -65,15 +75,15 @@ export default function CircleShrinkingDots({
cy="4.21" cy="4.21"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`thirdShrinkingDot_${id}`} id={`thirdShrinkingDot_${id}`}
begin={`fifthShrinkingDot_${id}.begin+${animationDuration / 6}s`} begin={`fifthShrinkingDot_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keyTimes="0;.2;1" keyTimes="0;.2;1"
keySplines="0,1,0,1;.53,0,.61,.73" keySplines="0,1,0,1;.53,0,.61,.73"
@@ -85,15 +95,15 @@ export default function CircleShrinkingDots({
cy="7.50" cy="7.50"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`fourthShrinkingDot_${id}`} id={`fourthShrinkingDot_${id}`}
begin={`secondShrinkingDot_${id}.begin+${animationDuration / 6}s`} begin={`secondShrinkingDot_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keyTimes="0;.2;1" keyTimes="0;.2;1"
keySplines="0,1,0,1;.53,0,.61,.73" fill="freeze" keySplines="0,1,0,1;.53,0,.61,.73" fill="freeze"
@@ -104,15 +114,15 @@ export default function CircleShrinkingDots({
cy="7.50" cy="7.50"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`fifthShrinkingDot_${id}`} id={`fifthShrinkingDot_${id}`}
begin={`seventhShrinkingDot_${id}.begin+${animationDuration / 6}s`} begin={`seventhShrinkingDot_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keyTimes="0;.2;1" keyTimes="0;.2;1"
keySplines="0,1,0,1;.53,0,.61,.73" keySplines="0,1,0,1;.53,0,.61,.73"
@@ -124,15 +134,15 @@ export default function CircleShrinkingDots({
cy="12.00" cy="12.00"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`sixthShrinkingDot_${id}`} id={`sixthShrinkingDot_${id}`}
begin={`fourthShrinkingDot_${id}.begin+${animationDuration / 6}s`} begin={`fourthShrinkingDot_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keyTimes="0;.2;1" keyTimes="0;.2;1"
keySplines="0,1,0,1;.53,0,.61,.73" keySplines="0,1,0,1;.53,0,.61,.73"
@@ -144,15 +154,15 @@ export default function CircleShrinkingDots({
cy="12.00" cy="12.00"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`seventhShrinkingDot_${id}`} id={`seventhShrinkingDot_${id}`}
begin={`ninthShrinkingDot_${id}.begin+${animationDuration / 6}s`} begin={`ninthShrinkingDot_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keyTimes="0;.2;1" keyTimes="0;.2;1"
keySplines="0,1,0,1;.53,0,.61,.73" keySplines="0,1,0,1;.53,0,.61,.73"
@@ -164,15 +174,15 @@ export default function CircleShrinkingDots({
cy="16.50" cy="16.50"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`eighthShrinkingDot_${id}`} id={`eighthShrinkingDot_${id}`}
begin={`sixthShrinkingDot_${id}.begin+${animationDuration / 6}s`} begin={`sixthShrinkingDot_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keyTimes="0;.2;1" keyTimes="0;.2;1"
keySplines="0,1,0,1;.53,0,.61,.73" keySplines="0,1,0,1;.53,0,.61,.73"
@@ -184,15 +194,15 @@ export default function CircleShrinkingDots({
cy="16.50" cy="16.50"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`ninthShrinkingDot_${id}`} id={`ninthShrinkingDot_${id}`}
begin={`eleventhShrinkingDot_${id}.begin+${animationDuration / 6}s`} begin={`eleventhShrinkingDot_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keyTimes="0;.2;1" keyTimes="0;.2;1"
keySplines="0,1,0,1;.53,0,.61,.73" keySplines="0,1,0,1;.53,0,.61,.73"
@@ -204,15 +214,15 @@ export default function CircleShrinkingDots({
cy="19.79" cy="19.79"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`tenthShrinkingDot_${id}`} id={`tenthShrinkingDot_${id}`}
begin={`eighthShrinkingDot_${id}.begin+${animationDuration / 6}s`} begin={`eighthShrinkingDot_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keyTimes="0;.2;1" keyTimes="0;.2;1"
keySplines="0,1,0,1;.53,0,.61,.73" keySplines="0,1,0,1;.53,0,.61,.73"
@@ -224,15 +234,15 @@ export default function CircleShrinkingDots({
cy="19.79" cy="19.79"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`eleventhShrinkingDot_${id}`} id={`eleventhShrinkingDot_${id}`}
begin={`twelfthShrinkingDot_${id}.begin+${animationDuration / 6}s`} begin={`twelfthShrinkingDot_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keyTimes="0;.2;1" keyTimes="0;.2;1"
keySplines="0,1,0,1;.53,0,.61,.73" keySplines="0,1,0,1;.53,0,.61,.73"
@@ -244,15 +254,15 @@ export default function CircleShrinkingDots({
cy="21" cy="21"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`twelfthShrinkingDot_${id}`} id={`twelfthShrinkingDot_${id}`}
begin={`tenthShrinkingDot_${id}.begin+${animationDuration / 6}s`} begin={`tenthShrinkingDot_${id}.begin+${dur / 6}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;2;0" values="0;2;0"
keyTimes="0;.2;1" keyTimes="0;.2;1"
keySplines="0,1,0,1;.53,0,.61,.73" keySplines="0,1,0,1;.53,0,.61,.73"

View File

@@ -1,21 +1,33 @@
import type { CircleSpinningDotProps } from "$/types/Loading"; import type { CircleSpinningDotProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
export default function CircleSpinningDot({ export default function CircleSpinningDot({
size,
width, width,
height, height,
className, className,
animationDuration = 0.75, animationDuration = 750,
color,
stroke, stroke,
fill, fill,
trackClassName = "fill-transparent", trackClassName = "fill-transparent",
trackStroke, trackStroke,
trackFill trackFill,
}: CircleSpinningDotProps){ ariaLabel = "Loading"
}: Readonly<CircleSpinningDotProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/dot-revolve.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/dot-revolve.svg
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -30,13 +42,13 @@ export default function CircleSpinningDot({
cy="2.5" cy="2.5"
r="1.5" r="1.5"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animateTransform <animateTransform
attributeName="transform" attributeName="transform"
type="rotate" type="rotate"
dur={animationDuration} dur={dur}
values="0 12 12;360 12 12" values="0 12 12;360 12 12"
repeatCount="indefinite" repeatCount="indefinite"
/> />

View File

@@ -1,21 +1,32 @@
import type { LoadingDotsProps } from "$/types/Loading"; import type { LoadingDotsProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function CyclingDots({ export default function CyclingDots({
size,
width, width,
height, height,
className, className,
animationDuration = 0.5, animationDuration = 500,
color,
stroke, stroke,
fill fill,
}: LoadingDotsProps){ ariaLabel = "Loading"
}: Readonly<LoadingDotsProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/3-dots-move.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/3-dots-move.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -24,14 +35,14 @@ export default function CyclingDots({
cy="12" cy="12"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin="0;spinner_z0Or.end" begin="0;spinner_z0Or.end"
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1" keySplines=".36,.6,.31,1"
values="0;3" values="0;3"
fill="freeze" fill="freeze"
@@ -40,7 +51,7 @@ export default function CyclingDots({
begin="spinner_OLMs.end" begin="spinner_OLMs.end"
attributeName="cx" attributeName="cx"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1" keySplines=".36,.6,.31,1"
values="4;12" values="4;12"
fill="freeze" fill="freeze"
@@ -49,7 +60,7 @@ export default function CyclingDots({
begin="spinner_UHR2.end" begin="spinner_UHR2.end"
attributeName="cx" attributeName="cx"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1" keySplines=".36,.6,.31,1"
values="12;20" values="12;20"
fill="freeze" fill="freeze"
@@ -59,7 +70,7 @@ export default function CyclingDots({
begin="spinner_Aguh.end" begin="spinner_Aguh.end"
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1" keySplines=".36,.6,.31,1"
values="3;0" values="3;0"
fill="freeze" fill="freeze"
@@ -78,14 +89,14 @@ export default function CyclingDots({
cy="12" cy="12"
r="3" r="3"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin="0;spinner_z0Or.end" begin="0;spinner_z0Or.end"
attributeName="cx" attributeName="cx"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1" keySplines=".36,.6,.31,1"
values="4;12" values="4;12"
fill="freeze" fill="freeze"
@@ -94,7 +105,7 @@ export default function CyclingDots({
begin="spinner_OLMs.end" begin="spinner_OLMs.end"
attributeName="cx" attributeName="cx"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1" keySplines=".36,.6,.31,1"
values="12;20" values="12;20"
fill="freeze" fill="freeze"
@@ -104,7 +115,7 @@ export default function CyclingDots({
begin="spinner_UHR2.end" begin="spinner_UHR2.end"
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1" keySplines=".36,.6,.31,1"
values="3;0" values="3;0"
fill="freeze" fill="freeze"
@@ -121,7 +132,7 @@ export default function CyclingDots({
begin="spinner_Aguh.end" begin="spinner_Aguh.end"
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1" keySplines=".36,.6,.31,1"
values="0;3" values="0;3"
fill="freeze" fill="freeze"
@@ -132,14 +143,14 @@ export default function CyclingDots({
cy="12" cy="12"
r="3" r="3"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin="0;spinner_z0Or.end" begin="0;spinner_z0Or.end"
attributeName="cx" attributeName="cx"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1" keySplines=".36,.6,.31,1"
values="12;20" values="12;20"
fill="freeze" fill="freeze"
@@ -149,7 +160,7 @@ export default function CyclingDots({
begin="spinner_OLMs.end" begin="spinner_OLMs.end"
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1" keySplines=".36,.6,.31,1"
values="3;0" values="3;0"
fill="freeze" fill="freeze"
@@ -166,7 +177,7 @@ export default function CyclingDots({
begin="spinner_UHR2.end" begin="spinner_UHR2.end"
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1" keySplines=".36,.6,.31,1"
values="0;3" values="0;3"
fill="freeze" fill="freeze"
@@ -175,7 +186,7 @@ export default function CyclingDots({
begin="spinner_Aguh.end" begin="spinner_Aguh.end"
attributeName="cx" attributeName="cx"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1" keySplines=".36,.6,.31,1"
values="4;12" values="4;12"
fill="freeze" fill="freeze"
@@ -186,15 +197,15 @@ export default function CyclingDots({
cy="12" cy="12"
r="3" r="3"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`fourthDotFirstAnimate_${id}`} id={`fourthDotFirstAnimate_${id}`}
begin="0;spinner_z0Or.end" begin="0;spinner_z0Or.end"
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1" keySplines=".36,.6,.31,1"
values="3;0" values="3;0"
fill="freeze" fill="freeze"
@@ -211,7 +222,7 @@ export default function CyclingDots({
begin="spinner_OLMs.end" begin="spinner_OLMs.end"
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1" keySplines=".36,.6,.31,1"
values="0;3" values="0;3"
fill="freeze" fill="freeze"
@@ -220,7 +231,7 @@ export default function CyclingDots({
begin="spinner_UHR2.end" begin="spinner_UHR2.end"
attributeName="cx" attributeName="cx"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1" keySplines=".36,.6,.31,1"
values="4;12" values="4;12"
fill="freeze" fill="freeze"
@@ -229,7 +240,7 @@ export default function CyclingDots({
begin="spinner_Aguh.end" begin="spinner_Aguh.end"
attributeName="cx" attributeName="cx"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1" keySplines=".36,.6,.31,1"
values="12;20" values="12;20"
fill="freeze" fill="freeze"

View File

@@ -1,22 +1,32 @@
import type { LoadingDotsProps } from "$/types/Loading"; import type { LoadingDotsProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function FadingDots({ export default function FadingDots({
size,
width, width,
height, height,
className, className,
animationDuration = 0.75, animationDuration = 750,
color,
stroke, stroke,
fill fill,
}: LoadingDotsProps){ ariaLabel = "Loading"
}: Readonly<LoadingDotsProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/3-dots-fade.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/3-dots-fade.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -26,14 +36,14 @@ export default function FadingDots({
r="3" r="3"
opacity="1" opacity="1"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`firstFadingDots_${id}`} id={`firstFadingDots_${id}`}
begin={`0;lastFadingDots_${id}.end-${animationDuration / 3}s`} begin={`0;lastFadingDots_${id}.end-${dur / 3}s`}
attributeName="opacity" attributeName="opacity"
dur={animationDuration} dur={dur}
values="1;.2" values="1;.2"
fill="freeze" fill="freeze"
/> />
@@ -44,14 +54,14 @@ export default function FadingDots({
r="3" r="3"
opacity=".4" opacity=".4"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`secondFadingDots_${id}`} id={`secondFadingDots_${id}`}
begin={`firstFadingDots_${id}.begin+${animationDuration / 5}s`} begin={`firstFadingDots_${id}.begin+${dur / 5}s`}
attributeName="opacity" attributeName="opacity"
dur={animationDuration} dur={dur}
values="1;.2" values="1;.2"
fill="freeze" fill="freeze"
/> />
@@ -62,14 +72,14 @@ export default function FadingDots({
r="3" r="3"
opacity=".3" opacity=".3"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`lastFadingDots_${id}`} id={`lastFadingDots_${id}`}
begin={`secondFadingDots_${id}.begin+${animationDuration / 5}s`} begin={`secondFadingDots_${id}.begin+${dur / 5}s`}
attributeName="opacity" attributeName="opacity"
dur={animationDuration} dur={dur}
values="1;.2" values="1;.2"
fill="freeze" fill="freeze"
/> />

View File

@@ -1,19 +1,30 @@
import type { LoadingDotsProps } from "$/types/Loading"; import type { LoadingDotsProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
export default function PulsingDots({ export default function PulsingDots({
size,
width, width,
height, height,
className, className,
animationDuration = 0.75, animationDuration = 750,
color,
stroke, stroke,
fill fill,
}: LoadingDotsProps){ ariaLabel = "Loading"
}: Readonly<LoadingDotsProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/3-dots-scale-middle.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/3-dots-scale-middle.svg
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -22,12 +33,12 @@ export default function PulsingDots({
cy="12" cy="12"
r="1.5" r="1.5"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
attributeName="r" attributeName="r"
dur={animationDuration} dur={dur}
values="1.5;3;1.5" values="1.5;3;1.5"
repeatCount="indefinite" repeatCount="indefinite"
/> />
@@ -37,12 +48,12 @@ export default function PulsingDots({
cy="12" cy="12"
r="3" r="3"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
attributeName="r" attributeName="r"
dur={animationDuration} dur={dur}
values="3;1.5;3" values="3;1.5;3"
repeatCount="indefinite" repeatCount="indefinite"
/> />
@@ -52,12 +63,12 @@ export default function PulsingDots({
cy="12" cy="12"
r="1.5" r="1.5"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
attributeName="r" attributeName="r"
dur={animationDuration} dur={dur}
values="1.5;3;1.5" values="1.5;3;1.5"
repeatCount="indefinite" repeatCount="indefinite"
/> />

View File

@@ -1,18 +1,29 @@
import type { LoadingDotsProps } from "$/types/Loading"; import type { LoadingDotsProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
export default function RotatingDots({ export default function RotatingDots({
size,
width, width,
height, height,
className, className,
animationDuration = 1, animationDuration = 1000,
color,
stroke, stroke,
fill fill,
}: LoadingDotsProps){ ariaLabel = "Loading"
}: Readonly<LoadingDotsProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/3-dots-rotate.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/3-dots-rotate.svg
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -21,8 +32,8 @@ export default function RotatingDots({
cy="12" cy="12"
r="3" r="3"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<g> <g>
<circle <circle
@@ -30,22 +41,22 @@ export default function RotatingDots({
cy="12" cy="12"
r="3" r="3"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<circle <circle
cx="20" cx="20"
cy="12" cy="12"
r="3" r="3"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
/> />
<animateTransform <animateTransform
attributeName="transform" attributeName="transform"
type="rotate" type="rotate"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
keySplines=".36,.6,.31,1;.36,.6,.31,1" keySplines=".36,.6,.31,1;.36,.6,.31,1"
values="0 12 12;180 12 12;360 12 12" values="0 12 12;180 12 12;360 12 12"
repeatCount="indefinite" repeatCount="indefinite"

View File

@@ -1,22 +1,32 @@
import type { LoadingDotsProps } from "$/types/Loading"; import type { LoadingDotsProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function SwellingDots({ export default function SwellingDots({
size,
width, width,
height, height,
className, className,
animationDuration = 0.75, animationDuration = 750,
color,
stroke, stroke,
fill fill,
}: LoadingDotsProps){ ariaLabel = "Loading"
}: Readonly<LoadingDotsProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/3-dots-scale.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/3-dots-scale.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -25,14 +35,14 @@ export default function SwellingDots({
cy="12" cy="12"
r="3" r="3"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`firstDot_${id}`} id={`firstDot_${id}`}
begin={`0;lastDot_${id}.end-${animationDuration / 3}s`} begin={`0;lastDot_${id}.end-${dur / 3}s`}
attributeName="r" attributeName="r"
dur={animationDuration} dur={dur}
values="3;.2;3" values="3;.2;3"
/> />
</circle> </circle>
@@ -41,13 +51,13 @@ export default function SwellingDots({
cy="12" cy="12"
r="3" r="3"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
begin={`firstDot_${id}.end-${animationDuration * 4 / 5}s`} begin={`firstDot_${id}.end-${dur * 4 / 5}s`}
attributeName="r" attributeName="r"
dur={animationDuration} dur={dur}
values="3;.2;3" values="3;.2;3"
/> />
</circle> </circle>
@@ -56,14 +66,14 @@ export default function SwellingDots({
cy="12" cy="12"
r="3" r="3"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`lastDot_${id}`} id={`lastDot_${id}`}
begin={`firstDot_${id}.end-${animationDuration * 2 / 3}s`} begin={`firstDot_${id}.end-${dur * 2 / 3}s`}
attributeName="r" attributeName="r"
dur={animationDuration} dur={dur}
values="3;.2;3" values="3;.2;3"
/> />
</circle> </circle>

View File

@@ -1,22 +1,32 @@
import type { LoadingPulseProps } from "$/types/Loading"; import type { LoadingPulseProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function DoubleDrop({ export default function DoubleDrop({
size,
width, width,
height, height,
className, className,
animationDuration = 1.2, animationDuration = 1200,
color,
stroke, stroke,
fill fill,
}: LoadingPulseProps){ ariaLabel = "Loading"
}: Readonly<LoadingPulseProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/pulse-2.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/pulse-2.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -25,24 +35,24 @@ export default function DoubleDrop({
cy="12" cy="12"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`drop1_${id}`} id={`drop1_${id}`}
begin={`0;drop2_${id}.begin+${animationDuration / 2}s`} begin={`0;drop2_${id}.begin+${dur / 2}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;11" values="0;11"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
fill="freeze" fill="freeze"
/> />
<animate <animate
begin={`0;drop2_${id}.begin+${animationDuration / 2}s`} begin={`0;drop2_${id}.begin+${dur / 2}s`}
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
fill="freeze" fill="freeze"
@@ -53,24 +63,24 @@ export default function DoubleDrop({
cy="12" cy="12"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`drop2_${id}`} id={`drop2_${id}`}
begin={`drop1_${id}.begin+${animationDuration / 2}s`} begin={`drop1_${id}.begin+${dur / 2}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;11" values="0;11"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
fill="freeze" fill="freeze"
/> />
<animate <animate
begin={`drop1_${id}.begin+${animationDuration / 2}s`} begin={`drop1_${id}.begin+${dur / 2}s`}
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
fill="freeze" fill="freeze"

View File

@@ -1,22 +1,32 @@
import type { LoadingPulseProps } from "$/types/Loading"; import type { LoadingPulseProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function DoubleWaveDrop({ export default function DoubleWaveDrop({
size,
width, width,
height, height,
className, className,
animationDuration = 1.2, animationDuration = 1200,
color,
stroke, stroke,
fill fill,
}: LoadingPulseProps){ ariaLabel = "Loading"
}: Readonly<LoadingPulseProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/pulse-rings-2.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/pulse-rings-2.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -24,34 +34,34 @@ export default function DoubleWaveDrop({
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z"
transform="translate(12, 12) scale(0)" transform="translate(12, 12) scale(0)"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animateTransform <animateTransform
id={`drop1_${id}`} id={`drop1_${id}`}
begin={`0;drop2_${id}.begin+${animationDuration / 2}s`} begin={`0;drop2_${id}.begin+${dur / 2}s`}
attributeName="transform" attributeName="transform"
calcMode="spline" calcMode="spline"
type="translate" type="translate"
dur={animationDuration} dur={dur}
values="12 12;0 0" values="12 12;0 0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
<animateTransform <animateTransform
begin={`0;drop2_${id}.begin+${animationDuration / 2}s`} begin={`0;drop2_${id}.begin+${dur / 2}s`}
attributeName="transform" attributeName="transform"
calcMode="spline" calcMode="spline"
additive="sum" additive="sum"
type="scale" type="scale"
dur={animationDuration} dur={dur}
values="0;1" values="0;1"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
<animate <animate
begin={`0;drop2_${id}.begin+${animationDuration / 2}s`} begin={`0;drop2_${id}.begin+${dur / 2}s`}
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
@@ -60,34 +70,34 @@ export default function DoubleWaveDrop({
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z"
transform="translate(12, 12) scale(0)" transform="translate(12, 12) scale(0)"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animateTransform <animateTransform
id={`drop2_${id}`} id={`drop2_${id}`}
begin={`drop1_${id}.begin+${animationDuration / 2}s`} begin={`drop1_${id}.begin+${dur / 2}s`}
attributeName="transform" attributeName="transform"
calcMode="spline" calcMode="spline"
type="translate" type="translate"
dur={animationDuration} dur={dur}
values="12 12;0 0" values="12 12;0 0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
<animateTransform <animateTransform
begin={`drop1_${id}.begin+${animationDuration / 2}s`} begin={`drop1_${id}.begin+${dur / 2}s`}
attributeName="transform" attributeName="transform"
calcMode="spline" calcMode="spline"
additive="sum" additive="sum"
type="scale" type="scale"
dur={animationDuration} dur={dur}
values="0;1" values="0;1"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
<animate <animate
begin={`drop1_${id}.begin+${animationDuration / 2}s`} begin={`drop1_${id}.begin+${dur / 2}s`}
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />

View File

@@ -1,21 +1,30 @@
import type { LoadingPulseProps } from "$/types/Loading"; import type { LoadingPulseProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
export default function Drop({ export default function Drop({
size,
width, width,
height, height,
className, className,
animationDuration = 1.2, animationDuration = 1200,
color,
stroke, stroke,
fill fill,
}: LoadingPulseProps){ ariaLabel = "Loading"
}: Readonly<LoadingPulseProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/pulse.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/pulse.svg
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -24,13 +33,13 @@ export default function Drop({
cy="12" cy="12"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;11" values="0;11"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
repeatCount="indefinite" repeatCount="indefinite"
@@ -38,7 +47,7 @@ export default function Drop({
<animate <animate
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
repeatCount="indefinite" repeatCount="indefinite"

View File

@@ -1,22 +1,32 @@
import type { LoadingPulseProps } from "$/types/Loading"; import type { LoadingPulseProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function QuickDrop({ export default function QuickDrop({
size,
width, width,
height, height,
className, className,
animationDuration = 1.2, animationDuration = 1200,
color,
stroke, stroke,
fill fill,
}: LoadingPulseProps){ ariaLabel = "Loading"
}: Readonly<LoadingPulseProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/pulse-multiple.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/pulse-multiple.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -25,15 +35,15 @@ export default function QuickDrop({
cy="12" cy="12"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`drop1_${id}`} id={`drop1_${id}`}
begin={`0;drop3_${id}.end`} begin={`0;drop3_${id}.end`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;11" values="0;11"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
fill="freeze" fill="freeze"
@@ -42,7 +52,7 @@ export default function QuickDrop({
begin={`0;drop3_${id}.end`} begin={`0;drop3_${id}.end`}
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
fill="freeze" fill="freeze"
@@ -53,24 +63,24 @@ export default function QuickDrop({
cy="12" cy="12"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`drop2_${id}`} id={`drop2_${id}`}
begin={`drop1_${id}.begin+${animationDuration / 6}`} begin={`drop1_${id}.begin+${dur / 6}`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;11" values="0;11"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
fill="freeze" fill="freeze"
/> />
<animate <animate
begin={`drop1_${id}.begin+${animationDuration / 6}`} begin={`drop1_${id}.begin+${dur / 6}`}
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
fill="freeze" fill="freeze"
@@ -81,24 +91,24 @@ export default function QuickDrop({
cy="12" cy="12"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`drop3_${id}`} id={`drop3_${id}`}
begin={`drop1_${id}.begin+${animationDuration / 3}`} begin={`drop1_${id}.begin+${dur / 3}`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;11" values="0;11"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
fill="freeze" fill="freeze"
/> />
<animate <animate
begin={`drop1_${id}.begin+${animationDuration / 3}`} begin={`drop1_${id}.begin+${dur / 3}`}
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
fill="freeze" fill="freeze"

View File

@@ -1,22 +1,32 @@
import type { LoadingPulseProps } from "$/types/Loading"; import type { LoadingPulseProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function QuickWaveDrop({ export default function QuickWaveDrop({
size,
width, width,
height, height,
className, className,
animationDuration = 1.2, animationDuration = 1200,
color,
stroke, stroke,
fill fill,
}: LoadingPulseProps){ ariaLabel = "Loading"
}: Readonly<LoadingPulseProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/pulse-rings-multiple.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/pulse-rings-multiple.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -24,8 +34,8 @@ export default function QuickWaveDrop({
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z"
transform="translate(12, 12) scale(0)" transform="translate(12, 12) scale(0)"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animateTransform <animateTransform
id={`drop1_${id}`} id={`drop1_${id}`}
@@ -33,7 +43,7 @@ export default function QuickWaveDrop({
attributeName="transform" attributeName="transform"
calcMode="spline" calcMode="spline"
type="translate" type="translate"
dur={animationDuration} dur={dur}
values="12 12;0 0" values="12 12;0 0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
@@ -43,7 +53,7 @@ export default function QuickWaveDrop({
calcMode="spline" calcMode="spline"
additive="sum" additive="sum"
type="scale" type="scale"
dur={animationDuration} dur={dur}
values="0;1" values="0;1"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
@@ -51,7 +61,7 @@ export default function QuickWaveDrop({
begin={`0;drop3_${id}.end`} begin={`0;drop3_${id}.end`}
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
@@ -60,34 +70,34 @@ export default function QuickWaveDrop({
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z"
transform="translate(12, 12) scale(0)" transform="translate(12, 12) scale(0)"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animateTransform <animateTransform
id={`drop2_${id}`} id={`drop2_${id}`}
begin={`drop1_${id}.begin+${animationDuration / 6}s`} begin={`drop1_${id}.begin+${dur / 6}s`}
attributeName="transform" attributeName="transform"
calcMode="spline" calcMode="spline"
type="translate" type="translate"
dur={animationDuration} dur={dur}
values="12 12;0 0" values="12 12;0 0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
<animateTransform <animateTransform
begin={`drop1_${id}.begin+${animationDuration / 6}s`} begin={`drop1_${id}.begin+${dur / 6}s`}
attributeName="transform" attributeName="transform"
calcMode="spline" calcMode="spline"
additive="sum" additive="sum"
type="scale" type="scale"
dur={animationDuration} dur={dur}
values="0;1" values="0;1"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
<animate <animate
begin={`drop1_${id}.begin+${animationDuration / 6}s`} begin={`drop1_${id}.begin+${dur / 6}s`}
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
@@ -96,34 +106,34 @@ export default function QuickWaveDrop({
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z"
transform="translate(12, 12) scale(0)" transform="translate(12, 12) scale(0)"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animateTransform <animateTransform
id={`drop3_${id}`} id={`drop3_${id}`}
begin={`drop1_${id}.begin+${animationDuration / 3}s`} begin={`drop1_${id}.begin+${dur / 3}s`}
attributeName="transform" attributeName="transform"
calcMode="spline" calcMode="spline"
type="translate" type="translate"
dur={animationDuration} dur={dur}
values="12 12;0 0" values="12 12;0 0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
<animateTransform <animateTransform
begin={`drop1_${id}.begin+${animationDuration / 3}s`} begin={`drop1_${id}.begin+${dur / 3}s`}
attributeName="transform" attributeName="transform"
calcMode="spline" calcMode="spline"
additive="sum" additive="sum"
type="scale" type="scale"
dur={animationDuration} dur={dur}
values="0;1" values="0;1"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
<animate <animate
begin={`drop1_${id}.begin+${animationDuration / 3}s`} begin={`drop1_${id}.begin+${dur / 3}s`}
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />

View File

@@ -1,22 +1,32 @@
import type { LoadingPulseProps } from "$/types/Loading"; import type { LoadingPulseProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function TripleDrop({ export default function TripleDrop({
size,
width, width,
height, height,
className, className,
animationDuration = 1.2, animationDuration = 1200,
color,
stroke, stroke,
fill fill,
}: LoadingPulseProps){ ariaLabel = "Loading"
}: Readonly<LoadingPulseProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/pulse-3.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/pulse-3.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -25,24 +35,24 @@ export default function TripleDrop({
cy="12" cy="12"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`drop1_${id}`} id={`drop1_${id}`}
begin={`0;drop3_${id}.begin+${animationDuration / 3}s`} begin={`0;drop3_${id}.begin+${dur / 3}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;11" values="0;11"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
fill="freeze" fill="freeze"
/> />
<animate <animate
begin={`0;drop3_${id}.begin+${animationDuration / 3}s`} begin={`0;drop3_${id}.begin+${dur / 3}s`}
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
fill="freeze" fill="freeze"
@@ -53,24 +63,24 @@ export default function TripleDrop({
cy="12" cy="12"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`drop2_${id}`} id={`drop2_${id}`}
begin={`drop1_${id}.begin+${animationDuration / 3}s`} begin={`drop1_${id}.begin+${dur / 3}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;11" values="0;11"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
fill="freeze" fill="freeze"
/> />
<animate <animate
begin={`drop1_${id}.begin+${animationDuration / 3}s`} begin={`drop1_${id}.begin+${dur / 3}s`}
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
fill="freeze" fill="freeze"
@@ -81,24 +91,24 @@ export default function TripleDrop({
cy="12" cy="12"
r="0" r="0"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animate <animate
id={`drop3_${id}`} id={`drop3_${id}`}
begin={`drop1_${id}.begin+${animationDuration * 2 / 3}s`} begin={`drop1_${id}.begin+${dur * 2 / 3}s`}
attributeName="r" attributeName="r"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="0;11" values="0;11"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
fill="freeze" fill="freeze"
/> />
<animate <animate
begin={`drop1_${id}.begin+${animationDuration * 2 / 3}s`} begin={`drop1_${id}.begin+${dur * 2 / 3}s`}
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
fill="freeze" fill="freeze"

View File

@@ -1,22 +1,32 @@
import type { LoadingPulseProps } from "$/types/Loading"; import type { LoadingPulseProps } from "$/types/LoadingTypes";
import { usePrefersReducedMotion } from "$/util/AccessibilityUtil";
import { useId } from "react";
export default function TripleWaveDrop({ export default function TripleWaveDrop({
size,
width, width,
height, height,
className, className,
animationDuration = 1.2, animationDuration = 1200,
color,
stroke, stroke,
fill fill,
}: LoadingPulseProps){ ariaLabel = "Loading"
}: Readonly<LoadingPulseProps>){
//https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/pulse-rings-3.svg //https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-smil/pulse-rings-3.svg
const id = crypto.randomUUID().replaceAll("-", ""); const id = useId();
const reducedMotion = usePrefersReducedMotion();
const dur = reducedMotion ? animationDuration / 100 : animationDuration / 1000;
return ( return (
<svg <svg
width={width} width={size ?? width}
height={height} height={size ?? height}
role="status"
aria-live="polite"
aria-label={ariaLabel}
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
@@ -24,34 +34,34 @@ export default function TripleWaveDrop({
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z"
transform="translate(12, 12) scale(0)" transform="translate(12, 12) scale(0)"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animateTransform <animateTransform
id={`drop1_${id}`} id={`drop1_${id}`}
begin={`0;drop3_${id}.begin+${animationDuration / 3}s`} begin={`0;drop3_${id}.begin+${dur / 3}s`}
attributeName="transform" attributeName="transform"
calcMode="spline" calcMode="spline"
type="translate" type="translate"
dur={animationDuration} dur={dur}
values="12 12;0 0" values="12 12;0 0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
<animateTransform <animateTransform
begin={`0;drop3_${id}.begin+${animationDuration / 3}s`} begin={`0;drop3_${id}.begin+${dur / 3}s`}
attributeName="transform" attributeName="transform"
calcMode="spline" calcMode="spline"
additive="sum" additive="sum"
type="scale" type="scale"
dur={animationDuration} dur={dur}
values="0;1" values="0;1"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
<animate <animate
begin={`0;drop3_${id}.begin+${animationDuration / 3}s`} begin={`0;drop3_${id}.begin+${dur / 3}s`}
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
@@ -60,34 +70,34 @@ export default function TripleWaveDrop({
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z"
transform="translate(12, 12) scale(0)" transform="translate(12, 12) scale(0)"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animateTransform <animateTransform
id={`drop2_${id}`} id={`drop2_${id}`}
begin={`drop1_${id}.begin+${animationDuration / 3}s`} begin={`drop1_${id}.begin+${dur / 3}s`}
attributeName="transform" attributeName="transform"
calcMode="spline" calcMode="spline"
type="translate" type="translate"
dur={animationDuration} dur={dur}
values="12 12;0 0" values="12 12;0 0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
<animateTransform <animateTransform
begin={`drop1_${id}.begin+${animationDuration / 3}s`} begin={`drop1_${id}.begin+${dur / 3}s`}
attributeName="transform" attributeName="transform"
calcMode="spline" calcMode="spline"
additive="sum" additive="sum"
type="scale" type="scale"
dur={animationDuration} dur={dur}
values="0;1" values="0;1"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
<animate <animate
begin={`drop1_${id}.begin+${animationDuration / 3}s`} begin={`drop1_${id}.begin+${dur / 3}s`}
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
@@ -96,34 +106,34 @@ export default function TripleWaveDrop({
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z"
transform="translate(12, 12) scale(0)" transform="translate(12, 12) scale(0)"
className={className} className={className}
stroke={stroke} stroke={color ?? stroke}
fill={fill} fill={color ?? fill}
> >
<animateTransform <animateTransform
id={`drop3_${id}`} id={`drop3_${id}`}
begin={`drop1_${id}.begin+${animationDuration * 2 / 3}s`} begin={`drop1_${id}.begin+${dur * 2 / 3}s`}
attributeName="transform" attributeName="transform"
calcMode="spline" calcMode="spline"
type="translate" type="translate"
dur={animationDuration} dur={dur}
values="12 12;0 0" values="12 12;0 0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
<animateTransform <animateTransform
begin={`drop1_${id}.begin+${animationDuration * 2 / 3}s`} begin={`drop1_${id}.begin+${dur * 2 / 3}s`}
attributeName="transform" attributeName="transform"
calcMode="spline" calcMode="spline"
additive="sum" additive="sum"
type="scale" type="scale"
dur={animationDuration} dur={dur}
values="0;1" values="0;1"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />
<animate <animate
begin={`drop1_${id}.begin+${animationDuration * 2 / 3}s`} begin={`drop1_${id}.begin+${dur * 2 / 3}s`}
attributeName="opacity" attributeName="opacity"
calcMode="spline" calcMode="spline"
dur={animationDuration} dur={dur}
values="1;0" values="1;0"
keySplines=".52,.6,.25,.99" keySplines=".52,.6,.25,.99"
/> />

Some files were not shown because too many files have changed in this diff Show More