Skip to content

Commit

Permalink
refactor: Segmented Control styled 레이어 추가, useSize 의존성 제거
Browse files Browse the repository at this point in the history
  • Loading branch information
te6-in committed Jan 9, 2025
1 parent 7255ea2 commit 9366d93
Show file tree
Hide file tree
Showing 26 changed files with 735 additions and 588 deletions.
8 changes: 4 additions & 4 deletions docs/components/example/segmented-control-disabled.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { SegmentedControl, Segment } from "seed-design/ui/segmented-control";
import { SegmentedControl, SegmentedControlSegment } from "seed-design/ui/segmented-control";

export default function SegmentedControlPreview() {
return (
<SegmentedControl defaultValue="Hot" disabled>
<Segment value="Hot">Hot</Segment>
<Segment value="New">New</Segment>
<SegmentedControl defaultValue="Hot" disabled aria-label="Sort by">
<SegmentedControlSegment value="Hot">Hot</SegmentedControlSegment>
<SegmentedControlSegment value="New">New</SegmentedControlSegment>
</SegmentedControl>
);
}
8 changes: 4 additions & 4 deletions docs/components/example/segmented-control-fixed-width.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { SegmentedControl, Segment } from "seed-design/ui/segmented-control";
import { SegmentedControl, SegmentedControlSegment } from "seed-design/ui/segmented-control";

export default function SegmentedControlFixedWidth() {
return (
<SegmentedControl defaultValue="new">
<Segment value="new">New</Segment>
<Segment value="hot">Hot</Segment>
<SegmentedControl defaultValue="new" style={{ width: "600px" }} aria-label="Sort by">
<SegmentedControlSegment value="new">New</SegmentedControlSegment>
<SegmentedControlSegment value="hot">Hot</SegmentedControlSegment>
</SegmentedControl>
);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { SegmentedControl, Segment } from "seed-design/ui/segmented-control";
import { SegmentedControl, SegmentedControlSegment } from "seed-design/ui/segmented-control";

export default function SegmentedControlLongLabelFixedWidth() {
return (
<SegmentedControl defaultValue="price">
<Segment value="price">가격 높은 순</Segment>
<Segment value="discount">할인율 높은 순</Segment>
<Segment value="popularity">인기 많은 순</Segment>
<SegmentedControl defaultValue="price" style={{ width: "600px" }} aria-label="정렬 기준">
<SegmentedControlSegment value="price">가격 높은 순</SegmentedControlSegment>
<SegmentedControlSegment value="discount">할인율 높은 순</SegmentedControlSegment>
<SegmentedControlSegment value="popularity">인기 많은 순</SegmentedControlSegment>
</SegmentedControl>
);
}
10 changes: 5 additions & 5 deletions docs/components/example/segmented-control-long-label.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { SegmentedControl, Segment } from "seed-design/ui/segmented-control";
import { SegmentedControl, SegmentedControlSegment } from "seed-design/ui/segmented-control";

export default function SegmentedControlLongLabel() {
return (
<SegmentedControl defaultValue="price">
<Segment value="price">가격 높은 순</Segment>
<Segment value="discount">할인율 높은 순</Segment>
<Segment value="popularity">인기 많은 순</Segment>
<SegmentedControl defaultValue="price" aria-label="정렬 기준">
<SegmentedControlSegment value="price">가격 높은 순</SegmentedControlSegment>
<SegmentedControlSegment value="discount">할인율 높은 순</SegmentedControlSegment>
<SegmentedControlSegment value="popularity">인기 많은 순</SegmentedControlSegment>
</SegmentedControl>
);
}
8 changes: 4 additions & 4 deletions docs/components/example/segmented-control-preview.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { SegmentedControl, Segment } from "seed-design/ui/segmented-control";
import { SegmentedControl, SegmentedControlSegment } from "seed-design/ui/segmented-control";

export default function SegmentedControlPreview() {
return (
<SegmentedControl defaultValue="Hot">
<Segment value="Hot">Hot</Segment>
<Segment value="New">New</Segment>
<SegmentedControl defaultValue="Hot" aria-label="Sort by">
<SegmentedControlSegment value="Hot">Hot</SegmentedControlSegment>
<SegmentedControlSegment value="New">New</SegmentedControlSegment>
</SegmentedControl>
);
}
4 changes: 2 additions & 2 deletions docs/content/docs/react/components/segmented-control.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ title: Segmented Control

<ReactTypeTable
path="./registry/ui/segmented-control.tsx"
name="SegmentProps"
name="SegmentedControlSegmentProps"
/>

## 예제
Expand All @@ -46,7 +46,7 @@ Pill 형태의 `Segment` 한 개는 86px의 최소 너비를 가져요. 제공

### Fixed Width

`SegmentControl``style` prop에 `width`를 제공해서 직접 너비를 설정할 수 있어요.
`SegmentedControl``style` prop에 `width`를 제공해서 직접 너비를 설정할 수 있어요.

<Callout type="info" title="지정한 너비가 무시되는 경우">

Expand Down
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"@seed-design/react": "0.0.0",
"@seed-design/react-icon": "^0.7.3",
"@seed-design/react-popover": "0.0.0-alpha-20241030023710",
"@seed-design/react-segmented-control": "workspace:^",
"@seed-design/react-segmented-control": "0.0.0",
"@seed-design/react-tabs": "0.0.0-alpha-20241209060641",
"@seed-design/recipe": "0.0.0-alpha-20241212122822",
"@seed-design/rootage-cli": "0.0.0",
Expand Down
123 changes: 40 additions & 83 deletions docs/registry/ui/segmented-control.tsx
Original file line number Diff line number Diff line change
@@ -1,101 +1,58 @@
"use client";

import "@seed-design/stylesheet/segmentedControl.css";
import {
useSegmentedControl,
type SegmentItemProps,
type UseSegmentedControlProps,
} from "@seed-design/react-segmented-control";
import * as React from "react";
import clsx from "clsx";
import {
segmentedControl,
type SegmentedControlVariantProps,
} from "@seed-design/recipe/segmentedControl";
import type { Assign } from "../util/types";
import { visuallyHidden } from "../util/visuallyHidden";

const SegmentedControlContext = React.createContext<{
api: ReturnType<typeof useSegmentedControl>;
} | null>(null);

const useSegmentedControlContext = () => {
const context = React.useContext(SegmentedControlContext);
if (!context)
throw new Error("Segment cannot be rendered outside the SegmentedControl");

return context;
};
import { SegmentedControl as SeedSegmentedControl } from "@seed-design/react";
import * as React from "react";

export type SegmentedControlProps = Assign<
React.HTMLAttributes<HTMLDivElement>,
UseSegmentedControlProps
> &
SegmentedControlVariantProps & {};
export interface SegmentedControlProps extends SeedSegmentedControl.RootProps {}

export const SegmentedControl = React.forwardRef<
HTMLDivElement,
SegmentedControlProps
>(({ className, children, ...otherProps }, ref) => {
const api = useSegmentedControl(otherProps);

const { rootProps, restProps, indicatorProps } = api;

const classNames = segmentedControl();
>(({ children, ...otherProps }, ref) => {
if (!otherProps["aria-label"] && !otherProps["aria-labelledby"]) {
console.warn(
"SegmentedControl component requires either an `aria-label` or `aria-labelledby` attribute.",
);
}

const [mounted, setMounted] = React.useState(false);
React.useEffect(() => setMounted(true), []);
if (otherProps.value === undefined && otherProps.defaultValue === undefined) {
console.warn(
"SegmentedControl component requires either a `value` or `defaultValue` attribute.",
);
}

return (
<div
ref={ref}
{...rootProps}
{...restProps}
{...(!mounted && { style: { display: "flex" } })}
className={clsx(classNames.root, className)}
>
{/* TODO */}
{/* <div {...labelProps} className={classNames.label} /> */}
<SegmentedControlContext.Provider value={{ api }}>
{children}
</SegmentedControlContext.Provider>
<div aria-hidden className={classNames.indicator} {...indicatorProps} />
</div>
<SeedSegmentedControl.Root ref={ref} {...otherProps}>
{children}
<SeedSegmentedControl.Indicator />
</SeedSegmentedControl.Root>
);
});
SegmentedControl.displayName = "SegmentedControl";

export interface SegmentProps
extends Assign<React.HTMLAttributes<HTMLLabelElement>, SegmentItemProps> {}
export interface SegmentedControlSegmentProps
extends SeedSegmentedControl.SegmentProps {
inputProps?: React.InputHTMLAttributes<HTMLInputElement>;

export const Segment = React.forwardRef<HTMLLabelElement, SegmentProps>(
({ className, children, value, ...otherProps }, ref) => {
const {
api: { getSegmentProps },
} = useSegmentedControlContext();
rootRef?: React.Ref<HTMLLabelElement>;
}

const { rootProps, hiddenInputProps, stateProps } = getSegmentProps({
value,
});
const classNames = segmentedControl();

return (
<label
ref={ref}
className={clsx(classNames.segment, className)}
{...rootProps}
{...otherProps}
>
<input {...hiddenInputProps} style={visuallyHidden} />
<div {...stateProps} className={classNames.segmentLabel}>
{children}
</div>
<div aria-hidden className={classNames.segmentLabelPlaceholder}>
{children}
</div>
</label>
);
},
);

Segment.displayName = "Segment";
export const SegmentedControlSegment = React.forwardRef<
HTMLInputElement,
SegmentedControlSegmentProps
>(({ children, inputProps, rootRef, ...otherProps }, ref) => {
return (
<SeedSegmentedControl.Segment ref={rootRef} {...otherProps}>
<SeedSegmentedControl.SegmentHiddenInput ref={ref} {...inputProps} />
<SeedSegmentedControl.SegmentLabel>
{children}
</SeedSegmentedControl.SegmentLabel>
<SeedSegmentedControl.SegmentLabelPlaceholder aria-hidden>
{children}
</SeedSegmentedControl.SegmentLabelPlaceholder>
</SeedSegmentedControl.Segment>
);
});
SegmentedControlSegment.displayName = "SegmentedControlSegment";
29 changes: 20 additions & 9 deletions docs/stories/SegmentedControl.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import type { Meta, StoryObj } from "@storybook/react";

import { SegmentedControl, Segment } from "seed-design/ui/segmented-control";
import { SegmentedControl, SegmentedControlSegment } from "seed-design/ui/segmented-control";

import { segmentedControlVariantMap } from "@seed-design/recipe/segmentedControl";
import { SeedThemeDecorator } from "./components/decorator";
import { VariantTable } from "./components/variant-table";
import { useState } from "react";

const Component = () => {
const Component = ({ disabled }: { disabled: boolean }) => {
const values = ["dolor", "magna", "sint"];
const [value, setValue] = useState(values[0]);

return (
<SegmentedControl value={value} onValueChange={setValue}>
<SegmentedControl value={value} onValueChange={setValue} disabled={disabled}>
{values.map((value) => (
<Segment key={value} value={value}>
<SegmentedControlSegment key={value} value={value}>
{value}
</Segment>
</SegmentedControlSegment>
))}
</SegmentedControl>
);
Expand All @@ -31,12 +31,23 @@ export default meta;

type Story = StoryObj<typeof meta>;

const CommonStoryTemplate: Story = {
args: {
defaultValue: "1",
const conditionMap = {
disabled: {
false: { disabled: false },
true: { disabled: true },
},
};

const CommonStoryTemplate: Story = {
render: function Render(args) {
return <VariantTable Component={Component} variantMap={segmentedControlVariantMap} {...args} />;
return (
<VariantTable
Component={Component}
variantMap={segmentedControlVariantMap}
conditionMap={conditionMap}
{...args}
/>
);
},
};

Expand Down
Loading

0 comments on commit 9366d93

Please sign in to comment.