Skip to content

Commit

Permalink
refactor: rename slots of Segmented Control
Browse files Browse the repository at this point in the history
  • Loading branch information
te6-in committed Nov 28, 2024
1 parent 809aded commit 16663bd
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 60 deletions.
2 changes: 1 addition & 1 deletion docs/public/__registry__/ui/segmented-control.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
{
"name": "segmented-control.tsx",
"type": "ui",
"content": "\"use client\";\n\nimport \"@seed-design/stylesheet/segmentedControl.css\";\nimport {\n useTabs,\n type TriggerProps,\n type UseTabsProps,\n} from \"@seed-design/react-tabs\";\nimport * as React from \"react\";\nimport clsx from \"clsx\";\nimport {\n segmentedControl,\n type SegmentedControlVariantProps,\n} from \"@seed-design/recipe/segmentedControl\";\nimport type { Assign } from \"../util/types\";\nexport interface SegmentedControlProps extends SegmentedControlVariantProps {}\n\nconst TabsContext = React.createContext<{\n api: ReturnType<typeof useTabs>;\n} | null>(null);\n\nconst useTabsContext = () => {\n const context = React.useContext(TabsContext);\n if (!context)\n throw new Error(\n \"SegmentedControlOption cannot be rendered outside the SegmentedControl\",\n );\n\n return context;\n};\n\nexport interface SegmentedControlProps\n extends SegmentedControlVariantProps,\n Pick<UseTabsProps, \"value\" | \"defaultValue\" | \"onValueChange\"> {}\n\ntype ReactSegmentedControlProps = SegmentedControlProps &\n Assign<React.HTMLAttributes<HTMLDivElement>, UseTabsProps>;\n\nexport const SegmentedControl = React.forwardRef<\n // HTMLFieldSetElement,\n HTMLDivElement,\n ReactSegmentedControlProps\n>(({ className, children, style, ...otherProps }, ref) => {\n const api = useTabs(otherProps);\n const { tabTriggerListProps, triggerSize, tabIndicatorProps } = api;\n\n const { left, width } = triggerSize;\n\n // TODO: value/defaultvalue 없는 경우 첫 번째 아이템으로 default (tabs 참고)\n\n const classNames = segmentedControl();\n\n return (\n <div\n style={{\n ...style,\n // XXX: tabCount 썼을 때 hydration 문제\n gridTemplateColumns: `repeat(${React.Children.count(children)}, 1fr)`,\n }}\n className={clsx(classNames.root, className)}\n ref={ref}\n {...tabTriggerListProps}\n {...otherProps}\n >\n <TabsContext.Provider value={{ api }}>{children}</TabsContext.Provider>\n <div\n aria-hidden\n className={classNames.indicator}\n {...tabIndicatorProps}\n style={{ left, width }}\n />\n </div>\n );\n});\nSegmentedControl.displayName = \"SegmentedControl\";\n\nexport interface SegmentedControlOptionProps\n extends SegmentedControlVariantProps,\n Omit<TriggerProps, \"isDisabled\"> {}\n\ntype ReactSegmentedControlOptionProps = Assign<\n React.HTMLAttributes<HTMLButtonElement>,\n SegmentedControlOptionProps\n>;\n\nexport const SegmentedControlOption = React.forwardRef<\n HTMLButtonElement,\n ReactSegmentedControlOptionProps\n>(({ className, children, value, ...otherProps }, ref) => {\n const {\n api: { getTabTriggerProps },\n } = useTabsContext();\n\n const { rootProps, labelProps } = getTabTriggerProps({ value });\n\n const classNames = segmentedControl();\n\n return (\n <button\n ref={ref}\n className={clsx(classNames.option, className)}\n {...rootProps}\n {...otherProps}\n >\n <div className={classNames.optionLabel} {...labelProps} tabIndex={-1}>\n {children}\n </div>\n <div aria-hidden className={classNames.optionLabelPlaceholder}>\n {children}\n </div>\n </button>\n );\n});\n\nSegmentedControlOption.displayName = \"SegmentedControlOption\";\n"
"content": "\"use client\";\n\nimport \"@seed-design/stylesheet/segmentedControl.css\";\nimport {\n useTabs,\n type TriggerProps,\n type UseTabsProps,\n} from \"@seed-design/react-tabs\";\nimport * as React from \"react\";\nimport clsx from \"clsx\";\nimport {\n segmentedControl,\n type SegmentedControlVariantProps,\n} from \"@seed-design/recipe/segmentedControl\";\nimport type { Assign } from \"../util/types\";\nexport interface SegmentedControlProps extends SegmentedControlVariantProps {}\n\nconst TabsContext = React.createContext<{\n api: ReturnType<typeof useTabs>;\n} | null>(null);\n\nconst useTabsContext = () => {\n const context = React.useContext(TabsContext);\n if (!context)\n throw new Error(\n \"SegmentedControlOption cannot be rendered outside the SegmentedControl\",\n );\n\n return context;\n};\n\nexport interface SegmentedControlProps\n extends SegmentedControlVariantProps,\n Pick<UseTabsProps, \"value\" | \"defaultValue\" | \"onValueChange\"> {}\n\ntype ReactSegmentedControlProps = SegmentedControlProps &\n Assign<React.HTMLAttributes<HTMLDivElement>, UseTabsProps>;\n\nexport const SegmentedControl = React.forwardRef<\n // HTMLFieldSetElement,\n HTMLDivElement,\n ReactSegmentedControlProps\n>(({ className, children, style, ...otherProps }, ref) => {\n const api = useTabs(otherProps);\n const { tabTriggerListProps, triggerSize, tabIndicatorProps } = api;\n\n const { left, width } = triggerSize;\n\n // TODO: value/defaultvalue 없는 경우 첫 번째 아이템으로 default (tabs 참고)\n\n const classNames = segmentedControl();\n\n return (\n <div\n style={{\n ...style,\n // XXX: tabCount 썼을 때 hydration 문제\n gridTemplateColumns: `repeat(${React.Children.count(children)}, 1fr)`,\n }}\n className={clsx(classNames.root, className)}\n ref={ref}\n {...tabTriggerListProps}\n {...otherProps}\n >\n <TabsContext.Provider value={{ api }}>{children}</TabsContext.Provider>\n <div\n aria-hidden\n className={classNames.selectedIndicator}\n {...tabIndicatorProps}\n style={{ left, width }}\n />\n </div>\n );\n});\nSegmentedControl.displayName = \"SegmentedControl\";\n\nexport interface SegmentedControlOptionProps\n extends SegmentedControlVariantProps,\n Omit<TriggerProps, \"isDisabled\"> {}\n\ntype ReactSegmentedControlOptionProps = Assign<\n React.HTMLAttributes<HTMLButtonElement>,\n SegmentedControlOptionProps\n>;\n\nexport const SegmentedControlOption = React.forwardRef<\n HTMLButtonElement,\n ReactSegmentedControlOptionProps\n>(({ className, children, value, ...otherProps }, ref) => {\n const {\n api: { getTabTriggerProps },\n } = useTabsContext();\n\n const { rootProps, labelProps } = getTabTriggerProps({ value });\n\n const classNames = segmentedControl();\n\n return (\n <button\n ref={ref}\n className={clsx(classNames.segment, className)}\n {...rootProps}\n {...otherProps}\n >\n <div className={classNames.segmentLabel} {...labelProps} tabIndex={-1}>\n {children}\n </div>\n <div aria-hidden className={classNames.segmentLabelPlaceholder}>\n {children}\n </div>\n </button>\n );\n});\n\nSegmentedControlOption.displayName = \"SegmentedControlOption\";\n"
}
]
}
8 changes: 4 additions & 4 deletions docs/registry/ui/segmented-control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const SegmentedControl = React.forwardRef<
<TabsContext.Provider value={{ api }}>{children}</TabsContext.Provider>
<div
aria-hidden
className={classNames.indicator}
className={classNames.selectedIndicator}
{...tabIndicatorProps}
style={{ left, width }}
/>
Expand Down Expand Up @@ -98,14 +98,14 @@ export const SegmentedControlOption = React.forwardRef<
return (
<button
ref={ref}
className={clsx(classNames.option, className)}
className={clsx(classNames.segment, className)}
{...rootProps}
{...otherProps}
>
<div className={classNames.optionLabel} {...labelProps} tabIndex={-1}>
<div className={classNames.segmentLabel} {...labelProps} tabIndex={-1}>
{children}
</div>
<div aria-hidden className={classNames.optionLabelPlaceholder}>
<div aria-hidden className={classNames.segmentLabelPlaceholder}>
{children}
</div>
</button>
Expand Down
47 changes: 24 additions & 23 deletions packages/recipe-generator/preset/src/segmented-control.recipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ import { disabled, active, pseudo, selected } from "./pseudo";

const segmentedControl = defineRecipe({
name: "segmentedControl",
slots: ["root", "option", "optionLabel", "optionLabelPlaceholder", "indicator"],
slots: ["root", "segment", "segmentLabel", "segmentLabelPlaceholder", "selectedIndicator"],
base: {
root: {
display: "grid",

height: vars.base.enabled.root.height,
minWidth: "fit-content",
maxWidth: "100%",

Expand All @@ -24,84 +23,86 @@ const segmentedControl = defineRecipe({
// XXX: css reset 생기면 제거
boxSizing: "border-box",
},
option: {
segment: {
// XXX: css reset 생기면 제거
border: "none",
padding: 0,
backgroundColor: "transparent",
font: "inherit",
cursor: "pointer",

position: "relative",

minWidth: vars.base.enabled.option.minWidth,
minWidth: vars.base.enabled.segment.minWidth,
height: vars.base.enabled.segment.height,

zIndex: 10,

borderRadius: vars.base.enabled.option.cornerRadius,
borderRadius: vars.base.enabled.segment.cornerRadius,

overflow: "hidden",

userSelect: "none",

lineHeight: vars.base.enabled.option.lineHeight,
lineHeight: vars.base.enabled.segment.lineHeight,

[pseudo(active)]: {
backgroundColor: vars.base.pressed.option.color,
backgroundColor: vars.base.pressed.segment.color,
},

[pseudo(selected, active)]: {
backgroundColor: vars.base.selectedPressed.option.color,
backgroundColor: vars.base.selectedPressed.segment.color,
},
},
optionLabel: {
segmentLabel: {
position: "absolute",
insetInline: 0,
transform: "translateY(-50%)",
insetBlockStart: "50%",

paddingInline: `calc(${vars.base.enabled.option.paddingX} - 1px)`,
paddingInline: `calc(${vars.base.enabled.segment.paddingX} - 1px)`,

textAlign: "center",
fontWeight: vars.base.enabled.option.fontWeight,
fontSize: vars.base.enabled.option.fontSize,
fontWeight: vars.base.enabled.segment.fontWeight,
fontSize: vars.base.enabled.segment.fontSize,

whiteSpace: "nowrap",

textOverflow: "ellipsis",
overflow: "hidden",

color: vars.base.enabled.option.color,
color: vars.base.enabled.segment.color,

[pseudo(selected)]: {
color: vars.base.selected.option.color,
color: vars.base.selected.segment.color,

fontWeight: vars.base.selected.option.fontWeight,
fontWeight: vars.base.selected.segment.fontWeight,
},

[pseudo(disabled)]: {
color: vars.base.disabled.option.color,
color: vars.base.disabled.segment.color,
},
},
optionLabelPlaceholder: {
paddingInline: vars.base.enabled.option.paddingX,
segmentLabelPlaceholder: {
paddingInline: vars.base.enabled.segment.paddingX,

textAlign: "center",
fontWeight: vars.base.selected.option.fontWeight,
fontSize: vars.base.enabled.option.fontSize,
fontWeight: vars.base.selected.segment.fontWeight,
fontSize: vars.base.enabled.segment.fontSize,

textOverflow: "ellipsis",
overflow: "hidden",
whiteSpace: "nowrap",

opacity: 0,
},
indicator: {
selectedIndicator: {
position: "absolute",
insetBlock: vars.base.enabled.root.padding,

borderRadius: vars.base.enabled.indicator.cornerRadius,
borderRadius: vars.base.enabled.selectedIndicator.cornerRadius,

backgroundColor: vars.base.enabled.indicator.color,
backgroundColor: vars.base.enabled.selectedIndicator.color,

// XXX: token으로 교체
boxShadow: "0 1px 6px rgba(0, 0, 0, 5%)",
Expand Down
2 changes: 1 addition & 1 deletion packages/recipe/lib/segmentedControl.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type SegmentedControlVariantMap = {

export type SegmentedControlVariantProps = Partial<SegmentedControlVariant>;

export type SegmentedControlSlotName = "root" | "option" | "optionLabel" | "optionLabelPlaceholder" | "indicator";
export type SegmentedControlSlotName = "root" | "segment" | "segmentLabel" | "segmentLabelPlaceholder" | "selectedIndicator";

export const segmentedControlVariantMap: SegmentedControlVariantMap;

Expand Down
16 changes: 8 additions & 8 deletions packages/recipe/lib/segmentedControl.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@ const segmentedControlSlotNames = [
"segmentedControl__root"
],
[
"option",
"segmentedControl__option"
"segment",
"segmentedControl__segment"
],
[
"optionLabel",
"segmentedControl__optionLabel"
"segmentLabel",
"segmentedControl__segmentLabel"
],
[
"optionLabelPlaceholder",
"segmentedControl__optionLabelPlaceholder"
"segmentLabelPlaceholder",
"segmentedControl__segmentLabelPlaceholder"
],
[
"indicator",
"segmentedControl__indicator"
"selectedIndicator",
"segmentedControl__selectedIndicator"
]
];

Expand Down
14 changes: 7 additions & 7 deletions packages/rootage/artifacts/components/segmented-control.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,31 @@ data:
base:
enabled:
root:
height: $unit.x10
padding: $unit.x1
cornerRadius: $radius.full
color: $color.bg.neutral-weak
option:
segment:
height: $unit.x8
cornerRadius: $radius.full
paddingX: $unit.x4
minWidth: 86px
fontSize: $font-size.t5
lineHeight: $line-height.t5
fontWeight: $font-weight.medium
color: $color.fg.neutral-muted
indicator:
selectedIndicator:
cornerRadius: $radius.full
color: $color.bg.layer-default
pressed:
option:
segment:
color: $color.bg.neutral-weak-pressed
selected:
option:
segment:
color: $color.fg.neutral
fontWeight: $font-weight.bold
selected,pressed:
option:
segment:
color: $color.bg.layer-default-pressed
disabled:
option:
segment:
color: $color.fg.disabled
19 changes: 10 additions & 9 deletions packages/stylesheet/segmentedControl.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
.segmentedControl__root {
display: grid;
height: var(--seed-v3-unit-x10);
min-width: fit-content;
max-width: 100%;
padding: var(--seed-v3-unit-x1);
Expand All @@ -9,26 +8,28 @@
background-color: var(--seed-v3-color-bg-neutral-weak);
box-sizing: border-box;
}
.segmentedControl__option {
.segmentedControl__segment {
border: none;
padding: 0;
background-color: transparent;
font: inherit;
cursor: pointer;
position: relative;
min-width: 86px;
height: var(--seed-v3-unit-x8);
z-index: 10;
border-radius: var(--seed-v3-radius-full);
overflow: hidden;
user-select: none;
line-height: var(--seed-v3-line-height-t5);
}
.segmentedControl__option:is(:active, [data-active]) {
.segmentedControl__segment:is(:active, [data-active]) {
background-color: var(--seed-v3-color-bg-neutral-weak-pressed);
}
.segmentedControl__option:is(:selected, [data-selected]):is(:active, [data-active]) {
.segmentedControl__segment:is(:selected, [data-selected]):is(:active, [data-active]) {
background-color: var(--seed-v3-color-bg-layer-default-pressed);
}
.segmentedControl__optionLabel {
.segmentedControl__segmentLabel {
position: absolute;
inset-inline: 0;
transform: translateY(-50%);
Expand All @@ -42,14 +43,14 @@
overflow: hidden;
color: var(--seed-v3-color-fg-neutral-muted);
}
.segmentedControl__optionLabel:is(:selected, [data-selected]) {
.segmentedControl__segmentLabel:is(:selected, [data-selected]) {
color: var(--seed-v3-color-fg-neutral);
font-weight: var(--seed-v3-font-weight-bold);
}
.segmentedControl__optionLabel:is(:disabled, [disabled], [data-disabled]) {
.segmentedControl__segmentLabel:is(:disabled, [disabled], [data-disabled]) {
color: var(--seed-v3-color-fg-disabled);
}
.segmentedControl__optionLabelPlaceholder {
.segmentedControl__segmentLabelPlaceholder {
padding-inline: var(--seed-v3-unit-x4);
text-align: center;
font-weight: var(--seed-v3-font-weight-bold);
Expand All @@ -59,7 +60,7 @@
white-space: nowrap;
opacity: 0;
}
.segmentedControl__indicator {
.segmentedControl__selectedIndicator {
position: absolute;
inset-block: var(--seed-v3-unit-x1);
border-radius: var(--seed-v3-radius-full);
Expand Down
14 changes: 7 additions & 7 deletions packages/vars/src/component/segmented-control.vars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ export const vars = {
"base": {
"enabled": {
"root": {
"height": "var(--seed-v3-unit-x10)",
"padding": "var(--seed-v3-unit-x1)",
"cornerRadius": "var(--seed-v3-radius-full)",
"color": "var(--seed-v3-color-bg-neutral-weak)"
},
"option": {
"segment": {
"height": "var(--seed-v3-unit-x8)",
"cornerRadius": "var(--seed-v3-radius-full)",
"paddingX": "var(--seed-v3-unit-x4)",
"minWidth": "86px",
Expand All @@ -16,29 +16,29 @@ export const vars = {
"fontWeight": "var(--seed-v3-font-weight-medium)",
"color": "var(--seed-v3-color-fg-neutral-muted)"
},
"indicator": {
"selectedIndicator": {
"cornerRadius": "var(--seed-v3-radius-full)",
"color": "var(--seed-v3-color-bg-layer-default)"
}
},
"pressed": {
"option": {
"segment": {
"color": "var(--seed-v3-color-bg-neutral-weak-pressed)"
}
},
"selected": {
"option": {
"segment": {
"color": "var(--seed-v3-color-fg-neutral)",
"fontWeight": "var(--seed-v3-font-weight-bold)"
}
},
"selectedPressed": {
"option": {
"segment": {
"color": "var(--seed-v3-color-bg-layer-default-pressed)"
}
},
"disabled": {
"option": {
"segment": {
"color": "var(--seed-v3-color-fg-disabled)"
}
}
Expand Down

0 comments on commit 16663bd

Please sign in to comment.