-
-
-
-
-
Example: file upload
-
-
-
Value: 15
-
Min value: 0
-
Max value: 18
-
-
-
-
-
-
-
-
-
-
Example: filter processing
-
-
-
Value: 0.92
-
Min value: 0
-
Max value: 1
-
-
-
-
-
-
-
-
-
- The value is the progress of a system operation (e.g., downloading,
- uploading, processing) within the progress circle’s range, from the
- min value to max value.
-
-
- The min and max values can also be customized appropriately for
- whatever the progress circle is showing. By default, the min value
- starts at 0 and the max value is set to 100.
-
-
- These values are not applicable when a progress circle is
- indeterminate.
-
-
-
- );
-}
diff --git a/apps/docs/src/components/Status/progresscircle/MinMaxValueView.tsx b/apps/docs/src/components/Status/progresscircle/MinMaxValueView.tsx
new file mode 100644
index 0000000..bd57af7
--- /dev/null
+++ b/apps/docs/src/components/Status/progresscircle/MinMaxValueView.tsx
@@ -0,0 +1,63 @@
+import React from "react";
+import { Grid, ProgressCircle, VBox } from "@react-elf/ui";
+
+export function MinMaxValueView() {
+ return (
+
+
+
+
+
Example: file upload
+
+
+
Value: 15
+
Min value: 0
+
Max value: 18
+
+
+
+
+
+
+
+
+
+
Example: filter processing
+
+
+
Value: 0.92
+
Min value: 0
+
Max value: 1
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/docs/src/components/Status/progresscircle/OverBackgroundVariantView.jsx b/apps/docs/src/components/Status/progresscircle/OverBackgroundVariantView.jsx
deleted file mode 100644
index bcb7ef9..0000000
--- a/apps/docs/src/components/Status/progresscircle/OverBackgroundVariantView.jsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import { Grid, ProgressCircle, VBox } from "@elf-framework/ui";
-
-export function OverBackgroundVariantView() {
- return (
-
-
-
-
-
-
-
- When a progress circle needs to be placed on top of a colored
- background, use the over background variant. This progress circle
- uses a static white color regardless of the color theme. Make sure
- the background offers enough contrast for the progress circle to be
- legible.
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/Status/progresscircle/OverBackgroundVariantView.tsx b/apps/docs/src/components/Status/progresscircle/OverBackgroundVariantView.tsx
new file mode 100644
index 0000000..2393ecf
--- /dev/null
+++ b/apps/docs/src/components/Status/progresscircle/OverBackgroundVariantView.tsx
@@ -0,0 +1,45 @@
+import React from "react";
+import { Grid, ProgressCircle, VBox } from "@react-elf/ui";
+
+export function OverBackgroundVariantView() {
+ return (
+
+
+
+ );
+}
diff --git a/apps/docs/src/components/Status/progresscircle/SizeView.jsx b/apps/docs/src/components/Status/progresscircle/SizeView.jsx
deleted file mode 100644
index be2ad8b..0000000
--- a/apps/docs/src/components/Status/progresscircle/SizeView.jsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import { Grid, ProgressCircle, VBox } from "@elf-framework/ui";
-
-export function SizeView() {
- return (
-
-
-
- {["small", "medium", "large", "extra-large"].map((size) => (
-
-
- {size}
-
-
-
- ))}
-
-
-
-
-
- Progress circles come in 3 sizes: small, medium (default), or large.
- These are available to fit various contexts. For example, the small
- progress circle can be used in place of an icon or in tight spaces,
- while the large one can be used for full-page loading.
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/Status/progresscircle/SizeView.tsx b/apps/docs/src/components/Status/progresscircle/SizeView.tsx
new file mode 100644
index 0000000..1abafe3
--- /dev/null
+++ b/apps/docs/src/components/Status/progresscircle/SizeView.tsx
@@ -0,0 +1,52 @@
+import React from "react";
+import { Grid, ProgressCircle, VBox } from "@react-elf/ui";
+
+export function SizeView() {
+ return (
+
+
+ {["small", "medium", "large", "extra-large"].map((size) => (
+
+
+ {size}
+
+
+
+ ))}
+
+
+ );
+}
diff --git a/apps/docs/src/components/Status/progresscircle/SpinView.jsx b/apps/docs/src/components/Status/progresscircle/SpinView.jsx
deleted file mode 100644
index 47e33bd..0000000
--- a/apps/docs/src/components/Status/progresscircle/SpinView.jsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import { Animation, Grid, ProgressCircle, VBox } from "@elf-framework/ui";
-
-export function SpinView() {
- return (
-
-
-
-
-
-
-
- When part of the page is waiting for asynchronous data or during a
- rendering process, an appropriate loading animation can effectively
- alleviate users' inquietude.
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/Status/progresscircle/SpinView.tsx b/apps/docs/src/components/Status/progresscircle/SpinView.tsx
new file mode 100644
index 0000000..49d926d
--- /dev/null
+++ b/apps/docs/src/components/Status/progresscircle/SpinView.tsx
@@ -0,0 +1,48 @@
+import React from "react";
+import {
+ SpinAnimation,
+ ProgressCircle,
+ VBox,
+ Button,
+ ActionGroup,
+} from "@react-elf/ui";
+
+export function SpinView() {
+ const ref = React.useRef(null);
+
+ return (
+
+
+
+
+
+
+
+
+ ref.current?.play()}>Play
+ ref.current?.pause()}>Pause
+ ref.current?.cancel()}>Cancel
+
+
+
+
+ );
+}
diff --git a/apps/docs/src/components/Status/progresscircle/ValueView.jsx b/apps/docs/src/components/Status/progresscircle/ValueView.jsx
deleted file mode 100644
index 9919250..0000000
--- a/apps/docs/src/components/Status/progresscircle/ValueView.jsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import { Button, Grid, ProgressBar, VBox } from "@elf-framework/ui";
-
-export function ValueView() {
- return (
-
-
-
-
-
-
-
- Progress bars can have a value label that gives detailed information
- about the progress (e.g. "60%" or "2 of 8"). This value label works
- alongside the label and should not be displayed if the label itself
- is not displayed. It should also not be displayed if the progress is
- indeterminate. Similar to the label, the value label is always
- placed above the track.
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/Status/progresscircle/VariantView.jsx b/apps/docs/src/components/Status/progresscircle/VariantView.jsx
deleted file mode 100644
index 8ca5249..0000000
--- a/apps/docs/src/components/Status/progresscircle/VariantView.jsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { Badge, Grid, VBox } from "@elf-framework/ui";
-
-import { ucwords } from "~/utils/ucwords";
-
-const variants = [
- "informative",
- "neutral",
- "positive",
- "notice",
- "negative",
- "indigo",
- "celery",
- "yellow",
- "magenta",
- "fuchsia",
- "purple",
- "seafoam",
-];
-
-export function VariantView() {
- return (
-
-
-
-
-
- {variants.map((variant) => {
- return (
-
-
- {ucwords(variant)}
-
-
- );
- })}
-
-
-
-
-
-
-
- When badges have a semantic meaning, they use semantic colors. Use
- these variants for the following statuses:
-
-
- Variant List
-
- {variants.map((variant) => {
- return {variant} ;
- })}
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/Status/progresscircle/WidthView.jsx b/apps/docs/src/components/Status/progresscircle/WidthView.jsx
deleted file mode 100644
index a44bb8d..0000000
--- a/apps/docs/src/components/Status/progresscircle/WidthView.jsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import { Button, Grid, ProgressBar, VBox } from "@elf-framework/ui";
-
-export function WidthView() {
- return (
-
-
-
-
-
}
- value={33}
- style={{
- width: 200,
- }}
- />
-
-
-
-
}
- value={33}
- style={{
- width: 240,
- }}
- />
-
-
-
-
-
-
- The width of a progress bar can be customized appropriately for its
- context. The default width is size-2400 (192 px on desktop and 240
- px on mobile).
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/actions/ActionGroup.mdx b/apps/docs/src/components/actions/ActionGroup.mdx
index 50709bd..0c4e4de 100644
--- a/apps/docs/src/components/actions/ActionGroup.mdx
+++ b/apps/docs/src/components/actions/ActionGroup.mdx
@@ -35,7 +35,9 @@ An action group can be either horizontal or vertical in its
orientation. By default, an action group is horizontal. The vertical
option should be reserved for when horizontal space is limited.
-
+
+
+
@@ -46,7 +48,9 @@ extra-large. The medium size is the default and most frequently used
option. Use the other sizes sparingly; they should be used to create a
hierarchy of importance within the page.
-
+
+
+
@@ -56,7 +60,9 @@ Action groups come in 2 densities: regular and compact. The compact
density retains the same font and icon sizes, but has tighter spacing.
The action buttons also become connected for non-quiet action groups.
-
+
+
+
@@ -66,7 +72,9 @@ Action groups come in 2 densities: regular and compact. The compact
density retains the same font and icon sizes, but has tighter spacing.
The action buttons also become connected for non-quiet action groups.
-
+
+
+
@@ -83,7 +91,9 @@ layout (vertical stack, table, grid) makes it easy to parse the
buttons. Too many quiet components in a small space can be hard to
read.
-
+
+
+
@@ -96,7 +106,9 @@ Selection can be enabled for an action group to allow for toggling.
This can be used to disclose parts of an interface (e.g., showing or
hiding panels) or to switch between views (e.g., grid or list views).
-
+
+
+
@@ -107,7 +119,9 @@ hiding panels) or to switch between views (e.g., grid or list views).
When selection is enabled, an action group can allow for single or
multiple selection of action buttons.
-
+
+
+
@@ -121,7 +135,9 @@ group is set to wrap, meaning that the action buttons inside the group
wrap to form another line. Alternatively, an action group can be set
to collapse inside a More (...) action button.
-
+
+
+
@@ -132,6 +148,8 @@ within the group exist, but are not available in that circumstance.
This state can be used to maintain layout continuity and to
communicate that an action group may become available later.
-
+
+
+
diff --git a/apps/docs/src/components/actions/ActionGroup.stories.tsx b/apps/docs/src/components/actions/ActionGroup.stories.tsx
index 59d1f8c..522a15f 100644
--- a/apps/docs/src/components/actions/ActionGroup.stories.tsx
+++ b/apps/docs/src/components/actions/ActionGroup.stories.tsx
@@ -1,6 +1,6 @@
import React from "react";
import type { Meta, StoryObj } from "@storybook/react";
-import { ActionGroup, Button } from "@react-elf/ui";
+import { ActionGroup, Button, IconWrapper } from "@react-elf/ui";
import { MdContentCopy, MdEdit, MdMore, MdPadding } from "react-icons/md";
const meta = {
@@ -17,21 +17,30 @@ type Story = StoryObj
;
*/
export const Default: Story = {
args: {
+ shape: "round",
children: (
<>
-
+
+
+
Edit
-
+
+
+
Copy
-
+
+
+
-
+
+
+
Edit
>
@@ -90,6 +99,7 @@ export const HorizontalQuiet: Story = {
export const Vertical: Story = {
args: {
direction: "vertical",
+ shape: "round",
children: (
<>
@@ -139,6 +149,7 @@ export const VerticalQuiet: Story = {
export const SizeDefault: Story = {
args: {
+ size: "medium",
children: (
<>
@@ -163,20 +174,21 @@ export const SizeDefault: Story = {
export const SizeSmall: Story = {
args: {
+ size: "small",
children: (
<>
-
+
Edit
-
+
Copy
-
+
-
+
Edit
@@ -187,20 +199,21 @@ export const SizeSmall: Story = {
export const SizeLarge: Story = {
args: {
+ size: "large",
children: (
<>
-
+
Edit
-
+
Copy
-
+
-
+
Edit
@@ -211,20 +224,21 @@ export const SizeLarge: Story = {
export const SizeExtraLarge: Story = {
args: {
+ size: "extra-large",
children: (
<>
-
+
Edit
-
+
Copy
-
+
-
+
Edit
@@ -311,6 +325,7 @@ export const JustifiedCompact: Story = {
export const Quiet: Story = {
args: {
+ quiet: true,
children: (
<>
@@ -336,9 +351,10 @@ export const Quiet: Story = {
export const Selection: Story = {
args: {
compact: true,
+ value: [1],
children: (
<>
-
+
Edit
@@ -360,13 +376,15 @@ export const Selection: Story = {
export const MultiSelection: Story = {
args: {
+ compact: true,
+ value: [1, 2],
children: (
<>
-
+
Edit
-
+
Copy
@@ -409,20 +427,21 @@ export const Collapsed: Story = {
export const Disabled: Story = {
args: {
+ disabled: true,
children: (
<>
-
+
Edit
-
+
Copy
-
+
-
+
Edit
diff --git a/apps/docs/src/components/actions/actiongroup/DensityView.tsx b/apps/docs/src/components/actions/actiongroup/DensityView.tsx
index 547576f..e00fcc7 100644
--- a/apps/docs/src/components/actions/actiongroup/DensityView.tsx
+++ b/apps/docs/src/components/actions/actiongroup/DensityView.tsx
@@ -1,17 +1,35 @@
import React from "react";
-import { ActionGroup, Button, Grid, IconButton, VBox } from "@react-elf/ui";
+import {
+ ActionGroup,
+ Button,
+ Grid,
+ IconButton,
+ IconWrapper,
+ VBox,
+} from "@react-elf/ui";
import { MdDescription, MdEdit, MdSettings } from "react-icons/md";
export function DensityView() {
return (
Regular
-
+
+
+
Document Setup
@@ -41,12 +59,14 @@ export function DensityView() {
-
+
Compact
-
+
+
+
Document Setup
@@ -84,11 +104,16 @@ export function DensityView() {
style={{ gap: 10, margin: 10 }}
>
-
+
+
+
Document Setup
- Settings
+
+
+ {" "}
+ Settings
-
+
-
+
diff --git a/apps/docs/src/components/actions/actiongroup/DisabledView.tsx b/apps/docs/src/components/actions/actiongroup/DisabledView.tsx
index 798085c..5dc5a30 100644
--- a/apps/docs/src/components/actions/actiongroup/DisabledView.tsx
+++ b/apps/docs/src/components/actions/actiongroup/DisabledView.tsx
@@ -9,17 +9,17 @@ export function DisabledView() {
>
- Wrap
+ Disabled
-
-
+
+
Document Setup
-
+
Settings
-
+
Copy
diff --git a/apps/docs/src/components/actions/actiongroup/EnableSelectionView.tsx b/apps/docs/src/components/actions/actiongroup/EnableSelectionView.tsx
index 4cffaa7..384a8bb 100644
--- a/apps/docs/src/components/actions/actiongroup/EnableSelectionView.tsx
+++ b/apps/docs/src/components/actions/actiongroup/EnableSelectionView.tsx
@@ -12,7 +12,7 @@ export function EnableSelectionView() {
Selection not enabled
-
+
Document Setup
@@ -34,8 +34,8 @@ export function EnableSelectionView() {
Selection enabled
-
-
+
+
Document Setup
@@ -43,8 +43,8 @@ export function EnableSelectionView() {
Settings
-
-
+
+
Document Setup
diff --git a/apps/docs/src/components/actions/actiongroup/JustifiedView.tsx b/apps/docs/src/components/actions/actiongroup/JustifiedView.tsx
index 2dd9681..e634a77 100644
--- a/apps/docs/src/components/actions/actiongroup/JustifiedView.tsx
+++ b/apps/docs/src/components/actions/actiongroup/JustifiedView.tsx
@@ -1,5 +1,11 @@
import React from "react";
-import { ActionGroup, Button, VBox } from "@react-elf/ui";
+import {
+ ActionGroup,
+ Button,
+ IconButton,
+ IconWrapper,
+ VBox,
+} from "@react-elf/ui";
import {
MdContentCopy,
MdDescription,
@@ -10,44 +16,70 @@ import {
export function JustifiedView() {
return (
-
+
Not justified
-
+
+
+
Document Setup
- Settings
+
+
+ {" "}
+ Settings
-
+
+
+
Document
- Copy
+
+
+ {" "}
+ Copy
- Settings
+
+
+ {" "}
+ Settings
-
+
-
-
+
+
-
-
+
+
-
+
-
+
Justified
@@ -75,15 +107,15 @@ export function JustifiedView() {
compact={true}
style={{ gap: 10, margin: "10px 10px" }}
>
-
+
-
-
+
+
-
-
+
+
-
+
diff --git a/apps/docs/src/components/actions/actiongroup/MultiSelectionView.tsx b/apps/docs/src/components/actions/actiongroup/MultiSelectionView.tsx
index 0a5ad62..c12bd4a 100644
--- a/apps/docs/src/components/actions/actiongroup/MultiSelectionView.tsx
+++ b/apps/docs/src/components/actions/actiongroup/MultiSelectionView.tsx
@@ -11,12 +11,12 @@ export function MultiSelectionView() {
Single Selection
-
-
+
+
Document Setup
-
+
Settings
@@ -28,12 +28,12 @@ export function MultiSelectionView() {
Multi Selection
-
-
+
+
Document Setup
-
+
Settings
diff --git a/apps/docs/src/components/actions/actiongroup/OptionsView.tsx b/apps/docs/src/components/actions/actiongroup/OptionsView.tsx
index 2434cbd..02d1d63 100644
--- a/apps/docs/src/components/actions/actiongroup/OptionsView.tsx
+++ b/apps/docs/src/components/actions/actiongroup/OptionsView.tsx
@@ -9,7 +9,7 @@ export function OptionsView() {
>
Horizontal
-
+
Document Setup
@@ -18,7 +18,7 @@ export function OptionsView() {
Settings
-
+
@@ -33,7 +33,11 @@ export function OptionsView() {
Vertical
-
+
Document Setup
@@ -45,6 +49,7 @@ export function OptionsView() {
diff --git a/apps/docs/src/components/actions/actiongroup/QuietView.tsx b/apps/docs/src/components/actions/actiongroup/QuietView.tsx
index 28caf5b..638e73c 100644
--- a/apps/docs/src/components/actions/actiongroup/QuietView.tsx
+++ b/apps/docs/src/components/actions/actiongroup/QuietView.tsx
@@ -5,7 +5,14 @@ import { MdDescription, MdSettings } from "react-icons/md";
export function QuietView() {
return (
@@ -25,7 +32,7 @@ export function QuietView() {
Quiet
-
+
Document Setup
diff --git a/apps/docs/src/components/actions/actiongroup/SizeView.tsx b/apps/docs/src/components/actions/actiongroup/SizeView.tsx
index 9220318..d64fb59 100644
--- a/apps/docs/src/components/actions/actiongroup/SizeView.tsx
+++ b/apps/docs/src/components/actions/actiongroup/SizeView.tsx
@@ -5,23 +5,57 @@ import { MdDescription, MdSettings } from "react-icons/md";
export function SizeView() {
return (
-
+
+ Extra Small
+
+
+
+ Document Setup
+
+
+ Settings
+
+
+
+
+
Small
-
-
+
+
Document Setup
-
+
Settings
-
+
Medium
-
+
Document Setup
@@ -32,9 +66,13 @@ export function SizeView() {
-
+
Large
-
+
Document Setup
@@ -44,9 +82,13 @@ export function SizeView() {
-
+
Extra Large
-
+
Document Setup
diff --git a/packages/@react-elf-types/animation/CHANGELOG.md b/packages/@react-elf-types/animation/CHANGELOG.md
new file mode 100644
index 0000000..96fd184
--- /dev/null
+++ b/packages/@react-elf-types/animation/CHANGELOG.md
@@ -0,0 +1,7 @@
+# @react-elf-types/animation
+
+## 0.0.92
+
+### Patch Changes
+
+- add ProgressCircle component, ActionGroupContext
diff --git a/packages/@react-elf-types/animation/package.json b/packages/@react-elf-types/animation/package.json
new file mode 100644
index 0000000..12d64f7
--- /dev/null
+++ b/packages/@react-elf-types/animation/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "@react-elf-types/animation",
+ "version": "0.0.92",
+ "description": "Elf UI components in React",
+ "license": "MIT",
+ "types": "src/index.d.ts",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/elf-framework/react-elf"
+ },
+ "dependencies": {
+ "@react-elf-types/shared": "workspace:*",
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/@react-elf-types/animation/src/index.d.ts b/packages/@react-elf-types/animation/src/index.d.ts
new file mode 100644
index 0000000..912e6d2
--- /dev/null
+++ b/packages/@react-elf-types/animation/src/index.d.ts
@@ -0,0 +1,35 @@
+import type { ReactNode } from "react";
+import { AnimationEvent } from "react";
+import { CommonStyle, SizeType, VariantType } from "@react-elf-types/shared";
+
+export type AnimationType =
+ | "spin"
+ | "ping"
+ | "fade"
+ | "scaledown"
+ | "bounce"
+ | "flash"
+ | "pulse"
+ | "rubberBand"
+ | "shake"
+ | "headShake"
+ | "swing"
+ | "tada"
+ | "wobble"
+ | "jello"
+ | "heartBeat";
+
+export interface AnimationProps {
+ name?: AnimationType;
+ duration?: number | `${number}s` | `${number}ms`;
+ delay?: number;
+ iterationCount?: string;
+ timingFunction?: string;
+ children?: ReactNode;
+ play?: boolean;
+ style?: CommonStyle;
+
+ onEnd?: (e: AnimationEvent) => void;
+ onIteration?: (e: AnimationEvent) => void;
+ onStart?: (e: AnimationEvent) => void;
+}
diff --git a/packages/@react-elf-types/button/CHANGELOG.md b/packages/@react-elf-types/button/CHANGELOG.md
index 61cf72b..a23861c 100644
--- a/packages/@react-elf-types/button/CHANGELOG.md
+++ b/packages/@react-elf-types/button/CHANGELOG.md
@@ -1,5 +1,11 @@
# @react-elf-types/button
+## 0.0.92
+
+### Patch Changes
+
+- add ProgressCircle component, ActionGroupContext
+
## 0.0.85
### Patch Changes
diff --git a/packages/@react-elf-types/button/package.json b/packages/@react-elf-types/button/package.json
index a35ee77..4c60670 100644
--- a/packages/@react-elf-types/button/package.json
+++ b/packages/@react-elf-types/button/package.json
@@ -1,6 +1,6 @@
{
"name": "@react-elf-types/button",
- "version": "0.0.85",
+ "version": "0.0.92",
"description": "Elf UI components in React",
"license": "MIT",
"types": "src/index.d.ts",
diff --git a/packages/@react-elf-types/button/src/index.d.ts b/packages/@react-elf-types/button/src/index.d.ts
index b53b154..ab14e9a 100644
--- a/packages/@react-elf-types/button/src/index.d.ts
+++ b/packages/@react-elf-types/button/src/index.d.ts
@@ -25,7 +25,7 @@ type ButtonVariant =
| "light"
| "dark"
| "white";
-type ButtonSize = "extra-small" | "small" | "default" | "large" | "extra-large";
+type ButtonSize = "extra-small" | "small" | "medium" | "large" | "extra-large";
type ButtonShape = "rect" | "round" | "circle";
@@ -50,14 +50,18 @@ interface ButtonProps {
hasMinWidth?: boolean;
onClick?: MouseEventHandler;
style?: ButtonStyle;
+ value?: ButtonValue;
thin?: boolean;
closable?: boolean;
href?: string;
target?: string;
pending?: boolean;
as?: string;
+ noContext?: boolean;
}
+type ButtonValue = string | number;
+
interface ActionGroupProps {
children?: ReactNode;
direction?: OrientationType;
@@ -67,7 +71,16 @@ interface ActionGroupProps {
justified?: boolean;
collapsed?: boolean;
disabled?: boolean;
- shape?: "rect" | "normal";
+ readonly?: boolean;
+ size?: ButtonSize;
+ shape?: ButtonShape;
+ variant?: ButtonVariant;
+ outline?: boolean;
+ className?: string;
+ iconOnly?: boolean;
+ thin?: boolean;
+ value?: ButtonValue[];
+ onChange?: (value: ButtonValue | ButtonValue[]) => void;
onMoreClick?: (event: PointerEvent, items: ReactNode[]) => void;
style?: ButtonStyle & CommonStyle;
boundary?: number;
diff --git a/packages/@react-elf-types/dialog/CHANGELOG.md b/packages/@react-elf-types/dialog/CHANGELOG.md
index 560f877..f09ce17 100644
--- a/packages/@react-elf-types/dialog/CHANGELOG.md
+++ b/packages/@react-elf-types/dialog/CHANGELOG.md
@@ -1,5 +1,12 @@
# @react-elf-types/notification
+## 0.0.92
+
+### Patch Changes
+
+- Updated dependencies []:
+ - @react-elf-types/button@0.0.92
+
## 0.0.85
### Patch Changes
diff --git a/packages/@react-elf-types/dialog/package.json b/packages/@react-elf-types/dialog/package.json
index bda1f9d..f566b14 100644
--- a/packages/@react-elf-types/dialog/package.json
+++ b/packages/@react-elf-types/dialog/package.json
@@ -1,6 +1,6 @@
{
"name": "@react-elf-types/dialog",
- "version": "0.0.85",
+ "version": "0.0.92",
"description": "Elf UI components in React",
"license": "MIT",
"types": "src/index.d.ts",
diff --git a/packages/@react-elf-types/progress-circle/CHANGELOG.md b/packages/@react-elf-types/progress-circle/CHANGELOG.md
new file mode 100644
index 0000000..dd555c8
--- /dev/null
+++ b/packages/@react-elf-types/progress-circle/CHANGELOG.md
@@ -0,0 +1,7 @@
+# @react-elf-types/progress-circle
+
+## 0.0.92
+
+### Patch Changes
+
+- add ProgressCircle component, ActionGroupContext
diff --git a/packages/@react-elf-types/progress-circle/package.json b/packages/@react-elf-types/progress-circle/package.json
new file mode 100644
index 0000000..e881422
--- /dev/null
+++ b/packages/@react-elf-types/progress-circle/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "@react-elf-types/progress-circle",
+ "version": "0.0.92",
+ "description": "Elf UI components in React",
+ "license": "MIT",
+ "types": "src/index.d.ts",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/elf-framework/react-elf"
+ },
+ "dependencies": {
+ "@react-elf-types/shared": "workspace:*",
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/@react-elf-types/progress-circle/src/index.d.ts b/packages/@react-elf-types/progress-circle/src/index.d.ts
new file mode 100644
index 0000000..b379d9a
--- /dev/null
+++ b/packages/@react-elf-types/progress-circle/src/index.d.ts
@@ -0,0 +1,17 @@
+import { CommonStyle, SizeType, VariantType } from "@react-elf-types/shared";
+
+type ProgressCircleAnimationType = "normal" | "spin";
+
+export interface ProgressCircleProps {
+ value?: number;
+
+ showValue?: boolean;
+ variant?: VariantType;
+ size?: SizeType;
+ style?: CommonStyle;
+ max?: number;
+ min?: number;
+ indeterminate?: boolean;
+ animated?: boolean;
+ animationType?: ProgressCircleAnimationType;
+}
diff --git a/packages/@react-elf/animation/.gitignore b/packages/@react-elf/animation/.gitignore
new file mode 100644
index 0000000..12c18d4
--- /dev/null
+++ b/packages/@react-elf/animation/.gitignore
@@ -0,0 +1 @@
+/lib/
diff --git a/packages/@react-elf/animation/CHANGELOG.md b/packages/@react-elf/animation/CHANGELOG.md
new file mode 100644
index 0000000..5013086
--- /dev/null
+++ b/packages/@react-elf/animation/CHANGELOG.md
@@ -0,0 +1,10 @@
+# @react-elf/animation
+
+## 0.0.92
+
+### Patch Changes
+
+- add ProgressCircle component, ActionGroupContext
+
+- Updated dependencies []:
+ - @react-elf-types/animation@0.0.92
diff --git a/packages/@react-elf/animation/index.ts b/packages/@react-elf/animation/index.ts
new file mode 100644
index 0000000..3bd16e1
--- /dev/null
+++ b/packages/@react-elf/animation/index.ts
@@ -0,0 +1 @@
+export * from "./src";
diff --git a/packages/@react-elf/animation/package.json b/packages/@react-elf/animation/package.json
new file mode 100644
index 0000000..db8edc1
--- /dev/null
+++ b/packages/@react-elf/animation/package.json
@@ -0,0 +1,46 @@
+{
+ "name": "@react-elf/animation",
+ "version": "0.0.92",
+ "description": "",
+ "files": [
+ "dist",
+ "README.md",
+ "package.json"
+ ],
+ "main": "./src/index.ts",
+ "publishConfig": {
+ "access": "public",
+ "main": "./dist/index.cjs",
+ "module": "./dist/index.mjs",
+ "types": "./dist/types/index.d.ts"
+ },
+ "scripts": {
+ "npm-check": "npx npm-check-updates -u",
+ "vite:dist": "vite build --config=vite.dist.config.ts",
+ "lib:build": "pnpm run vite:dist",
+ "test": "vitest",
+ "coverage": "vitest --coverage"
+ },
+ "keywords": [
+ "react",
+ "elf",
+ "animation"
+ ],
+ "author": "elf-framework",
+ "license": "MIT",
+ "dependencies": {
+ "@react-elf-types/animation": "workspace:*",
+ "@react-elf/shared": "workspace:*",
+ "@types/react": "^18.2.15",
+ "classnames": "^2.3.2",
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
+ },
+ "devDependencies": {
+ "@testing-library/jest-dom": "^5.17.0",
+ "@testing-library/react": "^14.0.0",
+ "vite": "^4.4.6",
+ "vite-plugin-dts": "^3.3.1",
+ "vitest": "0.33.0"
+ }
+}
diff --git a/packages/@react-elf/animation/src/Animation.tsx b/packages/@react-elf/animation/src/Animation.tsx
new file mode 100644
index 0000000..d62c513
--- /dev/null
+++ b/packages/@react-elf/animation/src/Animation.tsx
@@ -0,0 +1,164 @@
+import React, {
+ ForwardRefRenderFunction,
+ Ref,
+ forwardRef,
+ useCallback,
+ useImperativeHandle,
+ useRef,
+} from "react";
+
+import { makeCssVariablePrefixMap, propertyMap } from "@react-elf/shared";
+import { AnimationProps } from "@react-elf-types/animation";
+
+const cssProperties = makeCssVariablePrefixMap("--elf--animation", {
+ name: true,
+ iterationCount: true,
+ timingFunction: true,
+ duration: true,
+ delay: true,
+ playState: true,
+});
+
+interface AnimationHandle {
+ play: () => void;
+ pause: () => void;
+ cancel: () => void;
+}
+
+export const AnimationComp: ForwardRefRenderFunction<
+ AnimationHandle,
+ AnimationProps
+> = (props, ref) => {
+ const {
+ name = "spin",
+ delay = "0s",
+ iterationCount,
+ timingFunction,
+ duration = "1s",
+ style = {},
+ children,
+ play = false,
+ onEnd,
+ onIteration,
+ onStart,
+ } = props;
+
+ const localRef = useRef(null);
+
+ useImperativeHandle(ref, () => ({
+ play: () => {
+ localRef.current?.style.setProperty(
+ "--elf--animation-play-state",
+ "running"
+ );
+ },
+ pause: () => {
+ localRef.current?.style.setProperty(
+ "--elf--animation-play-state",
+ "paused"
+ );
+ },
+ cancel: () => {},
+ }));
+
+ const onAnimationStart = useCallback((e) => onStart?.(e), [onStart]);
+ const onAnimationEnd = useCallback((e) => onEnd?.(e), [onEnd]);
+ const onAnimationIteration = useCallback(
+ (e) => onIteration?.(e),
+ [onIteration]
+ );
+
+ const styleObject = {
+ className: "elf--animation",
+ style: propertyMap(
+ {
+ ...style,
+ duration,
+ name,
+ iterationCount,
+ timingFunction,
+ delay,
+ playState: play ? "running" : "paused",
+ },
+ cssProperties
+ ),
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const Animation = forwardRef(
+ AnimationComp
+);
+
+export const SpinAnimation = forwardRef(
+ (props, ref) => {
+ return ;
+ }
+);
+
+// Animation.ping = function (props) {
+// return ;
+// };
+
+// Animation.fade = function (props) {
+// return ;
+// };
+
+// Animation.scaledown = function (props) {
+// return ;
+// };
+
+// Animation.bounce = function (props) {
+// return ;
+// };
+
+// Animation.flash = function (props) {
+// return ;
+// };
+
+// Animation.pulse = function (props) {
+// return ;
+// };
+
+// Animation.rubberBand = function (props) {
+// return ;
+// };
+
+// Animation.shake = function (props) {
+// return ;
+// };
+
+// Animation.headShake = function (props) {
+// return ;
+// };
+
+// Animation.swing = function (props) {
+// return ;
+// };
+
+// Animation.tada = function (props) {
+// return ;
+// };
+
+// Animation.wobble = function (props) {
+// return ;
+// };
+
+// Animation.jello = function (props) {
+// return ;
+// };
+
+// Animation.heartBeat = function (props) {
+// return ;
+// };
diff --git a/packages/@react-elf/animation/src/index.ts b/packages/@react-elf/animation/src/index.ts
new file mode 100644
index 0000000..20c780d
--- /dev/null
+++ b/packages/@react-elf/animation/src/index.ts
@@ -0,0 +1 @@
+export * from "./Animation";
diff --git a/packages/@react-elf/animation/test/Animation.test.tsx b/packages/@react-elf/animation/test/Animation.test.tsx
new file mode 100644
index 0000000..78eec4b
--- /dev/null
+++ b/packages/@react-elf/animation/test/Animation.test.tsx
@@ -0,0 +1,17 @@
+import { render, screen } from "@testing-library/react";
+import React from "react";
+import { describe, expect, it } from "vitest";
+
+import { SpinAnimation } from "../src/Animation";
+
+describe("Animation", () => {
+ it("renders Animation", () => {
+ const { container } = render(body );
+
+ // screen.debug(container);
+
+ const tooltipEl = container.querySelector(".elf--animation");
+
+ expect(tooltipEl.classList.contains("primary")).toBe(true);
+ });
+});
diff --git a/packages/@react-elf/animation/test/setup.js b/packages/@react-elf/animation/test/setup.js
new file mode 100644
index 0000000..694ae66
--- /dev/null
+++ b/packages/@react-elf/animation/test/setup.js
@@ -0,0 +1,11 @@
+import matchers from "@testing-library/jest-dom/matchers";
+import { cleanup } from "@testing-library/react";
+import { expect, afterEach } from "vitest";
+
+// extends Vitest's expect method with methods from react-testing-library
+expect.extend(matchers);
+
+// runs a cleanup after each test case (e.g. clearing jsdom)
+afterEach(() => {
+ cleanup();
+});
diff --git a/packages/@react-elf/animation/tsconfig.json b/packages/@react-elf/animation/tsconfig.json
new file mode 100644
index 0000000..567e608
--- /dev/null
+++ b/packages/@react-elf/animation/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "../../../tsconfig.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "outDir": "dist",
+ "jsx": "react"
+ },
+ "include": ["src"],
+ "exclude": []
+}
diff --git a/packages/@react-elf/animation/vite.config.js b/packages/@react-elf/animation/vite.config.js
new file mode 100644
index 0000000..1f5ed97
--- /dev/null
+++ b/packages/@react-elf/animation/vite.config.js
@@ -0,0 +1,12 @@
+import react from "@vitejs/plugin-react";
+import { defineConfig } from "vite";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ test: {
+ globals: true,
+ environment: "jsdom",
+ setupFiles: "./test/setup.js",
+ },
+});
diff --git a/packages/@react-elf/animation/vite.dist.config.ts b/packages/@react-elf/animation/vite.dist.config.ts
new file mode 100644
index 0000000..fe4ae13
--- /dev/null
+++ b/packages/@react-elf/animation/vite.dist.config.ts
@@ -0,0 +1,46 @@
+import { defineConfig } from "vite";
+
+import path from "path";
+import dts from "vite-plugin-dts";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ css: {
+ modules: {
+ localsConvention: "camelCaseOnly",
+ },
+ },
+ esbuild: {
+ keepNames: true,
+ tsconfigRaw: {
+ compilerOptions: {
+ jsx: "react",
+ },
+ },
+ },
+ build: {
+ emptyOutDir: false,
+ minify: false,
+ lib: {
+ entry: path.resolve(__dirname, "src/index.ts"),
+ name: "@react-elf/animation",
+ fileName: "index",
+ },
+ rollupOptions: {
+ // make sure to externalize deps that shouldn't be bundled
+ // into your library
+ external: ["react", "react-dom", "@react-elf/shared", "classnames"],
+ output: {
+ // Provide global variables to use in the UMD build
+ // for externalized deps
+ globals: {
+ react: "React",
+ "react-dom": "ReactDOM",
+ "@react-elf/shared": "ReactElfShared",
+ classnames: "classNames",
+ },
+ },
+ },
+ },
+ plugins: [dts({ outDir: "dist/types" })],
+});
diff --git a/packages/@react-elf/button/CHANGELOG.md b/packages/@react-elf/button/CHANGELOG.md
index 0479aa5..f3a660e 100644
--- a/packages/@react-elf/button/CHANGELOG.md
+++ b/packages/@react-elf/button/CHANGELOG.md
@@ -1,5 +1,14 @@
# @react-elf/button
+## 0.0.92
+
+### Patch Changes
+
+- add ProgressCircle component, ActionGroupContext
+
+- Updated dependencies []:
+ - @react-elf-types/button@0.0.92
+
## 0.0.88
### Patch Changes
diff --git a/packages/@react-elf/button/package.json b/packages/@react-elf/button/package.json
index c8a15e8..3eed8c0 100644
--- a/packages/@react-elf/button/package.json
+++ b/packages/@react-elf/button/package.json
@@ -1,6 +1,6 @@
{
"name": "@react-elf/button",
- "version": "0.0.88",
+ "version": "0.0.92",
"description": "",
"files": [
"dist",
@@ -29,6 +29,7 @@
"author": "elf-framework",
"license": "MIT",
"dependencies": {
+ "@react-elf-types/shared": "workspace:*",
"@react-elf-types/button": "workspace:*",
"@react-elf/shared": "workspace:*",
"@react-elf/tooltip": "workspace:*",
diff --git a/packages/@react-elf/button/src/ActionGroup.tsx b/packages/@react-elf/button/src/ActionGroup.tsx
index b3850de..08d9569 100644
--- a/packages/@react-elf/button/src/ActionGroup.tsx
+++ b/packages/@react-elf/button/src/ActionGroup.tsx
@@ -10,6 +10,8 @@ import {
import { Tooltip } from "@react-elf/tooltip";
import { Button } from "./Button";
+import { ActionGroupContext } from "./context";
+import { useActionGroupState } from "./hooks/useActionGroupState";
const cssProperties = makeCssVariablePrefixMap("--elf--action-group", {
alignItems: true,
@@ -31,6 +33,8 @@ export function ActionGroup(props: ActionGroupProps) {
...extraStyle
} = props;
+ const state = useActionGroupState(props);
+
const elRef = useRef(null);
const [visibleTargetList, setVisibilityTargetList] = useState([]);
const [rootRect, setRootRect] = useState(null);
@@ -127,18 +131,20 @@ export function ActionGroup(props: ActionGroupProps) {
return (
- {items}
- {hiddenItems.length ? (
-
- {moreIcon}
-
- ) : undefined}
+
+ {items}
+ {hiddenItems.length ? (
+
+ {moreIcon}
+
+ ) : undefined}
+
);
}
diff --git a/packages/@react-elf/button/src/Button.tsx b/packages/@react-elf/button/src/Button.tsx
index f8d2f8e..302645e 100644
--- a/packages/@react-elf/button/src/Button.tsx
+++ b/packages/@react-elf/button/src/Button.tsx
@@ -1,6 +1,13 @@
-import { ButtonProps } from "@react-elf-types/button";
+import React, { createRef, useContext, useMemo } from "react";
+import {
+ ButtonProps,
+ ButtonShape,
+ ButtonSize,
+ ButtonVariant,
+} from "@react-elf-types/button";
import { makeCssVariablePrefixMap, propertyMap } from "@react-elf/shared";
import classnames from "classnames";
+import { ActionGroupContext } from "./context";
const cssProperties = makeCssVariablePrefixMap("--elf--button", {
borderColor: true,
@@ -16,17 +23,31 @@ const cssProperties = makeCssVariablePrefixMap("--elf--button", {
borderRadius: true,
});
+interface ButtonItemProps {
+ variant?: ButtonVariant;
+ size?: ButtonSize;
+ disabled?: boolean;
+ selected?: boolean;
+ shape?: ButtonShape;
+ quiet?: boolean;
+ outline?: boolean;
+ thin?: boolean;
+ iconOnly?: boolean;
+}
+
export function Button(props: ButtonProps) {
const {
- variant = "default",
- size = "medium",
- disabled = false,
+ variant,
+ size,
+ disabled,
selected,
+ shape,
+ quiet,
+ outline,
+ thin,
+ iconOnly,
+
focused,
- shape = "none",
- quiet = false,
- outline = false,
- thin = false,
closable = false,
place = "",
style = {},
@@ -34,36 +55,90 @@ export function Button(props: ButtonProps) {
target = "_blank",
children,
className = "",
- iconOnly = false,
justified = false,
pending = false,
play = false,
hover = false,
as = "button",
hasMinWidth = false,
+ noContext = false,
+
...extraProps
} = props;
- const localClass = classnames([
- "elf--button",
- {
- selected,
- outline,
+ // Only Button
+ const buttonRef = createRef();
+
+ // for CheckboxGroup
+ let groupState = useContext(ActionGroupContext);
+
+ const localProps: ButtonItemProps = {};
+
+ if (!groupState || noContext) {
+ localProps.variant = props.variant;
+ localProps.size = props.size;
+ localProps.disabled = props.disabled;
+ localProps.selected = props.selected;
+ localProps.shape = props.shape;
+ localProps.quiet = props.quiet;
+ localProps.outline = props.outline;
+ localProps.thin = props.thin;
+ localProps.iconOnly = props.iconOnly;
+ } else {
+ localProps.variant = groupState.variant;
+ localProps.size = groupState.size;
+ localProps.disabled = groupState.isDisabled;
+ localProps.selected = groupState.isSelected(props.value);
+ localProps.shape = groupState.shape;
+ localProps.quiet = groupState.quiet;
+ localProps.outline = groupState.outline;
+ localProps.thin = groupState.thin;
+ localProps.iconOnly = groupState.iconOnly;
+ }
+
+ const localClass = useMemo(
+ () =>
+ classnames([
+ "elf--button",
+ {
+ selected: localProps.selected || false,
+ outline: localProps.outline || false,
+ focused,
+ quiet: localProps.quiet || false,
+ closable,
+ justified,
+ [localProps.variant || "default"]: true,
+ [localProps.size || "medium"]: true,
+ [localProps.shape || "rect"]: true,
+ [place]: true,
+ thin: localProps.thin || false,
+ hover,
+ "icon-only": localProps.iconOnly,
+ "has-min-width": hasMinWidth,
+ },
+ className,
+ ]),
+ [
+ /* context state */
+ localProps.selected,
+ localProps.outline,
+ localProps.variant,
+ localProps.size,
+ localProps.shape,
+ localProps.thin,
+ localProps.quiet,
+ localProps.iconOnly,
+
+ /* props */
focused,
- quiet,
closable,
justified,
- [variant]: true,
- [size]: true,
- [shape]: true,
- [place]: true,
- thin,
+ place,
hover,
- "icon-only": iconOnly,
- "has-min-width": hasMinWidth,
- },
- className,
- ]);
+ hasMinWidth,
+ className,
+ ]
+ );
const buttonContent = (
@@ -80,7 +155,7 @@ export function Button(props: ButtonProps) {
if (as === "link") {
const styleObject = {
className: localClass,
- disabled,
+ disabled: localProps.disabled || false,
style: propertyMap(style, cssProperties),
...extraProps,
onClick: undefined,
@@ -93,10 +168,14 @@ export function Button(props: ButtonProps) {
} else {
const styleObject = {
className: localClass,
- disabled,
+ disabled: localProps.disabled || false,
style: propertyMap(style, cssProperties),
...extraProps,
};
- return {buttonContent} ;
+ return (
+
+ {buttonContent}
+
+ );
}
}
diff --git a/packages/@react-elf/button/src/IconButton.tsx b/packages/@react-elf/button/src/IconButton.tsx
index 4962050..986a3fd 100644
--- a/packages/@react-elf/button/src/IconButton.tsx
+++ b/packages/@react-elf/button/src/IconButton.tsx
@@ -1,6 +1,12 @@
+import React from "react";
import { ButtonProps } from "@react-elf-types/button";
import { RoundButton } from "./RoundButton";
+import { IconWrapper } from "@react-elf/shared";
-export function IconButton(props: ButtonProps) {
- return ( );
- }
\ No newline at end of file
+export function IconButton({ children, ...props }: ButtonProps) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/packages/@react-elf/button/src/context.tsx b/packages/@react-elf/button/src/context.tsx
new file mode 100644
index 0000000..3af357a
--- /dev/null
+++ b/packages/@react-elf/button/src/context.tsx
@@ -0,0 +1,4 @@
+import { createContext } from "react";
+import { ActionGroupState } from "./hooks/useActionGroupState";
+
+export const ActionGroupContext = createContext(null);
diff --git a/packages/@react-elf/button/src/hooks/useActionGroupState.tsx b/packages/@react-elf/button/src/hooks/useActionGroupState.tsx
new file mode 100644
index 0000000..e5e4a5e
--- /dev/null
+++ b/packages/@react-elf/button/src/hooks/useActionGroupState.tsx
@@ -0,0 +1,88 @@
+import { useCallback, useState } from "react";
+import {
+ ActionGroupProps,
+ ButtonShape,
+ ButtonSize,
+ ButtonValue,
+ ButtonVariant,
+} from "@react-elf-types/button";
+
+export interface ActionGroupState {
+ readonly value: readonly ButtonValue[];
+ readonly variant: ButtonVariant;
+ readonly size: ButtonSize;
+ readonly isDisabled: boolean;
+ readonly isReadOnly: boolean;
+ readonly quiet: boolean;
+ readonly shape: ButtonShape;
+ readonly thin: boolean;
+ readonly outline: boolean;
+ readonly iconOnly: boolean;
+
+ isSelected(val: any): boolean;
+ setValue(val: any): void;
+ toggleValue(val: any): void;
+ removeValue(val: any): void;
+}
+
+export function useActionGroupState(props: ActionGroupProps): ActionGroupState {
+ const [selectedValues, setValue] = useState(props.value || []);
+
+ const setStateValue = useCallback(
+ (value: ButtonValue[], index?: number) => {
+ setValue(value);
+ props.onChange?.(value);
+ },
+ [setValue, props.onChange]
+ );
+
+ const state = {
+ value: selectedValues,
+ variant: props.variant,
+ size: props.size,
+ quiet: props.quiet,
+ shape: props.shape,
+ thin: props.thin,
+ outline: props.outline,
+ isDisabled: props.disabled,
+ isReadOnly: props.readonly,
+ iconOnly: props.iconOnly,
+ setValue: (value) => {
+ if (props.readonly || props.disabled) {
+ return;
+ }
+
+ setStateValue(value);
+ },
+ isSelected: (val: any) => {
+ return selectedValues?.includes(val);
+ },
+ toggleValue: (val: any) => {
+ if (props.readonly || props.disabled) {
+ return;
+ }
+
+ if (selectedValues.includes(val)) {
+ setStateValue(selectedValues.filter((v) => v !== val));
+ } else {
+ setStateValue([...selectedValues, val]);
+ }
+ },
+ removeValue: (val: any) => {
+ if (props.readonly || props.disabled) {
+ return;
+ }
+
+ setStateValue(selectedValues.filter((v) => v !== val));
+ },
+ addValue: (val: any) => {
+ if (props.readonly || props.disabled) {
+ return;
+ }
+
+ setStateValue([...selectedValues, val]);
+ },
+ };
+
+ return state;
+}
diff --git a/packages/@react-elf/color-mixer/CHANGELOG.md b/packages/@react-elf/color-mixer/CHANGELOG.md
index d3d9335..61a8373 100644
--- a/packages/@react-elf/color-mixer/CHANGELOG.md
+++ b/packages/@react-elf/color-mixer/CHANGELOG.md
@@ -1,5 +1,12 @@
# @react-elf/color-mixer
+## 0.0.92
+
+### Patch Changes
+
+- Updated dependencies []:
+ - @react-elf/button@0.0.92
+
## 0.0.88
### Patch Changes
diff --git a/packages/@react-elf/color-mixer/package.json b/packages/@react-elf/color-mixer/package.json
index b2354ee..c566715 100644
--- a/packages/@react-elf/color-mixer/package.json
+++ b/packages/@react-elf/color-mixer/package.json
@@ -1,6 +1,6 @@
{
"name": "@react-elf/color-mixer",
- "version": "0.0.88",
+ "version": "0.0.92",
"description": "",
"files": [
"dist",
diff --git a/packages/@react-elf/dialog/CHANGELOG.md b/packages/@react-elf/dialog/CHANGELOG.md
index 316c31f..c237397 100644
--- a/packages/@react-elf/dialog/CHANGELOG.md
+++ b/packages/@react-elf/dialog/CHANGELOG.md
@@ -1,5 +1,13 @@
# @react-elf/dialog
+## 0.0.92
+
+### Patch Changes
+
+- Updated dependencies []:
+ - @react-elf/button@0.0.92
+ - @react-elf-types/dialog@0.0.92
+
## 0.0.88
### Patch Changes
diff --git a/packages/@react-elf/dialog/package.json b/packages/@react-elf/dialog/package.json
index 69b5d66..093eece 100644
--- a/packages/@react-elf/dialog/package.json
+++ b/packages/@react-elf/dialog/package.json
@@ -1,6 +1,6 @@
{
"name": "@react-elf/dialog",
- "version": "0.0.88",
+ "version": "0.0.92",
"description": "",
"files": [
"dist",
diff --git a/packages/@react-elf/progress-circle/.gitignore b/packages/@react-elf/progress-circle/.gitignore
new file mode 100644
index 0000000..12c18d4
--- /dev/null
+++ b/packages/@react-elf/progress-circle/.gitignore
@@ -0,0 +1 @@
+/lib/
diff --git a/packages/@react-elf/progress-circle/CHANGELOG.md b/packages/@react-elf/progress-circle/CHANGELOG.md
new file mode 100644
index 0000000..f9d916f
--- /dev/null
+++ b/packages/@react-elf/progress-circle/CHANGELOG.md
@@ -0,0 +1,19 @@
+# @react-elf/progress-bar
+
+## 0.0.92
+
+### Patch Changes
+
+- add ProgressCircle component, ActionGroupContext
+
+- Updated dependencies []:
+ - @react-elf-types/progress-circle@0.0.92
+
+## 0.0.91
+
+### Patch Changes
+
+- add ProgressBar component
+
+- Updated dependencies []:
+ - @react-elf-types/progress-bar@0.0.91
diff --git a/packages/@react-elf/progress-circle/index.ts b/packages/@react-elf/progress-circle/index.ts
new file mode 100644
index 0000000..3bd16e1
--- /dev/null
+++ b/packages/@react-elf/progress-circle/index.ts
@@ -0,0 +1 @@
+export * from "./src";
diff --git a/packages/@react-elf/progress-circle/package.json b/packages/@react-elf/progress-circle/package.json
new file mode 100644
index 0000000..10ef6d7
--- /dev/null
+++ b/packages/@react-elf/progress-circle/package.json
@@ -0,0 +1,46 @@
+{
+ "name": "@react-elf/progress-circle",
+ "version": "0.0.92",
+ "description": "",
+ "files": [
+ "dist",
+ "README.md",
+ "package.json"
+ ],
+ "main": "./src/index.ts",
+ "publishConfig": {
+ "access": "public",
+ "main": "./dist/index.cjs",
+ "module": "./dist/index.mjs",
+ "types": "./dist/types/index.d.ts"
+ },
+ "scripts": {
+ "npm-check": "npx npm-check-updates -u",
+ "vite:dist": "vite build --config=vite.dist.config.ts",
+ "lib:build": "pnpm run vite:dist",
+ "test": "vitest",
+ "coverage": "vitest --coverage"
+ },
+ "keywords": [
+ "react",
+ "elf",
+ "progress circle"
+ ],
+ "author": "elf-framework",
+ "license": "MIT",
+ "dependencies": {
+ "@react-elf-types/progress-circle": "workspace:*",
+ "@react-elf/shared": "workspace:*",
+ "@types/react": "^18.2.15",
+ "classnames": "^2.3.2",
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
+ },
+ "devDependencies": {
+ "@testing-library/jest-dom": "^5.17.0",
+ "@testing-library/react": "^14.0.0",
+ "vite": "^4.4.6",
+ "vite-plugin-dts": "^3.3.1",
+ "vitest": "0.33.0"
+ }
+}
diff --git a/packages/@react-elf/progress-circle/src/ProgressCircle.tsx b/packages/@react-elf/progress-circle/src/ProgressCircle.tsx
new file mode 100644
index 0000000..ab93b23
--- /dev/null
+++ b/packages/@react-elf/progress-circle/src/ProgressCircle.tsx
@@ -0,0 +1,73 @@
+import React, { ForwardRefRenderFunction, forwardRef, useMemo } from "react";
+import classNames from "classnames";
+
+import { makeCssVariablePrefixMap, propertyMap } from "@react-elf/shared";
+import { ProgressCircleProps } from "@react-elf-types/progress-circle";
+
+const cssProperties = makeCssVariablePrefixMap("--elf--progress-circle", {
+ backgroundColor: true,
+ color: true,
+ duration: true,
+ offset: true,
+ width: true,
+});
+
+interface ProgressCircleHandle {}
+
+const ProgressCircleComp: ForwardRefRenderFunction<
+ ProgressCircleHandle,
+ ProgressCircleProps
+> = (props, ref) => {
+ const {
+ min = 0,
+ max = 100,
+
+ variant = "default",
+ size = "medium",
+ style = {},
+ value,
+ indeterminate = false,
+ animated = false,
+ animationType = "normal",
+ } = props;
+
+ const localClass = useMemo(() => {
+ return classNames("elf--progress-circle", {
+ [variant]: true,
+ [size]: true,
+ animated,
+ indeterminate,
+ [animationType]: true,
+ });
+ }, [variant, size, indeterminate, animated, animationType]);
+
+ const currentValue = typeof value === "number" ? value : min;
+ const percentValue = (currentValue - min) / (max - min);
+
+ const styleObject = {
+ className: localClass,
+ style: propertyMap(
+ {
+ ...style,
+ offset: percentValue,
+ },
+ cssProperties
+ ),
+ };
+
+ return (
+
+ );
+};
+
+export const ProgressCircle = forwardRef<
+ ProgressCircleHandle,
+ ProgressCircleProps
+>(ProgressCircleComp);
diff --git a/packages/@react-elf/progress-circle/src/index.ts b/packages/@react-elf/progress-circle/src/index.ts
new file mode 100644
index 0000000..da15e09
--- /dev/null
+++ b/packages/@react-elf/progress-circle/src/index.ts
@@ -0,0 +1 @@
+export * from "./ProgressCircle";
diff --git a/packages/@react-elf/progress-circle/test/ProgressCircle.test.tsx b/packages/@react-elf/progress-circle/test/ProgressCircle.test.tsx
new file mode 100644
index 0000000..df61e2a
--- /dev/null
+++ b/packages/@react-elf/progress-circle/test/ProgressCircle.test.tsx
@@ -0,0 +1,17 @@
+import { render, screen } from "@testing-library/react";
+import React from "react";
+import { describe, expect, it } from "vitest";
+
+import { ProgressCircle } from "../src";
+
+describe("ProgressCircle", () => {
+ it("renders ProgressCircle", () => {
+ const { container } = render( );
+
+ // screen.debug(container);
+
+ const tooltipEl = container.querySelector(".elf--tooltip");
+
+ expect(tooltipEl.classList.contains("primary")).toBe(true);
+ });
+});
diff --git a/packages/@react-elf/progress-circle/test/setup.js b/packages/@react-elf/progress-circle/test/setup.js
new file mode 100644
index 0000000..694ae66
--- /dev/null
+++ b/packages/@react-elf/progress-circle/test/setup.js
@@ -0,0 +1,11 @@
+import matchers from "@testing-library/jest-dom/matchers";
+import { cleanup } from "@testing-library/react";
+import { expect, afterEach } from "vitest";
+
+// extends Vitest's expect method with methods from react-testing-library
+expect.extend(matchers);
+
+// runs a cleanup after each test case (e.g. clearing jsdom)
+afterEach(() => {
+ cleanup();
+});
diff --git a/packages/@react-elf/progress-circle/tsconfig.json b/packages/@react-elf/progress-circle/tsconfig.json
new file mode 100644
index 0000000..567e608
--- /dev/null
+++ b/packages/@react-elf/progress-circle/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "../../../tsconfig.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "outDir": "dist",
+ "jsx": "react"
+ },
+ "include": ["src"],
+ "exclude": []
+}
diff --git a/packages/@react-elf/progress-circle/vite.config.js b/packages/@react-elf/progress-circle/vite.config.js
new file mode 100644
index 0000000..1f5ed97
--- /dev/null
+++ b/packages/@react-elf/progress-circle/vite.config.js
@@ -0,0 +1,12 @@
+import react from "@vitejs/plugin-react";
+import { defineConfig } from "vite";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ test: {
+ globals: true,
+ environment: "jsdom",
+ setupFiles: "./test/setup.js",
+ },
+});
diff --git a/packages/@react-elf/progress-circle/vite.dist.config.ts b/packages/@react-elf/progress-circle/vite.dist.config.ts
new file mode 100644
index 0000000..f4d82af
--- /dev/null
+++ b/packages/@react-elf/progress-circle/vite.dist.config.ts
@@ -0,0 +1,46 @@
+import { defineConfig } from "vite";
+
+import path from "path";
+import dts from "vite-plugin-dts";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ css: {
+ modules: {
+ localsConvention: "camelCaseOnly",
+ },
+ },
+ esbuild: {
+ keepNames: true,
+ tsconfigRaw: {
+ compilerOptions: {
+ jsx: "react",
+ },
+ },
+ },
+ build: {
+ emptyOutDir: false,
+ minify: false,
+ lib: {
+ entry: path.resolve(__dirname, "src/index.ts"),
+ name: "@react-elf/progress-circle",
+ fileName: "index",
+ },
+ rollupOptions: {
+ // make sure to externalize deps that shouldn't be bundled
+ // into your library
+ external: ["react", "react-dom", "@react-elf/shared", "classnames"],
+ output: {
+ // Provide global variables to use in the UMD build
+ // for externalized deps
+ globals: {
+ react: "React",
+ "react-dom": "ReactDOM",
+ "@react-elf/shared": "ReactElfShared",
+ classnames: "classNames",
+ },
+ },
+ },
+ },
+ plugins: [dts({ outDir: "dist/types" })],
+});
diff --git a/packages/@react-elf/toast/CHANGELOG.md b/packages/@react-elf/toast/CHANGELOG.md
index 4435be7..f0da22d 100644
--- a/packages/@react-elf/toast/CHANGELOG.md
+++ b/packages/@react-elf/toast/CHANGELOG.md
@@ -1,5 +1,12 @@
# @react-elf/dialog
+## 0.0.92
+
+### Patch Changes
+
+- Updated dependencies []:
+ - @react-elf/button@0.0.92
+
## 0.0.88
### Patch Changes
diff --git a/packages/@react-elf/toast/package.json b/packages/@react-elf/toast/package.json
index 175a140..14c2fcd 100644
--- a/packages/@react-elf/toast/package.json
+++ b/packages/@react-elf/toast/package.json
@@ -1,6 +1,6 @@
{
"name": "@react-elf/toast",
- "version": "0.0.88",
+ "version": "0.0.92",
"description": "",
"files": [
"dist",
diff --git a/packages/@react-elf/ui/CHANGELOG.md b/packages/@react-elf/ui/CHANGELOG.md
index 20f39e2..5b38dfb 100644
--- a/packages/@react-elf/ui/CHANGELOG.md
+++ b/packages/@react-elf/ui/CHANGELOG.md
@@ -1,5 +1,19 @@
# @react-elf/button
+## 0.0.92
+
+### Patch Changes
+
+- add ProgressCircle component, ActionGroupContext
+
+- Updated dependencies []:
+ - @react-elf/button@0.0.92
+ - @react-elf/animation@0.0.92
+ - @react-elf/progress-circle@0.0.92
+ - @react-elf/color-mixer@0.0.92
+ - @react-elf/dialog@0.0.92
+ - @react-elf/toast@0.0.92
+
## 0.0.91
### Patch Changes
diff --git a/packages/@react-elf/ui/package.json b/packages/@react-elf/ui/package.json
index 6b10bd4..d8c9358 100644
--- a/packages/@react-elf/ui/package.json
+++ b/packages/@react-elf/ui/package.json
@@ -1,6 +1,6 @@
{
"name": "@react-elf/ui",
- "version": "0.0.91",
+ "version": "0.0.92",
"description": "",
"files": [
"dist",
@@ -52,7 +52,9 @@
"@react-elf/input-editor": "workspace:*",
"@react-elf/switch": "workspace:*",
"@react-elf/slider": "workspace:*",
- "@react-elf/progress-bar": "workspace:*"
+ "@react-elf/progress-bar": "workspace:*",
+ "@react-elf/progress-circle": "workspace:*",
+ "@react-elf/animation": "workspace:*"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.17.0",
diff --git a/packages/@react-elf/ui/src/index.ts b/packages/@react-elf/ui/src/index.ts
index d485824..dc685d6 100644
--- a/packages/@react-elf/ui/src/index.ts
+++ b/packages/@react-elf/ui/src/index.ts
@@ -23,3 +23,5 @@ export * from "@react-elf/input-editor";
export * from "@react-elf/switch";
export * from "@react-elf/slider";
export * from "@react-elf/progress-bar";
+export * from "@react-elf/progress-circle";
+export * from "@react-elf/animation";
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ff7c4be..a699766 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -153,6 +153,15 @@ importers:
specifier: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
version: 18.2.0
+ packages/@react-elf-types/animation:
+ dependencies:
+ '@react-elf-types/shared':
+ specifier: workspace:*
+ version: link:../shared
+ react:
+ specifier: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+ version: 18.2.0
+
packages/@react-elf-types/badge:
dependencies:
'@react-elf-types/shared':
@@ -307,6 +316,15 @@ importers:
specifier: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
version: 18.2.0
+ packages/@react-elf-types/progress-circle:
+ dependencies:
+ '@react-elf-types/shared':
+ specifier: workspace:*
+ version: link:../shared
+ react:
+ specifier: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+ version: 18.2.0
+
packages/@react-elf-types/radio:
dependencies:
'@react-elf-types/shared':
@@ -419,6 +437,43 @@ importers:
specifier: 0.33.0
version: 0.33.0
+ packages/@react-elf/animation:
+ dependencies:
+ '@react-elf-types/animation':
+ specifier: workspace:*
+ version: link:../../@react-elf-types/animation
+ '@react-elf/shared':
+ specifier: workspace:*
+ version: link:../shared
+ '@types/react':
+ specifier: ^18.2.15
+ version: 18.2.16
+ classnames:
+ specifier: ^2.3.2
+ version: 2.3.2
+ react:
+ specifier: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+ version: 18.2.0
+ react-dom:
+ specifier: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+ version: 18.2.0(react@18.2.0)
+ devDependencies:
+ '@testing-library/jest-dom':
+ specifier: ^5.17.0
+ version: 5.17.0
+ '@testing-library/react':
+ specifier: ^14.0.0
+ version: 14.0.0(react-dom@18.2.0)(react@18.2.0)
+ vite:
+ specifier: ^4.4.6
+ version: 4.4.7(@types/node@20.4.5)(less@4.1.3)(sass@1.64.1)
+ vite-plugin-dts:
+ specifier: ^3.3.1
+ version: 3.3.1(@types/node@20.4.5)(typescript@5.1.6)(vite@4.4.7)
+ vitest:
+ specifier: 0.33.0
+ version: 0.33.0
+
packages/@react-elf/badge:
dependencies:
'@react-elf-types/badge':
@@ -458,6 +513,9 @@ importers:
'@react-elf-types/button':
specifier: workspace:*
version: link:../../@react-elf-types/button
+ '@react-elf-types/shared':
+ specifier: workspace:*
+ version: link:../../@react-elf-types/shared
'@react-elf/shared':
specifier: workspace:*
version: link:../shared
@@ -968,6 +1026,43 @@ importers:
specifier: 0.33.0
version: 0.33.0
+ packages/@react-elf/progress-circle:
+ dependencies:
+ '@react-elf-types/progress-circle':
+ specifier: workspace:*
+ version: link:../../@react-elf-types/progress-circle
+ '@react-elf/shared':
+ specifier: workspace:*
+ version: link:../shared
+ '@types/react':
+ specifier: ^18.2.15
+ version: 18.2.16
+ classnames:
+ specifier: ^2.3.2
+ version: 2.3.2
+ react:
+ specifier: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+ version: 18.2.0
+ react-dom:
+ specifier: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+ version: 18.2.0(react@18.2.0)
+ devDependencies:
+ '@testing-library/jest-dom':
+ specifier: ^5.17.0
+ version: 5.17.0
+ '@testing-library/react':
+ specifier: ^14.0.0
+ version: 14.0.0(react-dom@18.2.0)(react@18.2.0)
+ vite:
+ specifier: ^4.4.6
+ version: 4.4.7(@types/node@20.4.5)(less@4.1.3)(sass@1.64.1)
+ vite-plugin-dts:
+ specifier: ^3.3.1
+ version: 3.3.1(@types/node@20.4.5)(typescript@5.1.6)(vite@4.4.7)
+ vitest:
+ specifier: 0.33.0
+ version: 0.33.0
+
packages/@react-elf/radio:
dependencies:
'@react-elf-types/radio':
@@ -1278,6 +1373,9 @@ importers:
'@react-elf/alert':
specifier: workspace:*
version: link:../alert
+ '@react-elf/animation':
+ specifier: workspace:*
+ version: link:../animation
'@react-elf/badge':
specifier: workspace:*
version: link:../badge
@@ -1323,6 +1421,9 @@ importers:
'@react-elf/progress-bar':
specifier: workspace:*
version: link:../progress-bar
+ '@react-elf/progress-circle':
+ specifier: workspace:*
+ version: link:../progress-circle
'@react-elf/radio':
specifier: workspace:*
version: link:../radio