[WEB-5602] feat: new design system (#8220)
* chore: init tailwind v4 * chore: update all configs * chore: add source to parse monorepo packages * chore: combine all css files * feat: added extended colors * chore: update typography * chore: update extended color var names * refactor: remove initial spacing variable and update dark mode selector * chore: update css files * chore: update animations * chore: remove spacing tokens * fix: external css files * chore: update tailwind-merge version * chore: update font family * chore: added brief agents.md and story for new design system * chore: enhance design system documentation with rare exceptions for visual separation * chore: add fontsource package for typography * chore: material symbols font added * chore: update shadow default * chore: add stroke and outline theme vars * chore: update ring and fill colors * chore: overwrite tailwind typography tokens * chore: add high contrast mode tokens * chore: update scrollbar colors * chore: backward compatibility for buttons and placeholders * chore: add priority colors * chore: update urgent priority color * chore: update plan colors * chore: add missing utility class * chore: update height and padding classes * chore: update label colors * chore: add missing utlity * chore: add typography plugin to space app * chore: replace existing classNames with new design system tokens #8244 (#8278) * chore: update border colors * chore: update all borders * chore: update text colors * chore: update css variables * chore: update font sizes and weights * chore: update bg colors * chore: sync changes * fix: uncomment spacing-1200 variable in variables.css * chore: update primary colors * refactor: updated border to border-subtle * refactor: update various components and improve UI consistency across the application * updated classnames * updated classnames * refactor: update color-related class names to use new design system variables for consistency * chore: default automations * chore: update text sizes * chore: home and power k * chore: home and power k * chore: replace ui package button components * chore: update text sizes * chore: updated issue identifier (#8275) * refactor: top navigation and sidebar design token (#8276) * chore: update all button components (#8277) * chore: new button component * chore: update existing buttons * chore: overwrite tailwind typography tokens * fix: twMerge config + fixed cn instances * refactor: toast design token updated (#8279) * chore: update existing buttons * chore: tooltip design token updatged (#8280) * chore: moved cn utility to propel (#8281) * chore: update space app UI (#8285) * chore; update space app filters component * fix: button whitespace wrap * chore: space app votes * chore: update dropdown components * refactor: auth, onboarding, sidebar, and common component design token migration (#8291) * chore: checkbox component design token updated * chore: indicator and oauth component design token updated * chore: sidebar design token updated * chore: auth and onboarding design token updated * chore: update divider color * style: update background colors and hover effects across list components * fix: tailwind merge * refactor: toggle switch design token migration and header utility classname added (#8295) * chore: toggle component design token updated * chore: h-header utility class added * chore: updated color tokens for work item detail page (#8296) * chore: update react-day-picker UI * refactor: update button sizes and styles in filters components * refactor: breadcrumbs design token updated (#8297) * chore: update priority icon colors * refactor: updated layout variables * chore: update plan card primary CTA * Chore update editor design system (#8299) * refactor: update styles for callout, color selector, logo selector, and image uploader * refactor:fix image * chore: update settings UI * chore: updated notifications color and size tokens (#8302) * chore: update sm button border radius * fix: logo renderer * chore: icon button component * chore: remove deprecated classes * chore: remove deprecated classes * chore: update editor list spacing * fix: icon button size * chore: improvements (#8309) * chore: update cycles and modules pages * refactor: update background styles across various components to use new design system colors * fix: button type errors * chore: update modals design system (#8310) * refactor: callout bg * refactor: code bg * refactor: modal size and variant --------- Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com> * chore: update next-themes * design: update billing and plans component styles and remove unused utility functions (#8313) * refactor: empty state design token migration and improvements (#8315) * fix: profile page * refactor: tabs design token updated (#8316) * chore: updated buttons and tokens for work items (#8317) * fix: adjust trial button spacing in checkout modal * chore: update add button hover state * fix: type error (#8318) * fix: type error * chore: code refactor * refactor: update button sizes and background styles in rich filters components * refactor: update editor bg * refactor: enhance Gantt chart sidebar functionality and styling - Removed unused prop from . - Updated to include new props for better block management and scrolling behavior. - Improved auto-scroll functionality for Gantt chart items. - Adjusted styles in component for consistent design. * regression: gantt design * chore: new badge component * fix: favorite star * chore: update backgroung, typography and button sizes across workspace settings general and members pages * fix: header button sizes * fix: emoji icon logo (#8323) * more fixes * chore: update settings sidebar * refactor: avatar component * chore: updated work item detail sidebar (#8327) * refactor: update link preview * fix: work item property dropdowns * fix: dropdown buttons border radius * chore: update power k translation * chore: updated profile activity design (#8328) * chore: update settings pages * chore: update work item sidebar alignments (#8330) * refactor: admin design system * chore: update page header --------- Co-authored-by: Jayash Tripathy <76092296+JayashTripathy@users.noreply.github.com> Co-authored-by: VipinDevelops <vipinchaudhary1809@gmail.com> Co-authored-by: Vamsi Krishna <46787868+vamsikrishnamathala@users.noreply.github.com> Co-authored-by: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Co-authored-by: gakshita <akshitagoyal1516@gmail.com> Co-authored-by: Palanikannan M <akashmalinimurugu@gmail.com> Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> Co-authored-by: b-saikrishnakanth <bsaikrishnakanth97@gmail.com> Co-authored-by: M. Palanikannan <73993394+Palanikannan1437@users.noreply.github.com> * fix: formatting * reexport types * fix: lint error --------- Co-authored-by: Jayash Tripathy <76092296+JayashTripathy@users.noreply.github.com> Co-authored-by: VipinDevelops <vipinchaudhary1809@gmail.com> Co-authored-by: Vamsi Krishna <46787868+vamsikrishnamathala@users.noreply.github.com> Co-authored-by: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Co-authored-by: gakshita <akshitagoyal1516@gmail.com> Co-authored-by: Palanikannan M <akashmalinimurugu@gmail.com> Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> Co-authored-by: b-saikrishnakanth <bsaikrishnakanth97@gmail.com> Co-authored-by: M. Palanikannan <73993394+Palanikannan1437@users.noreply.github.com>
This commit is contained in:
parent
d86418aad8
commit
22339b9786
1342 changed files with 14227 additions and 15119 deletions
|
|
@ -1,8 +1,8 @@
|
|||
import type { TChartColorScheme } from "@plane/types";
|
||||
import { ChartXAxisProperty } from "@plane/types";
|
||||
|
||||
export const LABEL_CLASSNAME = "uppercase text-custom-text-300/60 text-sm tracking-wide";
|
||||
export const AXIS_LABEL_CLASSNAME = "uppercase text-custom-text-300/60 text-sm tracking-wide";
|
||||
export const LABEL_CLASSNAME = "uppercase text-tertiary/60 text-13 tracking-wide";
|
||||
export const AXIS_LABEL_CLASSNAME = "uppercase text-tertiary/60 text-13 tracking-wide";
|
||||
|
||||
export enum ChartXAxisDateGrouping {
|
||||
DAY = "DAY",
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export const CYCLE_STATUS: {
|
|||
value: "draft",
|
||||
i18n_title: "project_cycles.status.draft",
|
||||
color: "#525252",
|
||||
textColor: "text-custom-text-300",
|
||||
bgColor: "bg-custom-background-90",
|
||||
textColor: "text-tertiary",
|
||||
bgColor: "bg-surface-2",
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
export const CHARTS_THEME = {
|
||||
background: "transparent",
|
||||
text: {
|
||||
color: "rgb(var(--color-text-200))",
|
||||
color: "var(--text-color-secondary)",
|
||||
},
|
||||
axis: {
|
||||
domain: {
|
||||
|
|
@ -14,14 +14,14 @@ export const CHARTS_THEME = {
|
|||
tooltip: {
|
||||
container: {
|
||||
background: "rgb(var(--color-background-80))",
|
||||
color: "rgb(var(--color-text-200))",
|
||||
color: "var(--text-color-secondary)",
|
||||
fontSize: "0.8rem",
|
||||
border: "1px solid rgb(var(--color-border-300))",
|
||||
border: "1px solid var(--border-color-strong)",
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
line: {
|
||||
stroke: "rgb(var(--color-border-100))",
|
||||
stroke: "var(--border-color-subtle)",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -61,31 +61,31 @@ export const ISSUE_PRIORITY_FILTERS: TIssueFilterPriorityObject[] = [
|
|||
{
|
||||
key: "urgent",
|
||||
titleTranslationKey: "issue.priority.urgent",
|
||||
className: "bg-red-500 border-red-500 text-white",
|
||||
className: "bg-layer-2 text-priority-urgent border-strong",
|
||||
icon: "error",
|
||||
},
|
||||
{
|
||||
key: "high",
|
||||
titleTranslationKey: "issue.priority.high",
|
||||
className: "text-orange-500 border-custom-border-300",
|
||||
className: "bg-layer-2 text-priority-high border-strong",
|
||||
icon: "signal_cellular_alt",
|
||||
},
|
||||
{
|
||||
key: "medium",
|
||||
titleTranslationKey: "issue.priority.medium",
|
||||
className: "text-yellow-500 border-custom-border-300",
|
||||
className: "bg-layer-2 text-priority-medium border-strong",
|
||||
icon: "signal_cellular_alt_2_bar",
|
||||
},
|
||||
{
|
||||
key: "low",
|
||||
titleTranslationKey: "issue.priority.low",
|
||||
className: "text-green-500 border-custom-border-300",
|
||||
className: "bg-layer-2 text-priority-low border-strong",
|
||||
icon: "signal_cellular_alt_1_bar",
|
||||
},
|
||||
{
|
||||
key: "none",
|
||||
titleTranslationKey: "common.none",
|
||||
className: "text-gray-500 border-custom-border-300",
|
||||
className: "bg-layer-2 text-priority-none border-strong",
|
||||
icon: "block",
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ export const MODULE_STATUS: {
|
|||
i18n_label: "project_modules.status.backlog",
|
||||
value: "backlog",
|
||||
color: MODULE_STATUS_COLORS.backlog,
|
||||
textColor: "text-custom-text-400",
|
||||
bgColor: "bg-custom-background-80",
|
||||
textColor: "text-placeholder",
|
||||
bgColor: "bg-layer-1",
|
||||
},
|
||||
{
|
||||
i18n_label: "project_modules.status.planned",
|
||||
|
|
@ -44,8 +44,8 @@ export const MODULE_STATUS: {
|
|||
i18n_label: "project_modules.status.paused",
|
||||
value: "paused",
|
||||
color: MODULE_STATUS_COLORS.paused,
|
||||
textColor: "text-custom-text-300",
|
||||
bgColor: "bg-custom-background-90",
|
||||
textColor: "text-tertiary",
|
||||
bgColor: "bg-surface-2",
|
||||
},
|
||||
{
|
||||
i18n_label: "project_modules.status.completed",
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
// If you want to use other PostCSS plugins, see the following:
|
||||
// https://tailwindcss.com/docs/using-with-preprocessors
|
||||
import postcssConfig from "@plane/tailwind-config/postcss.config.js";
|
||||
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
export default postcssConfig;
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@ type InputViewProps = {
|
|||
function InputView({ label, value, placeholder, onChange, autoFocus }: InputViewProps) {
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="inline-block font-semibold text-xs text-custom-text-400">{label}</label>
|
||||
<label className="inline-block font-semibold text-11 text-placeholder">{label}</label>
|
||||
<input
|
||||
placeholder={placeholder}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="w-[280px] outline-none bg-custom-background-90 text-custom-text-900 text-sm border border-custom-border-300 rounded-md p-2"
|
||||
className="w-[280px] outline-none bg-layer-1 text-primary text-13 border border-strong rounded-md p-2"
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
autoFocus={autoFocus}
|
||||
|
|
@ -132,7 +132,7 @@ export function LinkEditView({ viewProps }: LinkEditViewProps) {
|
|||
return (
|
||||
<div
|
||||
onKeyDown={handleKeyDown}
|
||||
className="shadow-md rounded p-2 flex flex-col gap-3 bg-custom-background-90 border-custom-border-100 border-2 animate-in fade-in translate-y-1"
|
||||
className="shadow-md rounded-sm p-2 flex flex-col gap-3 bg-layer-1 border-subtle border-2 animate-in fade-in translate-y-1"
|
||||
style={{
|
||||
transition: "all 0.1s cubic-bezier(.55, .085, .68, .53)",
|
||||
}}
|
||||
|
|
@ -140,10 +140,10 @@ export function LinkEditView({ viewProps }: LinkEditViewProps) {
|
|||
>
|
||||
<InputView label="URL" placeholder="Enter or paste URL" value={localUrl} onChange={setLocalUrl} autoFocus />
|
||||
<InputView label="Text" placeholder="Enter Text to display" value={localText} onChange={handleTextChange} />
|
||||
<div className="mb-1 bg-custom-border-300 h-[1px] w-full gap-2" />
|
||||
<div className="flex text-sm text-custom-text-800 gap-2 items-center">
|
||||
<div className="mb-1 bg-strong h-[1px] w-full gap-2" />
|
||||
<div className="flex text-13 text-secondary gap-2 items-center">
|
||||
<Link2Off size={14} className="inline-block" />
|
||||
<button onClick={removeLink} className="cursor-pointer hover:text-custom-text-400 transition-colors">
|
||||
<button onClick={removeLink} className="cursor-pointer hover:text-placeholder transition-colors">
|
||||
Remove Link
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -28,22 +28,22 @@ export function LinkPreview({
|
|||
transition: "all 0.2s cubic-bezier(.55, .085, .68, .53)",
|
||||
}}
|
||||
>
|
||||
<div className="shadow-md items-center rounded p-2 flex gap-3 bg-custom-background-90 border-custom-border-100 border-2 text-custom-text-300 text-xs">
|
||||
<div className="shadow-md items-center rounded-sm p-2 flex gap-3 bg-layer-1 border-subtle border-2 text-tertiary text-11">
|
||||
<GlobeIcon size={14} className="inline-block" />
|
||||
<p>{url?.length > 40 ? url.slice(0, 40) + "..." : url}</p>
|
||||
<div className="flex gap-2">
|
||||
<button onClick={copyLinkToClipboard} className="cursor-pointer hover:text-custom-text-100 transition-colors">
|
||||
<button onClick={copyLinkToClipboard} className="cursor-pointer hover:text-primary transition-colors">
|
||||
<Copy size={14} className="inline-block" />
|
||||
</button>
|
||||
{editor.isEditable && (
|
||||
<>
|
||||
<button
|
||||
onClick={() => switchView("LinkEditView")}
|
||||
className="cursor-pointer hover:text-custom-text-100 transition-colors"
|
||||
className="cursor-pointer hover:text-primary transition-colors"
|
||||
>
|
||||
<PencilIcon size={14} className="inline-block" />
|
||||
</button>
|
||||
<button onClick={removeLink} className="cursor-pointer hover:text-custom-text-100 transition-colors">
|
||||
<button onClick={removeLink} className="cursor-pointer hover:text-primary transition-colors">
|
||||
<Link2Off size={14} className="inline-block" />
|
||||
</button>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ export function BlockMenu(props: Props) {
|
|||
zIndex: 100,
|
||||
}}
|
||||
className={cn(
|
||||
"max-h-60 min-w-[7rem] overflow-y-scroll rounded-lg border border-custom-border-200 bg-custom-background-100 p-1.5 shadow-custom-shadow-rg",
|
||||
"max-h-60 min-w-[7rem] overflow-y-scroll rounded-lg border border-subtle bg-surface-1 p-1.5 shadow-custom-shadow-rg",
|
||||
"transition-all duration-300 transform origin-top-right",
|
||||
isAnimatedIn ? "opacity-100 scale-100" : "opacity-0 scale-75"
|
||||
)}
|
||||
|
|
@ -229,7 +229,7 @@ export function BlockMenu(props: Props) {
|
|||
<button
|
||||
key={item.key}
|
||||
type="button"
|
||||
className="flex w-full items-center gap-1.5 truncate rounded px-1 py-1.5 text-xs text-custom-text-200 hover:bg-custom-background-90"
|
||||
className="flex w-full items-center gap-1.5 truncate rounded-sm px-1 py-1.5 text-11 text-secondary hover:bg-layer-1"
|
||||
onClick={(e) => {
|
||||
item.onClick(e);
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -69,9 +69,9 @@ export function TextAlignmentSelector(props: Props) {
|
|||
item.command();
|
||||
}}
|
||||
className={cn(
|
||||
"size-7 grid place-items-center rounded text-custom-text-300 hover:bg-custom-background-80 active:bg-custom-background-80 transition-colors",
|
||||
"size-7 grid place-items-center rounded-sm text-tertiary hover:bg-layer-1 active:bg-layer-1 transition-colors",
|
||||
{
|
||||
"bg-custom-background-80 text-custom-text-100": item.isActive(),
|
||||
"bg-layer-1 text-primary": item.isActive(),
|
||||
}
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -30,25 +30,22 @@ export function BubbleMenuColorSelector(props: Props) {
|
|||
classNames={{
|
||||
buttonContainer: "h-full",
|
||||
button:
|
||||
"flex items-center gap-1 h-full whitespace-nowrap px-3 text-sm font-medium text-custom-text-300 hover:bg-custom-background-80 active:bg-custom-background-80 rounded transition-colors",
|
||||
"flex items-center gap-1 h-full whitespace-nowrap px-3 text-13 font-medium text-tertiary hover:bg-layer-1 active:bg-layer-1 rounded-sm transition-colors",
|
||||
}}
|
||||
menuButton={
|
||||
<>
|
||||
<span>Color</span>
|
||||
<span
|
||||
className={cn(
|
||||
"flex-shrink-0 size-6 grid place-items-center rounded border-[0.5px] border-custom-border-300",
|
||||
{
|
||||
"bg-custom-background-100": !activeBackgroundColor,
|
||||
}
|
||||
)}
|
||||
className={cn("flex-shrink-0 size-6 grid place-items-center rounded-sm border-[0.5px] border-strong", {
|
||||
"bg-surface-1": !activeBackgroundColor,
|
||||
})}
|
||||
style={{
|
||||
backgroundColor: activeBackgroundColor ? activeBackgroundColor.backgroundColor : "transparent",
|
||||
}}
|
||||
>
|
||||
<ALargeSmall
|
||||
className={cn("size-3.5", {
|
||||
"text-custom-text-100": !activeTextColor,
|
||||
"text-primary": !activeTextColor,
|
||||
})}
|
||||
style={{
|
||||
color: activeTextColor ? activeTextColor.textColor : "inherit",
|
||||
|
|
@ -61,15 +58,15 @@ export function BubbleMenuColorSelector(props: Props) {
|
|||
getFloatingProps={getFloatingProps}
|
||||
getReferenceProps={getReferenceProps}
|
||||
>
|
||||
<section className="mt-1 rounded-md border-[0.5px] border-custom-border-300 bg-custom-background-100 p-2 space-y-2 shadow-custom-shadow-rg">
|
||||
<section className="mt-1 rounded-md border-[0.5px] border-strong bg-surface-1 p-2 space-y-2 shadow-custom-shadow-rg">
|
||||
<div className="space-y-1.5">
|
||||
<p className="text-xs text-custom-text-300 font-semibold">Text colors</p>
|
||||
<p className="text-11 text-tertiary font-semibold">Text colors</p>
|
||||
<div className="flex items-center gap-2">
|
||||
{COLORS_LIST.map((color) => (
|
||||
<button
|
||||
key={color.key}
|
||||
type="button"
|
||||
className="flex-shrink-0 size-6 rounded border-[0.5px] border-custom-border-400 hover:opacity-60 transition-opacity"
|
||||
className="flex-shrink-0 size-6 rounded-sm border-[0.5px] border-strong-1 hover:opacity-60 transition-opacity"
|
||||
style={{
|
||||
backgroundColor: color.textColor,
|
||||
}}
|
||||
|
|
@ -78,7 +75,7 @@ export function BubbleMenuColorSelector(props: Props) {
|
|||
))}
|
||||
<button
|
||||
type="button"
|
||||
className="flex-shrink-0 size-6 grid place-items-center rounded text-custom-text-300 border-[0.5px] border-custom-border-400 hover:bg-custom-background-80 transition-colors"
|
||||
className="flex-shrink-0 size-6 grid place-items-center rounded-sm text-tertiary border-[0.5px] border-strong-1 hover:bg-layer-1 transition-colors"
|
||||
onClick={() => TextColorItem(editor).command({ color: undefined })}
|
||||
>
|
||||
<Ban className="size-4" />
|
||||
|
|
@ -86,13 +83,13 @@ export function BubbleMenuColorSelector(props: Props) {
|
|||
</div>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<p className="text-xs text-custom-text-300 font-semibold">Background colors</p>
|
||||
<p className="text-11 text-tertiary font-semibold">Background colors</p>
|
||||
<div className="flex items-center gap-2">
|
||||
{COLORS_LIST.map((color) => (
|
||||
<button
|
||||
key={color.key}
|
||||
type="button"
|
||||
className="flex-shrink-0 size-6 rounded border-[0.5px] border-custom-border-400 hover:opacity-60 transition-opacity"
|
||||
className="flex-shrink-0 size-6 rounded-sm border-[0.5px] border-strong-1 hover:opacity-60 transition-opacity"
|
||||
style={{
|
||||
backgroundColor: color.backgroundColor,
|
||||
}}
|
||||
|
|
@ -101,7 +98,7 @@ export function BubbleMenuColorSelector(props: Props) {
|
|||
))}
|
||||
<button
|
||||
type="button"
|
||||
className="flex-shrink-0 size-6 grid place-items-center rounded text-custom-text-300 border-[0.5px] border-custom-border-400 hover:bg-custom-background-80 transition-colors"
|
||||
className="flex-shrink-0 size-6 grid place-items-center rounded-sm text-tertiary border-[0.5px] border-strong-1 hover:bg-layer-1 transition-colors"
|
||||
onClick={() => BackgroundColorItem(editor).command({ color: undefined })}
|
||||
>
|
||||
<Ban className="size-4" />
|
||||
|
|
|
|||
|
|
@ -46,10 +46,10 @@ export function BubbleMenuLinkSelector(props: Props) {
|
|||
classNames={{
|
||||
buttonContainer: "h-full",
|
||||
button: cn(
|
||||
"h-full flex items-center gap-1 px-3 text-sm font-medium text-custom-text-300 hover:bg-custom-background-80 active:bg-custom-background-80 rounded whitespace-nowrap transition-colors",
|
||||
"h-full flex items-center gap-1 px-3 text-13 font-medium text-tertiary hover:bg-layer-1 active:bg-layer-1 rounded-sm whitespace-nowrap transition-colors",
|
||||
{
|
||||
"bg-custom-background-80": context.open,
|
||||
"text-custom-text-100": editor.isActive(CORE_EXTENSIONS.CUSTOM_LINK),
|
||||
"bg-layer-1": context.open,
|
||||
"text-primary": editor.isActive(CORE_EXTENSIONS.CUSTOM_LINK),
|
||||
}
|
||||
),
|
||||
}}
|
||||
|
|
@ -63,9 +63,9 @@ export function BubbleMenuLinkSelector(props: Props) {
|
|||
}
|
||||
options={options}
|
||||
>
|
||||
<div className="w-60 mt-1 rounded-md bg-custom-background-100 shadow-custom-shadow-rg">
|
||||
<div className="w-60 mt-1 rounded-md bg-surface-1 shadow-custom-shadow-rg">
|
||||
<div
|
||||
className={cn("flex rounded border-[0.5px] border-custom-border-300 transition-colors", {
|
||||
className={cn("flex rounded-sm border-[0.5px] border-strong transition-colors", {
|
||||
"border-red-500": error,
|
||||
})}
|
||||
>
|
||||
|
|
@ -74,7 +74,7 @@ export function BubbleMenuLinkSelector(props: Props) {
|
|||
type="url"
|
||||
placeholder="Enter or paste a link"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="flex-1 border-r-[0.5px] border-custom-border-300 bg-custom-background-100 py-2 px-1.5 text-xs outline-none placeholder:text-custom-text-400 rounded"
|
||||
className="flex-1 border-r-[0.5px] border-strong bg-surface-1 py-2 px-1.5 text-11 outline-none placeholder:text-placeholder rounded-sm"
|
||||
defaultValue={editor.getAttributes("link").href || ""}
|
||||
onKeyDown={(e) => {
|
||||
setError(false);
|
||||
|
|
@ -89,7 +89,7 @@ export function BubbleMenuLinkSelector(props: Props) {
|
|||
{editor.getAttributes("link").href ? (
|
||||
<button
|
||||
type="button"
|
||||
className="grid place-items-center rounded-sm p-1 text-red-500 hover:bg-red-500/20 transition-all"
|
||||
className="grid place-items-center rounded-xs p-1 text-red-500 hover:bg-red-500/20 transition-all"
|
||||
onClick={(e) => {
|
||||
unsetLinkEditor(editor);
|
||||
e.stopPropagation();
|
||||
|
|
@ -101,7 +101,7 @@ export function BubbleMenuLinkSelector(props: Props) {
|
|||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className="h-full aspect-square grid place-items-center p-1 rounded-sm text-custom-text-300 hover:bg-custom-background-80 transition-all"
|
||||
className="h-full aspect-square grid place-items-center p-1 rounded-xs text-tertiary hover:bg-layer-1 transition-all"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleLinkSubmit();
|
||||
|
|
@ -112,7 +112,7 @@ export function BubbleMenuLinkSelector(props: Props) {
|
|||
)}
|
||||
</div>
|
||||
{error && (
|
||||
<p className="text-xs text-red-500 my-1 px-2 pointer-events-none animate-in fade-in slide-in-from-top-0">
|
||||
<p className="text-11 text-red-500 my-1 px-2 pointer-events-none animate-in fade-in slide-in-from-top-0">
|
||||
Please enter a valid URL
|
||||
</p>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -59,9 +59,9 @@ export function BubbleMenuNodeSelector(props: Props) {
|
|||
classNames={{
|
||||
buttonContainer: "h-full",
|
||||
button: cn(
|
||||
"h-full flex items-center gap-1 px-3 text-sm font-medium text-custom-text-300 hover:bg-custom-background-80 active:bg-custom-background-80 rounded whitespace-nowrap transition-colors",
|
||||
"h-full flex items-center gap-1 px-3 text-13 font-medium text-tertiary hover:bg-layer-1 active:bg-layer-1 rounded-sm whitespace-nowrap transition-colors",
|
||||
{
|
||||
"bg-custom-background-80": context.open,
|
||||
"bg-layer-1": context.open,
|
||||
}
|
||||
),
|
||||
}}
|
||||
|
|
@ -75,7 +75,7 @@ export function BubbleMenuNodeSelector(props: Props) {
|
|||
getFloatingProps={getFloatingProps}
|
||||
getReferenceProps={getReferenceProps}
|
||||
>
|
||||
<section className="w-48 max-h-[90vh] mt-1 flex flex-col overflow-y-scroll rounded-md border-[0.5px] border-custom-border-300 bg-custom-background-100 px-2 py-2.5 shadow-custom-shadow-rg">
|
||||
<section className="w-48 max-h-[90vh] mt-1 flex flex-col overflow-y-scroll rounded-md border-[0.5px] border-strong bg-surface-1 px-2 py-2.5 shadow-custom-shadow-rg">
|
||||
{items.map((item) => (
|
||||
<button
|
||||
key={item.name}
|
||||
|
|
@ -86,9 +86,9 @@ export function BubbleMenuNodeSelector(props: Props) {
|
|||
e.stopPropagation();
|
||||
}}
|
||||
className={cn(
|
||||
"flex items-center justify-between rounded px-1 py-1.5 text-sm text-custom-text-200 hover:bg-custom-background-80",
|
||||
"flex items-center justify-between rounded-sm px-1 py-1.5 text-13 text-secondary hover:bg-layer-1",
|
||||
{
|
||||
"bg-custom-background-80": activeItem.name === item.name,
|
||||
"bg-layer-1": activeItem.name === item.name,
|
||||
}
|
||||
)}
|
||||
>
|
||||
|
|
@ -96,7 +96,7 @@ export function BubbleMenuNodeSelector(props: Props) {
|
|||
<item.icon className="size-3 flex-shrink-0" />
|
||||
<span>{item.name}</span>
|
||||
</div>
|
||||
{activeItem.name === item.name && <Check className="size-3 text-custom-text-300 flex-shrink-0" />}
|
||||
{activeItem.name === item.name && <Check className="size-3 text-tertiary flex-shrink-0" />}
|
||||
</button>
|
||||
))}
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ export function EditorBubbleMenu(props: Props) {
|
|||
{!isSelecting && (
|
||||
<div
|
||||
ref={menuRef}
|
||||
className="flex py-2 divide-x divide-custom-border-200 rounded-lg border border-custom-border-200 bg-custom-background-100 shadow-custom-shadow-rg overflow-x-scroll horizontal-scrollbar scrollbar-xs"
|
||||
className="flex py-2 divide-x divide-subtle-1 rounded-lg border border-subtle bg-surface-1 shadow-custom-shadow-rg overflow-x-scroll horizontal-scrollbar scrollbar-xs"
|
||||
>
|
||||
<div className="px-2">
|
||||
<BubbleMenuNodeSelector editor={editor} />
|
||||
|
|
@ -211,9 +211,9 @@ export function EditorBubbleMenu(props: Props) {
|
|||
e.stopPropagation();
|
||||
}}
|
||||
className={cn(
|
||||
"size-7 grid place-items-center rounded text-custom-text-300 hover:bg-custom-background-80 active:bg-custom-background-80 transition-colors",
|
||||
"size-7 grid place-items-center rounded-sm text-tertiary hover:bg-layer-1 active:bg-layer-1 transition-colors",
|
||||
{
|
||||
"bg-custom-background-80 text-custom-text-100": editorState[item.key],
|
||||
"bg-layer-1 text-primary": editorState[item.key],
|
||||
}
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { NodeViewProps } from "@tiptap/react";
|
||||
import { NodeViewContent, NodeViewWrapper } from "@tiptap/react";
|
||||
import React, { useState } from "react";
|
||||
import { useState } from "react";
|
||||
// constants
|
||||
import { COLORS_LIST } from "@/constants/common";
|
||||
// local components
|
||||
|
|
@ -29,7 +29,7 @@ export function CustomCalloutBlock(props: CustomCalloutNodeViewProps) {
|
|||
|
||||
return (
|
||||
<NodeViewWrapper
|
||||
className="editor-callout-component group/callout-node relative bg-custom-background-90 rounded-lg text-custom-text-100 p-4 my-2 flex items-start gap-4 transition-colors duration-500 break-words"
|
||||
className="editor-callout-component group/callout-node relative bg-layer-2 rounded-lg text-primary p-4 my-2 flex items-start gap-4 transition-colors duration-500 break-words"
|
||||
style={{
|
||||
backgroundColor: activeBackgroundColor,
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -36,24 +36,24 @@ export function CalloutBlockColorSelector(props: Props) {
|
|||
e.stopPropagation();
|
||||
}}
|
||||
className={cn(
|
||||
"flex items-center gap-1 h-full whitespace-nowrap py-1 px-2.5 text-sm font-medium text-custom-text-300 hover:bg-white/10 active:bg-custom-background-80 rounded transition-colors",
|
||||
"flex items-center gap-1 h-full whitespace-nowrap py-1 px-2.5 text-13 font-medium text-tertiary hover:bg-layer-1-hover active:bg-layer-1-active rounded-sm transition-colors",
|
||||
{
|
||||
"bg-white/10": isOpen,
|
||||
"bg-layer-1": isOpen,
|
||||
}
|
||||
)}
|
||||
disabled={disabled}
|
||||
>
|
||||
<span>Color</span>
|
||||
<span className="text-12">Color</span>
|
||||
<ChevronDownIcon className="flex-shrink-0 size-3" />
|
||||
</button>
|
||||
{isOpen && (
|
||||
<section className="absolute top-full right-0 z-10 mt-1 rounded-md border-[0.5px] border-custom-border-300 bg-custom-background-100 p-2 shadow-custom-shadow-rg animate-in fade-in slide-in-from-top-1">
|
||||
<section className="absolute top-full right-0 z-10 mt-1 rounded-md border-[0.5px] border-strong bg-surface-1 p-2 shadow-custom-shadow-rg animate-in fade-in slide-in-from-top-1">
|
||||
<div className="flex items-center gap-2">
|
||||
{COLORS_LIST.map((color) => (
|
||||
<button
|
||||
key={color.key}
|
||||
type="button"
|
||||
className="flex-shrink-0 size-6 rounded border-[0.5px] border-custom-border-400 hover:opacity-60 transition-opacity"
|
||||
className="flex-shrink-0 size-6 rounded-sm border-[0.5px] border-strong-1 hover:opacity-60 transition-opacity"
|
||||
style={{
|
||||
backgroundColor: color.backgroundColor,
|
||||
}}
|
||||
|
|
@ -62,7 +62,7 @@ export function CalloutBlockColorSelector(props: Props) {
|
|||
))}
|
||||
<button
|
||||
type="button"
|
||||
className="flex-shrink-0 size-6 grid place-items-center rounded text-custom-text-300 border-[0.5px] border-custom-border-400 hover:bg-custom-background-80 transition-colors"
|
||||
className="flex-shrink-0 size-6 grid place-items-center rounded-sm text-tertiary border-[0.5px] border-strong-1 hover:bg-layer-1-hover transition-colors"
|
||||
onClick={() => handleColorSelect(null)}
|
||||
>
|
||||
<Ban className="size-4" />
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ export function CalloutBlockLogoSelector(props: Props) {
|
|||
isOpen={isOpen}
|
||||
handleToggle={handleOpen}
|
||||
className="flex-shrink-0 grid place-items-center"
|
||||
buttonClassName={cn("flex-shrink-0 size-8 grid place-items-center rounded-lg", {
|
||||
"hover:bg-white/10": !disabled,
|
||||
buttonClassName={cn("flex-shrink-0 size-8 grid place-items-center rounded-lg text-primary", {
|
||||
"hover:bg-layer-1-hover": !disabled,
|
||||
})}
|
||||
label={<Logo logo={logoValue} size={18} type="lucide" />}
|
||||
onChange={(val) => {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export const CustomCodeInlineExtension = Mark.create<InlineCodeOptions>({
|
|||
return {
|
||||
HTMLAttributes: {
|
||||
class:
|
||||
"rounded bg-custom-background-80 px-[6px] py-[1.5px] font-mono font-medium text-orange-500 border-[0.5px] border-custom-border-200",
|
||||
"rounded-sm bg-layer-1 px-[6px] py-[1.5px] font-mono font-medium text-orange-500 border-[0.5px] border-subtle",
|
||||
spellcheck: "false",
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export function CodeBlockComponent({ node }: Props) {
|
|||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"group/button hidden group-hover/code:flex items-center justify-center absolute top-2 right-2 z-10 size-8 rounded-md bg-custom-background-80 border border-custom-border-200 transition duration-150 ease-in-out backdrop-blur-sm",
|
||||
"group/button hidden group-hover/code:flex items-center justify-center absolute top-2 right-2 z-10 size-8 rounded-md bg-layer-2 border border-subtle transition duration-150 ease-in-out backdrop-blur-sm",
|
||||
{
|
||||
"bg-green-500/30 hover:bg-green-500/30 active:bg-green-500/30": copied,
|
||||
}
|
||||
|
|
@ -48,12 +48,12 @@ export function CodeBlockComponent({ node }: Props) {
|
|||
{copied ? (
|
||||
<CheckIcon className="h-3 w-3 text-green-500" strokeWidth={3} />
|
||||
) : (
|
||||
<CopyIcon className="h-3 w-3 text-custom-text-300 group-hover/button:text-custom-text-100" />
|
||||
<CopyIcon className="h-3 w-3 text-tertiary group-hover/button:text-primary" />
|
||||
)}
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<pre className="bg-custom-background-90 text-custom-text-100 rounded-lg p-4 my-2">
|
||||
<pre className="bg-layer-2 text-primary rounded-lg p-4 my-2">
|
||||
<NodeViewContent as="code" className="whitespace-pre-wrap" />
|
||||
</pre>
|
||||
</NodeViewWrapper>
|
||||
|
|
|
|||
|
|
@ -234,10 +234,7 @@ export function CustomImageBlock(props: CustomImageBlockProps) {
|
|||
}}
|
||||
>
|
||||
{showImageLoader && (
|
||||
<div
|
||||
className="animate-pulse bg-custom-background-80 rounded-md"
|
||||
style={{ width: size.width, height: size.height }}
|
||||
/>
|
||||
<div className="animate-pulse bg-layer-2 rounded-md" style={{ width: size.width, height: size.height }} />
|
||||
)}
|
||||
<img
|
||||
ref={imageRef}
|
||||
|
|
@ -307,13 +304,13 @@ export function CustomImageBlock(props: CustomImageBlockProps) {
|
|||
/>
|
||||
)}
|
||||
{selected && displayedImageSrc === resolvedImageSrc && (
|
||||
<div className="absolute inset-0 size-full bg-custom-primary-500/30 pointer-events-none" />
|
||||
<div className="absolute inset-0 size-full bg-accent-primary/30 pointer-events-none" />
|
||||
)}
|
||||
{showImageResizer && (
|
||||
<>
|
||||
<div
|
||||
className={cn(
|
||||
"absolute inset-0 border-2 border-custom-primary-100 pointer-events-none rounded-md transition-opacity duration-100 ease-in-out",
|
||||
"absolute inset-0 border-2 border-accent-strong pointer-events-none rounded-md transition-opacity duration-100 ease-in-out",
|
||||
{
|
||||
"opacity-100": isResizing,
|
||||
"opacity-0 group-hover/image-component:opacity-100": !isResizing,
|
||||
|
|
@ -322,7 +319,7 @@ export function CustomImageBlock(props: CustomImageBlockProps) {
|
|||
/>
|
||||
<div
|
||||
className={cn(
|
||||
"absolute bottom-0 translate-y-1/2 size-4 rounded-full bg-custom-primary-100 border-2 border-white transition-opacity duration-100 ease-in-out",
|
||||
"absolute bottom-0 translate-y-1/2 size-4 rounded-full bg-accent-primary border-2 border-white transition-opacity duration-100 ease-in-out",
|
||||
{
|
||||
"opacity-100 pointer-events-auto": isResizing,
|
||||
"opacity-0 pointer-events-none group-hover/image-component:opacity-100 group-hover/image-component:pointer-events-auto":
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export function ImageAlignmentAction(props: Props) {
|
|||
<Tooltip disabled={isTouchDevice} tooltipContent="Align">
|
||||
<button
|
||||
type="button"
|
||||
className="h-full flex items-center gap-1 text-white/60 hover:text-white transition-colors"
|
||||
className="h-full flex items-center gap-1 text-on-color/60 hover:text-on-color transition-colors"
|
||||
onClick={() => setIsDropdownOpen((prev) => !prev)}
|
||||
>
|
||||
{activeAlignmentDetails && <activeAlignmentDetails.icon className="flex-shrink-0 size-3" />}
|
||||
|
|
@ -42,12 +42,12 @@ export function ImageAlignmentAction(props: Props) {
|
|||
</button>
|
||||
</Tooltip>
|
||||
{isDropdownOpen && (
|
||||
<div className="absolute top-full left-1/2 -translate-x-1/2 mt-0.5 h-7 bg-black/80 flex items-center gap-2 px-2 rounded">
|
||||
<div className="absolute top-full left-1/2 -translate-x-1/2 mt-0.5 h-7 bg-black/80 flex items-center gap-2 px-2 rounded-sm">
|
||||
{IMAGE_ALIGNMENT_OPTIONS.map((option) => (
|
||||
<Tooltip disabled={isTouchDevice} key={option.value} tooltipContent={option.label}>
|
||||
<button
|
||||
type="button"
|
||||
className="flex-shrink-0 h-full grid place-items-center text-white/60 hover:text-white transition-colors"
|
||||
className="flex-shrink-0 h-full grid place-items-center text-on-color/60 hover:text-on-color transition-colors"
|
||||
onClick={() => {
|
||||
handleChange(option.value);
|
||||
setIsDropdownOpen(false);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export function ImageDownloadAction(props: Props) {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => window.open(src, "_blank")}
|
||||
className="flex-shrink-0 h-full grid place-items-center text-white/60 hover:text-white transition-colors"
|
||||
className="flex-shrink-0 h-full grid place-items-center text-on-color/60 hover:text-on-color transition-colors"
|
||||
aria-label="Download image"
|
||||
>
|
||||
<Download className="size-3" />
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ function ImageFullScreenModalWithoutPortal(props: Props) {
|
|||
className="absolute top-10 right-10 size-8 grid place-items-center"
|
||||
aria-label="Close image viewer"
|
||||
>
|
||||
<CloseIcon className="size-8 text-white/60 hover:text-white transition-colors" />
|
||||
<CloseIcon className="size-8 text-on-color/60 hover:text-on-color transition-colors" />
|
||||
</button>
|
||||
<img
|
||||
ref={setImageRef}
|
||||
|
|
@ -242,13 +242,13 @@ function ImageFullScreenModalWithoutPortal(props: Props) {
|
|||
}
|
||||
handleMagnification("decrease");
|
||||
}}
|
||||
className="size-6 grid place-items-center text-white/60 hover:text-white disabled:text-white/30 transition-colors duration-200"
|
||||
className="size-6 grid place-items-center text-on-color/60 hover:text-on-color disabled:text-on-color/30 transition-colors duration-200"
|
||||
disabled={magnification <= MIN_ZOOM}
|
||||
aria-label="Zoom out"
|
||||
>
|
||||
<Minus className="size-4" />
|
||||
</button>
|
||||
<span className="text-sm w-12 text-center text-white">{Math.round(100 * magnification)}%</span>
|
||||
<span className="text-13 w-12 text-center text-on-color">{Math.round(100 * magnification)}%</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
|
|
@ -258,7 +258,7 @@ function ImageFullScreenModalWithoutPortal(props: Props) {
|
|||
}
|
||||
handleMagnification("increase");
|
||||
}}
|
||||
className="size-6 grid place-items-center text-white/60 hover:text-white disabled:text-white/30 transition-colors duration-200"
|
||||
className="size-6 grid place-items-center text-on-color/60 hover:text-on-color disabled:text-on-color/30 transition-colors duration-200"
|
||||
disabled={magnification >= MAX_ZOOM}
|
||||
aria-label="Zoom in"
|
||||
>
|
||||
|
|
@ -269,7 +269,7 @@ function ImageFullScreenModalWithoutPortal(props: Props) {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => window.open(downloadSrc, "_blank")}
|
||||
className="flex-shrink-0 size-8 grid place-items-center text-white/60 hover:text-white transition-colors duration-200"
|
||||
className="flex-shrink-0 size-8 grid place-items-center text-on-color/60 hover:text-on-color transition-colors duration-200"
|
||||
aria-label="Download image"
|
||||
>
|
||||
<Download className="size-4" />
|
||||
|
|
@ -279,7 +279,7 @@ function ImageFullScreenModalWithoutPortal(props: Props) {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => window.open(src, "_blank")}
|
||||
className="flex-shrink-0 size-8 grid place-items-center text-white/60 hover:text-white transition-colors duration-200"
|
||||
className="flex-shrink-0 size-8 grid place-items-center text-on-color/60 hover:text-on-color transition-colors duration-200"
|
||||
aria-label="Open image in new tab"
|
||||
>
|
||||
<ExternalLink className="size-4" />
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export function ImageFullScreenActionRoot(props: Props) {
|
|||
e.stopPropagation();
|
||||
setIsFullScreenEnabled(true);
|
||||
}}
|
||||
className="flex-shrink-0 h-full grid place-items-center text-white/60 hover:text-white transition-colors"
|
||||
className="flex-shrink-0 h-full grid place-items-center text-on-color/60 hover:text-on-color transition-colors"
|
||||
aria-label="View image in full screen"
|
||||
>
|
||||
<Maximize className="size-3" />
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export function ImageToolbarRoot(props: Props) {
|
|||
<>
|
||||
<div
|
||||
className={cn(
|
||||
"absolute top-1 right-1 h-7 z-20 bg-black/80 rounded flex items-center gap-2 px-2 opacity-0 pointer-events-none group-hover/image-component:opacity-100 group-hover/image-component:pointer-events-auto transition-opacity",
|
||||
"absolute top-1 right-1 h-7 z-20 bg-black/80 rounded-sm flex items-center gap-2 px-2 opacity-0 pointer-events-none group-hover/image-component:opacity-100 group-hover/image-component:pointer-events-auto transition-opacity",
|
||||
{
|
||||
"opacity-100 pointer-events-auto": shouldShowToolbar,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export function ImageUploadStatus(props: Props) {
|
|||
if (uploadStatus === undefined) return null;
|
||||
|
||||
return (
|
||||
<div className="absolute top-1 right-1 z-20 bg-black/60 rounded text-xs font-medium w-10 text-center">
|
||||
<div className="absolute top-1 right-1 z-20 bg-black/60 rounded-sm text-11 font-medium w-10 text-center">
|
||||
{displayStatus}%
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -168,9 +168,16 @@ export function CustomImageUploader(props: CustomImageUploaderProps) {
|
|||
[uploadFile, editor, getPos]
|
||||
);
|
||||
|
||||
const isErrorState = failedToLoadImage || hasDuplicationFailed;
|
||||
|
||||
const borderColor =
|
||||
selected && editor.isEditable && !isErrorState
|
||||
? "color-mix(in srgb, var(--border-color-accent-strong) 20%, transparent)"
|
||||
: undefined;
|
||||
|
||||
const getDisplayMessage = useCallback(() => {
|
||||
const isUploading = isImageBeingUploaded;
|
||||
if (failedToLoadImage || hasDuplicationFailed) {
|
||||
if (isErrorState) {
|
||||
return "Error loading image";
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +190,7 @@ export function CustomImageUploader(props: CustomImageUploaderProps) {
|
|||
}
|
||||
|
||||
return "Add an image";
|
||||
}, [draggedInside, editor.isEditable, failedToLoadImage, isImageBeingUploaded, hasDuplicationFailed]);
|
||||
}, [draggedInside, editor.isEditable, isErrorState, isImageBeingUploaded]);
|
||||
|
||||
const handleRetryClick = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
|
|
@ -198,18 +205,20 @@ export function CustomImageUploader(props: CustomImageUploaderProps) {
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"image-upload-component flex items-center justify-start gap-2 py-3 px-2 rounded-lg text-custom-text-300 bg-custom-background-90 border border-dashed border-custom-border-300 transition-all duration-200 ease-in-out cursor-default",
|
||||
"image-upload-component flex items-center justify-start gap-2 py-3 px-2 rounded-lg text-tertiary bg-layer-2 border border-dashed transition-all duration-200 ease-in-out cursor-default",
|
||||
{
|
||||
"hover:text-custom-text-200 hover:bg-custom-background-80 cursor-pointer": editor.isEditable,
|
||||
"bg-custom-background-80 text-custom-text-200": draggedInside && editor.isEditable,
|
||||
"text-custom-primary-200 bg-custom-primary-100/10 border-custom-primary-200/10 hover:bg-custom-primary-100/10 hover:text-custom-primary-200":
|
||||
selected && editor.isEditable,
|
||||
"text-red-500 cursor-default": failedToLoadImage || hasDuplicationFailed,
|
||||
"hover:text-red-500": (failedToLoadImage || hasDuplicationFailed) && editor.isEditable,
|
||||
"bg-red-500/10": (failedToLoadImage || hasDuplicationFailed) && selected,
|
||||
"hover:bg-red-500/10": (failedToLoadImage || hasDuplicationFailed) && selected && editor.isEditable,
|
||||
"border-subtle": !(selected && editor.isEditable && !isErrorState),
|
||||
"hover:text-secondary hover:bg-layer-2-hover cursor-pointer": editor.isEditable && !isErrorState,
|
||||
"bg-layer-2-hover text-secondary": draggedInside && editor.isEditable && !isErrorState,
|
||||
"text-accent-secondary bg-accent-primary/10 hover:bg-accent-primary/10 hover:text-accent-secondary":
|
||||
selected && editor.isEditable && !isErrorState,
|
||||
"text-red-500 cursor-default": isErrorState,
|
||||
"hover:text-red-500 hover:bg-red-500/10": isErrorState && editor.isEditable,
|
||||
"bg-red-500/10": isErrorState && selected,
|
||||
"hover:bg-red-500/20": isErrorState && selected && editor.isEditable,
|
||||
}
|
||||
)}
|
||||
style={borderColor ? { borderColor } : undefined}
|
||||
onDrop={onDrop}
|
||||
onDragOver={onDragEnter}
|
||||
onDragLeave={onDragLeave}
|
||||
|
|
@ -221,13 +230,13 @@ export function CustomImageUploader(props: CustomImageUploaderProps) {
|
|||
}}
|
||||
>
|
||||
<ImageIcon className="size-4" />
|
||||
<div className="text-base font-medium flex-1">{getDisplayMessage()}</div>
|
||||
<div className="text-14 font-medium flex-1">{getDisplayMessage()}</div>
|
||||
{hasDuplicationFailed && editor.isEditable && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleRetryClick}
|
||||
className={cn(
|
||||
"flex items-center gap-1 px-2 py-1 text-xs font-medium text-custom-text-300 hover:bg-custom-background-90 hover:text-custom-text-200 rounded-md transition-all duration-200 ease-in-out",
|
||||
"flex items-center gap-1 px-2 py-1 font-medium text-red-500 rounded-md transition-all duration-200 ease-in-out hover:bg-red-500/20 hover:text-red-500",
|
||||
{
|
||||
"hover:bg-red-500/20": selected,
|
||||
}
|
||||
|
|
@ -235,7 +244,7 @@ export function CustomImageUploader(props: CustomImageUploaderProps) {
|
|||
title="Retry duplication"
|
||||
>
|
||||
<RotateCcw className="size-3" />
|
||||
Retry
|
||||
<span className="text-11">Retry</span>
|
||||
</button>
|
||||
)}
|
||||
<input
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ export const CustomLinkExtension = Mark.create<LinkOptions, CustomLinkStorage>({
|
|||
target: "_blank",
|
||||
rel: "noopener noreferrer nofollow",
|
||||
class:
|
||||
"text-custom-primary-300 underline underline-offset-[3px] hover:text-custom-primary-500 transition-colors cursor-pointer",
|
||||
"text-accent-secondary underline underline-offset-[3px] hover:text-custom-primary-500 transition-colors cursor-pointer",
|
||||
},
|
||||
validate: (url: string) => isValidHttpUrl(url).isValid,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ export const EmojisListDropdown = forwardRef(function EmojisListDropdown(
|
|||
<div
|
||||
ref={dropdownContainerRef}
|
||||
className={cn(
|
||||
"relative max-h-80 w-[14rem] overflow-y-auto rounded-md border-[0.5px] border-custom-border-300 bg-custom-background-100 px-2 py-2.5 shadow-custom-shadow-rg space-y-2 opacity-0 invisible transition-opacity",
|
||||
"relative max-h-80 w-[14rem] overflow-y-auto rounded-md border-[0.5px] border-strong bg-surface-1 px-2 py-2.5 shadow-custom-shadow-rg space-y-2 opacity-0 invisible transition-opacity",
|
||||
{
|
||||
"opacity-100 visible": isVisible,
|
||||
}
|
||||
|
|
@ -150,15 +150,15 @@ export const EmojisListDropdown = forwardRef(function EmojisListDropdown(
|
|||
id={`emoji-item-${index}`}
|
||||
type="button"
|
||||
className={cn(
|
||||
"flex items-center gap-2 w-full rounded px-2 py-1.5 text-sm text-left truncate text-custom-text-200 hover:bg-custom-background-80 transition-colors duration-150",
|
||||
"flex items-center gap-2 w-full rounded-sm px-2 py-1.5 text-13 text-left truncate text-secondary hover:bg-layer-1-hover transition-colors duration-150",
|
||||
{
|
||||
"bg-custom-background-80": isSelected,
|
||||
"bg-layer-1-hover": isSelected,
|
||||
}
|
||||
)}
|
||||
onClick={() => selectItem(index)}
|
||||
onMouseEnter={() => setSelectedIndex(index)}
|
||||
>
|
||||
<span className="size-5 grid place-items-center flex-shrink-0 text-base">{item.emoji}</span>
|
||||
<span className="size-5 grid place-items-center flex-shrink-0 text-14">{item.emoji}</span>
|
||||
<span className="flex-grow truncate">
|
||||
<span className="font-medium">:{item.name}:</span>
|
||||
</span>
|
||||
|
|
@ -166,7 +166,7 @@ export const EmojisListDropdown = forwardRef(function EmojisListDropdown(
|
|||
);
|
||||
})
|
||||
) : (
|
||||
<div className="text-center text-sm text-custom-text-400 py-2">No emojis found</div>
|
||||
<div className="text-center text-13 text-placeholder py-2">No emojis found</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export const CustomHorizontalRule = Node.create<HorizontalRuleOptions>({
|
|||
addOptions() {
|
||||
return {
|
||||
HTMLAttributes: {
|
||||
class: "py-4 border-custom-border-400",
|
||||
class: "py-4 border-strong-1",
|
||||
},
|
||||
};
|
||||
},
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ export const MentionsListDropdown = forwardRef(function MentionsListDropdown(pro
|
|||
/>
|
||||
<div
|
||||
ref={dropdownContainer}
|
||||
className="relative max-h-80 w-[14rem] overflow-y-auto rounded-md border-[0.5px] border-custom-border-300 bg-custom-background-100 px-2 py-2.5 shadow-custom-shadow-rg space-y-2"
|
||||
className="relative max-h-80 w-[14rem] overflow-y-auto rounded-md border-[0.5px] border-strong bg-surface-1 px-2 py-2.5 shadow-custom-shadow-rg space-y-2"
|
||||
style={{
|
||||
zIndex: 100,
|
||||
}}
|
||||
|
|
@ -136,11 +136,11 @@ export const MentionsListDropdown = forwardRef(function MentionsListDropdown(pro
|
|||
}}
|
||||
>
|
||||
{isLoading ? (
|
||||
<div className="text-center text-sm text-custom-text-400">Loading...</div>
|
||||
<div className="text-center text-13 text-placeholder">Loading...</div>
|
||||
) : sections.length ? (
|
||||
sections.map((section, sectionIndex) => (
|
||||
<div key={section.key} className="space-y-2">
|
||||
{section.title && <h6 className="text-xs font-semibold text-custom-text-300">{section.title}</h6>}
|
||||
{section.title && <h6 className="text-11 font-semibold text-tertiary">{section.title}</h6>}
|
||||
{section.items.map((item, itemIndex) => {
|
||||
const isSelected = sectionIndex === selectedIndex.section && itemIndex === selectedIndex.item;
|
||||
|
||||
|
|
@ -150,9 +150,9 @@ export const MentionsListDropdown = forwardRef(function MentionsListDropdown(pro
|
|||
id={`mention-item-${sectionIndex}-${itemIndex}`}
|
||||
type="button"
|
||||
className={cn(
|
||||
"flex items-center gap-2 w-full rounded px-1 py-1.5 text-xs text-left truncate text-custom-text-200",
|
||||
"flex items-center gap-2 w-full rounded-sm px-1 py-1.5 text-11 text-left truncate text-secondary hover:bg-layer-1-hover",
|
||||
{
|
||||
"bg-custom-background-80": isSelected,
|
||||
"bg-layer-1-hover": isSelected,
|
||||
}
|
||||
)}
|
||||
onClick={(e) => {
|
||||
|
|
@ -169,7 +169,7 @@ export const MentionsListDropdown = forwardRef(function MentionsListDropdown(pro
|
|||
>
|
||||
<span className="size-5 grid place-items-center flex-shrink-0">{item.icon}</span>
|
||||
{item.subTitle && (
|
||||
<h5 className="whitespace-nowrap text-xs text-custom-text-300 flex-shrink-0">{item.subTitle}</h5>
|
||||
<h5 className="whitespace-nowrap text-11 text-tertiary flex-shrink-0">{item.subTitle}</h5>
|
||||
)}
|
||||
<p className="flex-grow truncate">{item.title}</p>
|
||||
</button>
|
||||
|
|
@ -178,7 +178,7 @@ export const MentionsListDropdown = forwardRef(function MentionsListDropdown(pro
|
|||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="text-center text-sm text-custom-text-400">No results</div>
|
||||
<div className="text-center text-13 text-placeholder">No results</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -215,14 +215,7 @@ export const getSlashCommandFilteredSections =
|
|||
title: "Default",
|
||||
description: "Change text color",
|
||||
searchTerms: ["color", "text", "default"],
|
||||
icon: (
|
||||
<ALargeSmall
|
||||
className="size-3.5"
|
||||
style={{
|
||||
color: "rgba(var(--color-text-100))",
|
||||
}}
|
||||
/>
|
||||
),
|
||||
icon: <ALargeSmall className="size-3.5 text-primary" />,
|
||||
command: ({ editor, range }) => toggleTextColor(undefined, editor, range),
|
||||
},
|
||||
...COLORS_LIST.map(
|
||||
|
|
@ -262,7 +255,7 @@ export const getSlashCommandFilteredSections =
|
|||
iconContainerStyle: {
|
||||
borderRadius: "4px",
|
||||
backgroundColor: "rgba(var(--color-background-100))",
|
||||
border: "1px solid rgba(var(--color-border-300))",
|
||||
border: "1px solid var(--border-color-strong)",
|
||||
},
|
||||
command: ({ editor, range }) => toggleTextColor(undefined, editor, range),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ const highlightMatch = (text: string, query: string): React.ReactNode => {
|
|||
return (
|
||||
<>
|
||||
{before}
|
||||
<span className="font-medium text-custom-text-100">{match}</span>
|
||||
<span className="font-medium text-primary">{match}</span>
|
||||
{after}
|
||||
</>
|
||||
);
|
||||
|
|
@ -48,9 +48,9 @@ export function CommandMenuItem(props: Props) {
|
|||
type="button"
|
||||
id={`item-${sectionIndex}-${itemIndex}`}
|
||||
className={cn(
|
||||
"flex items-center gap-2 w-full rounded px-1 py-1.5 text-sm text-left truncate text-custom-text-200",
|
||||
"flex items-center gap-2 w-full rounded-sm px-1 py-1.5 text-13 text-left truncate text-secondary hover:bg-layer-1-hover",
|
||||
{
|
||||
"bg-custom-background-80": isSelected,
|
||||
"bg-layer-1-hover": isSelected,
|
||||
}
|
||||
)}
|
||||
onClick={onClick}
|
||||
|
|
@ -59,7 +59,7 @@ export function CommandMenuItem(props: Props) {
|
|||
<span className="size-5 grid place-items-center flex-shrink-0" style={item.iconContainerStyle}>
|
||||
{item.icon}
|
||||
</span>
|
||||
<p className="flex-grow truncate">{query ? highlightMatch(item.title, query) : item.title}</p>
|
||||
<p className="flex-grow truncate text-12">{query ? highlightMatch(item.title, query) : item.title}</p>
|
||||
{item.badge}
|
||||
</button>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ export const SlashCommandsMenu = forwardRef(function SlashCommandsMenu(props: Sl
|
|||
<div
|
||||
id="slash-command"
|
||||
ref={commandListContainer}
|
||||
className="relative max-h-80 min-w-[12rem] overflow-y-auto rounded-md border-[0.5px] border-custom-border-300 bg-custom-background-100 px-2 py-2.5 shadow-custom-shadow-rg space-y-2"
|
||||
className="relative max-h-80 min-w-[12rem] overflow-y-auto rounded-md border-[0.5px] border-strong bg-surface-1 px-2 py-2.5 shadow-custom-shadow-rg space-y-2"
|
||||
style={{
|
||||
zIndex: 100,
|
||||
}}
|
||||
|
|
@ -144,7 +144,7 @@ export const SlashCommandsMenu = forwardRef(function SlashCommandsMenu(props: Sl
|
|||
>
|
||||
{sections.map((section, sectionIndex) => (
|
||||
<div key={section.key} className="space-y-2">
|
||||
{section.title && <h6 className="text-xs font-semibold text-custom-text-300">{section.title}</h6>}
|
||||
{section.title && <h6 className="text-11 font-semibold text-tertiary">{section.title}</h6>}
|
||||
<div>
|
||||
{section.items?.map((item, itemIndex) => (
|
||||
<CommandMenuItem
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@ export const CustomStarterKitExtension = (args: TArgs) => {
|
|||
return StarterKit.configure({
|
||||
bulletList: {
|
||||
HTMLAttributes: {
|
||||
class: "list-disc pl-7 space-y-[--list-spacing-y]",
|
||||
class: "list-disc pl-7 space-y-(--list-spacing-y)",
|
||||
},
|
||||
},
|
||||
orderedList: {
|
||||
HTMLAttributes: {
|
||||
class: "list-decimal pl-7 space-y-[--list-spacing-y]",
|
||||
class: "list-decimal pl-7 space-y-(--list-spacing-y)",
|
||||
},
|
||||
},
|
||||
listItem: {
|
||||
|
|
@ -39,7 +39,7 @@ export const CustomStarterKitExtension = (args: TArgs) => {
|
|||
},
|
||||
dropcursor: {
|
||||
class:
|
||||
"text-custom-text-300 transition-all motion-reduce:transition-none motion-reduce:hover:transform-none duration-200 ease-[cubic-bezier(0.165, 0.84, 0.44, 1)]",
|
||||
"text-tertiary transition-all motion-reduce:transition-none motion-reduce:hover:transform-none duration-200 ease-[cubic-bezier(0.165, 0.84, 0.44, 1)]",
|
||||
},
|
||||
...(enableHistory ? {} : { history: false }),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export function TableDragHandleDropdownColorSelector(props: Props) {
|
|||
<Disclosure.Button
|
||||
as="button"
|
||||
type="button"
|
||||
className="flex items-center justify-between gap-2 w-full rounded px-1 py-1.5 text-xs text-left truncate text-custom-text-200 hover:bg-custom-background-80"
|
||||
className="flex items-center justify-between gap-2 w-full rounded-sm px-1 py-1.5 text-11 text-left truncate text-secondary hover:bg-layer-1"
|
||||
>
|
||||
{({ open }) => (
|
||||
<>
|
||||
|
|
@ -61,13 +61,13 @@ export function TableDragHandleDropdownColorSelector(props: Props) {
|
|||
</Disclosure.Button>
|
||||
<Disclosure.Panel className="p-1 space-y-2 mb-1.5">
|
||||
{/* <div className="space-y-1.5">
|
||||
<p className="text-xs text-custom-text-300 font-semibold">Text colors</p>
|
||||
<p className="text-11 text-tertiary font-semibold">Text colors</p>
|
||||
<div className="flex items-center flex-wrap gap-2">
|
||||
{COLORS_LIST.map((color) => (
|
||||
<button
|
||||
key={color.key}
|
||||
type="button"
|
||||
className="flex-shrink-0 size-6 rounded border-[0.5px] border-custom-border-400 hover:opacity-60 transition-opacity"
|
||||
className="flex-shrink-0 size-6 rounded-sm border-[0.5px] border-strong-1 hover:opacity-60 transition-opacity"
|
||||
style={{
|
||||
backgroundColor: color.textColor,
|
||||
}}
|
||||
|
|
@ -76,7 +76,7 @@ export function TableDragHandleDropdownColorSelector(props: Props) {
|
|||
))}
|
||||
<button
|
||||
type="button"
|
||||
className="flex-shrink-0 size-6 grid place-items-center rounded text-custom-text-300 border-[0.5px] border-custom-border-400 hover:bg-custom-background-80 transition-colors"
|
||||
className="flex-shrink-0 size-6 grid place-items-center rounded-sm text-tertiary border-[0.5px] border-strong-1 hover:bg-layer-1 transition-colors"
|
||||
onClick={() => handleTextColorChange(editor, null)}
|
||||
>
|
||||
<Ban className="size-4" />
|
||||
|
|
@ -84,13 +84,13 @@ export function TableDragHandleDropdownColorSelector(props: Props) {
|
|||
</div>
|
||||
</div> */}
|
||||
<div className="space-y-1">
|
||||
<p className="text-xs text-custom-text-300 font-semibold">Background colors</p>
|
||||
<p className="text-11 text-tertiary font-semibold">Background colors</p>
|
||||
<div className="flex items-center flex-wrap gap-2">
|
||||
{COLORS_LIST.map((color) => (
|
||||
<button
|
||||
key={color.key}
|
||||
type="button"
|
||||
className="flex-shrink-0 size-6 rounded border-[0.5px] border-custom-border-400 hover:opacity-60 transition-opacity"
|
||||
className="flex-shrink-0 size-6 rounded-sm border-[0.5px] border-strong-1 hover:opacity-60 transition-opacity"
|
||||
style={{
|
||||
backgroundColor: color.backgroundColor,
|
||||
}}
|
||||
|
|
@ -102,7 +102,7 @@ export function TableDragHandleDropdownColorSelector(props: Props) {
|
|||
))}
|
||||
<button
|
||||
type="button"
|
||||
className="flex-shrink-0 size-6 grid place-items-center rounded text-custom-text-300 border-[0.5px] border-custom-border-400 hover:bg-custom-background-80 transition-colors"
|
||||
className="flex-shrink-0 size-6 grid place-items-center rounded-sm text-tertiary border-[0.5px] border-strong-1 hover:bg-layer-1-hover transition-colors"
|
||||
onClick={() => {
|
||||
handleBackgroundColorChange(editor, null);
|
||||
onSelect(null);
|
||||
|
|
|
|||
|
|
@ -187,15 +187,12 @@ export function ColumnDragHandle(props: ColumnDragHandleProps) {
|
|||
{...getReferenceProps()}
|
||||
type="button"
|
||||
onMouseDown={handleMouseDown}
|
||||
className={cn(
|
||||
"px-1 bg-custom-background-90 border border-custom-border-400 rounded outline-none transition-all duration-200",
|
||||
{
|
||||
"!opacity-100 bg-custom-primary-100 border-custom-primary-100": isDropdownOpen,
|
||||
"hover:bg-custom-background-80": !isDropdownOpen,
|
||||
}
|
||||
)}
|
||||
className={cn("px-1 bg-layer-1 border border-strong-1 rounded-sm outline-none transition-all duration-200", {
|
||||
"!opacity-100 bg-accent-primary border-accent-strong": isDropdownOpen,
|
||||
"hover:bg-layer-1-hover": !isDropdownOpen,
|
||||
})}
|
||||
>
|
||||
<Ellipsis className="size-4 text-custom-text-100" />
|
||||
<Ellipsis className="size-4 text-primary" />
|
||||
</button>
|
||||
</div>
|
||||
{isDropdownOpen && (
|
||||
|
|
@ -208,7 +205,7 @@ export function ColumnDragHandle(props: ColumnDragHandleProps) {
|
|||
lockScroll
|
||||
/>
|
||||
<div
|
||||
className="max-h-[90vh] w-[12rem] overflow-y-auto rounded-md border-[0.5px] border-custom-border-300 bg-custom-background-100 px-2 py-2.5 shadow-custom-shadow-rg"
|
||||
className="max-h-[90vh] w-[12rem] overflow-y-auto rounded-md border-[0.5px] border-strong bg-surface-1 px-2 py-2.5 shadow-custom-shadow-rg"
|
||||
ref={refs.setFloating}
|
||||
{...getFloatingProps()}
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export function ColumnOptionsDropdown(props: Props) {
|
|||
<>
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center justify-between gap-2 w-full rounded px-1 py-1.5 text-xs text-left truncate text-custom-text-200 hover:bg-custom-background-80"
|
||||
className="flex items-center justify-between gap-2 w-full rounded-sm px-1 py-1.5 text-11 text-left truncate text-secondary hover:bg-layer-1"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
|
@ -80,13 +80,13 @@ export function ColumnOptionsDropdown(props: Props) {
|
|||
<div className="flex-grow truncate">Header column</div>
|
||||
<ToggleRight className="shrink-0 size-3" />
|
||||
</button>
|
||||
<hr className="my-2 border-custom-border-200" />
|
||||
<hr className="my-2 border-subtle" />
|
||||
<TableDragHandleDropdownColorSelector editor={editor} onSelect={onClose} />
|
||||
{DROPDOWN_ITEMS.map((item) => (
|
||||
<button
|
||||
key={item.key}
|
||||
type="button"
|
||||
className="flex items-center gap-2 w-full rounded px-1 py-1.5 text-xs text-left truncate text-custom-text-200 hover:bg-custom-background-80"
|
||||
className="flex items-center gap-2 w-full rounded-sm px-1 py-1.5 text-11 text-left truncate text-secondary hover:bg-layer-1"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
|
|
|||
|
|
@ -186,15 +186,12 @@ export function RowDragHandle(props: RowDragHandleProps) {
|
|||
{...getReferenceProps()}
|
||||
type="button"
|
||||
onMouseDown={handleMouseDown}
|
||||
className={cn(
|
||||
"py-1 bg-custom-background-90 border border-custom-border-400 rounded outline-none transition-all duration-200",
|
||||
{
|
||||
"!opacity-100 bg-custom-primary-100 border-custom-primary-100": isDropdownOpen,
|
||||
"hover:bg-custom-background-80": !isDropdownOpen,
|
||||
}
|
||||
)}
|
||||
className={cn("py-1 bg-layer-1 border border-strong-1 rounded-sm outline-none transition-all duration-200", {
|
||||
"!opacity-100 bg-accent-primary border-accent-strong": isDropdownOpen,
|
||||
"hover:bg-layer-1-hover": !isDropdownOpen,
|
||||
})}
|
||||
>
|
||||
<Ellipsis className="size-4 text-custom-text-100 rotate-90" />
|
||||
<Ellipsis className="size-4 text-primary rotate-90" />
|
||||
</button>
|
||||
</div>
|
||||
{isDropdownOpen && (
|
||||
|
|
@ -207,7 +204,7 @@ export function RowDragHandle(props: RowDragHandleProps) {
|
|||
lockScroll
|
||||
/>
|
||||
<div
|
||||
className="max-h-[90vh] w-[12rem] overflow-y-auto rounded-md border-[0.5px] border-custom-border-300 bg-custom-background-100 px-2 py-2.5 shadow-custom-shadow-rg"
|
||||
className="max-h-[90vh] w-[12rem] overflow-y-auto rounded-md border-[0.5px] border-strong bg-surface-1 px-2 py-2.5 shadow-custom-shadow-rg"
|
||||
ref={refs.setFloating}
|
||||
{...getFloatingProps()}
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export function RowOptionsDropdown(props: Props) {
|
|||
<>
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center justify-between gap-2 w-full rounded px-1 py-1.5 text-xs text-left truncate text-custom-text-200 hover:bg-custom-background-80"
|
||||
className="flex items-center justify-between gap-2 w-full rounded-sm px-1 py-1.5 text-11 text-left truncate text-secondary hover:bg-layer-1"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
|
@ -80,13 +80,13 @@ export function RowOptionsDropdown(props: Props) {
|
|||
<div className="flex-grow truncate">Header row</div>
|
||||
<ToggleRight className="shrink-0 size-3" />
|
||||
</button>
|
||||
<hr className="my-2 border-custom-border-200" />
|
||||
<hr className="my-2 border-subtle" />
|
||||
<TableDragHandleDropdownColorSelector editor={editor} onSelect={onClose} />
|
||||
{DROPDOWN_ITEMS.map((item) => (
|
||||
<button
|
||||
key={item.key}
|
||||
type="button"
|
||||
className="flex items-center gap-2 w-full rounded px-1 py-1.5 text-xs text-left truncate text-custom-text-200 hover:bg-custom-background-80"
|
||||
className="flex items-center gap-2 w-full rounded-sm px-1 py-1.5 text-11 text-left truncate text-secondary hover:bg-layer-1"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export const constructDragPreviewTable = (): {
|
|||
} => {
|
||||
const tableElement = document.createElement("table");
|
||||
tableElement.classList.add("table-drag-preview");
|
||||
tableElement.classList.add("bg-custom-background-100");
|
||||
tableElement.classList.add("bg-surface-1");
|
||||
tableElement.style.opacity = "0.9";
|
||||
const tableBodyElement = document.createElement("tbody");
|
||||
tableElement.appendChild(tableBodyElement);
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ export const getEditorClassNames = ({ noBorder, borderOnFocus, containerClassNam
|
|||
cn(
|
||||
"w-full max-w-full sm:rounded-lg focus:outline-none focus:border-0",
|
||||
{
|
||||
"border border-custom-border-200": !noBorder,
|
||||
"focus:border border-custom-border-300": borderOnFocus,
|
||||
"border border-subtle-1": !noBorder,
|
||||
"focus:border border-strong": borderOnFocus,
|
||||
},
|
||||
containerClassName
|
||||
);
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ export const AIHandlePlugin = (options: SideMenuPluginProps): SideMenuHandleOpti
|
|||
const view = (view: EditorView, sideMenu: HTMLDivElement | null) => {
|
||||
// create handle element
|
||||
const className =
|
||||
"grid place-items-center font-medium size-5 aspect-square text-xs text-custom-text-300 hover:bg-custom-background-80 rounded-sm opacity-100 !outline-none z-[5] transition-[background-color,_opacity] duration-200 ease-linear";
|
||||
"grid place-items-center font-medium size-5 aspect-square text-11 text-tertiary hover:bg-layer-1 rounded-xs opacity-100 !outline-none z-[5] transition-[background-color,_opacity] duration-200 ease-linear";
|
||||
aiHandleElement = document.createElement("button");
|
||||
aiHandleElement.type = "button";
|
||||
aiHandleElement.id = "ai-handle";
|
||||
|
|
|
|||
|
|
@ -42,13 +42,13 @@ const createDragHandleElement = (): HTMLElement => {
|
|||
dragHandleElement.draggable = true;
|
||||
dragHandleElement.dataset.dragHandle = "";
|
||||
dragHandleElement.classList.value =
|
||||
"hidden sm:flex items-center size-5 aspect-square rounded-sm cursor-grab outline-none hover:bg-custom-background-80 active:bg-custom-background-80 active:cursor-grabbing transition-[background-color,_opacity] duration-200 ease-linear";
|
||||
"hidden sm:flex items-center size-5 aspect-square rounded-xs cursor-grab outline-none hover:bg-layer-1-hover active:bg-layer-1 active:cursor-grabbing transition-[background-color,_opacity] duration-200 ease-linear";
|
||||
|
||||
const iconElement1 = document.createElement("span");
|
||||
iconElement1.classList.value = "pointer-events-none text-custom-text-300";
|
||||
iconElement1.classList.value = "pointer-events-none text-tertiary";
|
||||
iconElement1.innerHTML = verticalEllipsisIcon;
|
||||
const iconElement2 = document.createElement("span");
|
||||
iconElement2.classList.value = "pointer-events-none text-custom-text-300 -ml-2.5";
|
||||
iconElement2.classList.value = "pointer-events-none text-tertiary -ml-2.5";
|
||||
iconElement2.innerHTML = verticalEllipsisIcon;
|
||||
|
||||
dragHandleElement.appendChild(iconElement1);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
.ProseMirror blockquote {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
border-left: 3px solid rgb(var(--color-border-300));
|
||||
@apply border-l-[3px] border-strong;
|
||||
}
|
||||
|
||||
.ProseMirror blockquote p::before,
|
||||
|
|
@ -84,7 +84,7 @@
|
|||
|
||||
/* Custom gap cursor styles */
|
||||
.ProseMirror-gapcursor::after {
|
||||
border-top: 1px solid rgb(var(--color-text-100)) !important;
|
||||
@apply border-t! border-strong!;
|
||||
}
|
||||
|
||||
/* to-do list */
|
||||
|
|
@ -105,14 +105,14 @@ ul[data-type="taskList"] li > div {
|
|||
}
|
||||
|
||||
ul[data-type="taskList"] li > label input[type="checkbox"] {
|
||||
border: 1px solid rgba(var(--color-text-100), 0.2) !important;
|
||||
@apply border! border-strong/20!;
|
||||
outline: none;
|
||||
border-radius: 2px;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.ProseMirror[contenteditable="true"] input[type="checkbox"]:hover {
|
||||
background-color: rgba(var(--color-text-100), 0.1);
|
||||
background-color: color-mix(in srgb, var(--text-color-primary) 10%, transparent);
|
||||
}
|
||||
|
||||
.ProseMirror[contenteditable="false"] input[type="checkbox"] {
|
||||
|
|
@ -120,14 +120,14 @@ ul[data-type="taskList"] li > label input[type="checkbox"] {
|
|||
}
|
||||
|
||||
ul[data-type="taskList"] li > label input[type="checkbox"]:checked {
|
||||
background-color: rgba(var(--color-primary-100)) !important;
|
||||
border-color: rgba(var(--color-primary-100)) !important;
|
||||
background-color: var(--background-color-accent-primary) !important;
|
||||
border-color: var(--border-color-accent-strong) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
ul[data-type="taskList"] li > label input[type="checkbox"]:checked:hover {
|
||||
background-color: rgba(var(--color-primary-300)) !important;
|
||||
border-color: rgba(var(--color-primary-300)) !important;
|
||||
background-color: var(--background-color-accent-primary-hover) !important;
|
||||
border-color: var(--border-color-accent-strong) !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
|
|
@ -151,12 +151,12 @@ ul[data-type="taskList"] li > label input[type="checkbox"] {
|
|||
width: 0.8rem;
|
||||
height: 0.8rem;
|
||||
position: relative;
|
||||
border: 1.5px solid rgb(var(--color-text-100));
|
||||
@apply border-md border-subtle-1;
|
||||
margin-right: 0.2rem;
|
||||
margin-top: 0.15rem;
|
||||
|
||||
&:active {
|
||||
background-color: rgb(var(--color-background-90));
|
||||
background-color: var(--background-color-layer-1);
|
||||
}
|
||||
|
||||
/* check sign */
|
||||
|
|
@ -257,7 +257,7 @@ div[data-type="horizontalRule"] {
|
|||
margin-bottom: 0;
|
||||
|
||||
& > div {
|
||||
border-bottom: 2px solid rgb(var(--color-border-200));
|
||||
@apply border-b-lg border-subtle-1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,17 +3,16 @@
|
|||
padding-bottom: 26px;
|
||||
|
||||
table {
|
||||
position: relative;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
margin: 0.5rem 0 0 0;
|
||||
border: 1px solid rgba(var(--color-border-200));
|
||||
@apply relative border border-subtle;
|
||||
width: 100%;
|
||||
|
||||
td,
|
||||
th {
|
||||
min-width: 1em;
|
||||
border: 1px solid rgba(var(--color-border-300));
|
||||
@apply border border-strong;
|
||||
padding: 7px 10px;
|
||||
vertical-align: top;
|
||||
box-sizing: border-box;
|
||||
|
|
@ -38,19 +37,19 @@
|
|||
}
|
||||
|
||||
&.selectedCell-border-top::after {
|
||||
border-top: 2px solid rgba(var(--color-primary-100));
|
||||
border-top: 2px solid var(--border-color-accent-strong);
|
||||
}
|
||||
|
||||
&.selectedCell-border-left::after {
|
||||
border-left: 2px solid rgba(var(--color-primary-100));
|
||||
border-left: 2px solid var(--border-color-accent-strong);
|
||||
}
|
||||
|
||||
&.selectedCell-border-bottom::after {
|
||||
border-bottom: 2px solid rgba(var(--color-primary-100));
|
||||
border-bottom: 2px solid var(--border-color-accent-strong);
|
||||
}
|
||||
|
||||
&.selectedCell-border-right::after {
|
||||
border-right: 2px solid rgba(var(--color-primary-100));
|
||||
border-right: 2px solid var(--border-color-accent-strong);
|
||||
}
|
||||
}
|
||||
/* End selected cell outline */
|
||||
|
|
@ -81,34 +80,31 @@
|
|||
}
|
||||
|
||||
th {
|
||||
font-weight: 500;
|
||||
text-align: left;
|
||||
@apply font-medium text-left;
|
||||
}
|
||||
|
||||
tr[background="none"],
|
||||
tr:not([background]) {
|
||||
th {
|
||||
background-color: rgba(var(--color-background-90));
|
||||
@apply bg-layer-2;
|
||||
}
|
||||
}
|
||||
|
||||
.table-drop-marker {
|
||||
background-color: rgba(var(--color-primary-100));
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
background-color: var(--border-color-accent-strong);
|
||||
@apply absolute z-10;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.table-col-drag-marker,
|
||||
.table-row-drag-marker {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
@apply absolute z-10;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -124,7 +120,7 @@
|
|||
/* Selected status */
|
||||
&.ProseMirror-selectednode {
|
||||
table {
|
||||
background-color: rgba(var(--color-primary-100), 0.2);
|
||||
background-color: color-mix(in srgb, var(--background-color-accent-primary) 20%, transparent);
|
||||
}
|
||||
}
|
||||
/* End selected status */
|
||||
|
|
@ -138,7 +134,7 @@
|
|||
width: 2px;
|
||||
height: calc(100% + 2px);
|
||||
z-index: 5;
|
||||
background-color: rgba(var(--color-primary-100));
|
||||
background-color: var(--border-color-accent-strong);
|
||||
pointer-events: none;
|
||||
}
|
||||
/* End column resizer */
|
||||
|
|
@ -156,9 +152,8 @@
|
|||
.table-column-insert-button,
|
||||
.table-row-insert-button {
|
||||
position: absolute;
|
||||
background-color: rgba(var(--color-background-90));
|
||||
color: rgba(var(--color-text-300));
|
||||
border: 1px solid rgba(var(--color-border-200));
|
||||
background-color: var(--background-color-layer-1);
|
||||
@apply text-tertiary border border-subtle-1;
|
||||
border-radius: 4px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
|
|
@ -169,15 +164,15 @@
|
|||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(var(--color-background-80));
|
||||
color: rgba(var(--color-text-100));
|
||||
background-color: var(--background-color-layer-1-hover);
|
||||
@apply text-primary;
|
||||
}
|
||||
|
||||
&.dragging {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
background-color: rgba(var(--color-primary-100), 0.2);
|
||||
color: rgba(var(--color-text-100));
|
||||
background-color: color-mix(in srgb, var(--background-color-accent-primary) 20%, transparent);
|
||||
@apply text-primary;
|
||||
}
|
||||
|
||||
svg {
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
|
||||
/* font size and style */
|
||||
.editor-container {
|
||||
--color-placeholder: rgba(var(--color-text-100), 0.5);
|
||||
--color-placeholder: --alpha(var(--text-color-primary) / 50%);
|
||||
|
||||
/* font sizes and line heights */
|
||||
&.large-font {
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
import sharedConfig from "@plane/tailwind-config/tailwind.config.js";
|
||||
|
||||
export default {
|
||||
// prefix ui lib classes to avoid conflicting with the app
|
||||
...sharedConfig,
|
||||
};
|
||||
|
|
@ -840,7 +840,7 @@ export default {
|
|||
label: "Remove parent work item",
|
||||
},
|
||||
},
|
||||
new: "New Work item",
|
||||
new: "New work item",
|
||||
adding: "Adding work item",
|
||||
create: {
|
||||
success: "Work item created successfully",
|
||||
|
|
@ -2627,6 +2627,7 @@ export default {
|
|||
copy_current_page_url: "Copy current page URL",
|
||||
copy_current_page_url_toast_success: "Current page URL copied to clipboard.",
|
||||
copy_current_page_url_toast_error: "Some error occurred while copying the current page URL to clipboard.",
|
||||
focus_top_nav_search: "Focus search input",
|
||||
},
|
||||
preferences_actions: {
|
||||
update_theme: "Change interface theme",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import type { Preview } from "@storybook/react-vite";
|
||||
import "@plane/tailwind-config/global.css";
|
||||
import "../src/styles/react-day-picker.css";
|
||||
import "./tailwind.css";
|
||||
|
||||
const parameters: Preview["parameters"] = {
|
||||
controls: {
|
||||
|
|
|
|||
2
packages/propel/.storybook/tailwind.css
Normal file
2
packages/propel/.storybook/tailwind.css
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
@import "@plane/tailwind-config/index.css";
|
||||
@import "../src/styles/react-day-picker.css";
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
"./accordion": "./dist/accordion/index.js",
|
||||
"./animated-counter": "./dist/animated-counter/index.js",
|
||||
"./avatar": "./dist/avatar/index.js",
|
||||
"./badge": "./dist/badge/index.js",
|
||||
"./banner": "./dist/banner/index.js",
|
||||
"./button": "./dist/button/index.js",
|
||||
"./calendar": "./dist/calendar/index.js",
|
||||
|
|
@ -39,6 +40,7 @@
|
|||
"./emoji-icon-picker": "./dist/emoji-icon-picker/index.js",
|
||||
"./emoji-reaction": "./dist/emoji-reaction/index.js",
|
||||
"./empty-state": "./dist/empty-state/index.js",
|
||||
"./icon-button": "./dist/icon-button/index.js",
|
||||
"./icons": "./dist/icons/index.js",
|
||||
"./input": "./dist/input/index.js",
|
||||
"./menu": "./dist/menu/index.js",
|
||||
|
|
@ -66,8 +68,9 @@
|
|||
"@plane/constants": "workspace:*",
|
||||
"@plane/hooks": "workspace:*",
|
||||
"@plane/types": "workspace:*",
|
||||
"@plane/utils": "workspace:*",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"class-variance-authority": "0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"framer-motion": "^12.23.0",
|
||||
|
|
@ -77,7 +80,7 @@
|
|||
"react-day-picker": "9.5.0",
|
||||
"react-dom": "catalog:",
|
||||
"recharts": "^2.15.1",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwind-merge": "3.4.0",
|
||||
"use-font-face-observer": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -1 +1,3 @@
|
|||
module.exports = require("@plane/tailwind-config/postcss.config.js");
|
||||
import postcssConfig from "@plane/tailwind-config/postcss.config.js";
|
||||
|
||||
export default postcssConfig;
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ export const AsChildTrigger: Story = {
|
|||
<Accordion.Root className="w-96">
|
||||
<Accordion.Item value="item-1">
|
||||
<Accordion.Trigger asChild>
|
||||
<button className="w-full rounded-md bg-blue-500 px-4 py-2 text-left text-white hover:bg-blue-600">
|
||||
<button className="w-full rounded-md bg-blue-500 px-4 py-2 text-left text-on-color hover:bg-blue-600">
|
||||
Custom Button Trigger
|
||||
</button>
|
||||
</Accordion.Trigger>
|
||||
|
|
@ -186,7 +186,7 @@ export const AsChildTrigger: Story = {
|
|||
</Accordion.Item>
|
||||
<Accordion.Item value="item-2">
|
||||
<Accordion.Trigger asChild>
|
||||
<button className="w-full rounded-md bg-green-500 px-4 py-2 text-left text-white hover:bg-green-600">
|
||||
<button className="w-full rounded-md bg-green-500 px-4 py-2 text-left text-on-color hover:bg-green-600">
|
||||
Another Custom Trigger
|
||||
</button>
|
||||
</Accordion.Trigger>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export interface AccordionContentProps {
|
|||
|
||||
function AccordionRoot({ defaultValue = [], allowMultiple = false, className = "", children }: AccordionRootProps) {
|
||||
return (
|
||||
<BaseAccordion.Root defaultValue={defaultValue} openMultiple={allowMultiple} className={`text-base ${className}`}>
|
||||
<BaseAccordion.Root defaultValue={defaultValue} openMultiple={allowMultiple} className={`text-14 ${className}`}>
|
||||
{children}
|
||||
</BaseAccordion.Root>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export const Default: Story = {
|
|||
<div className="space-y-6 p-4">
|
||||
<div className="flex items-center justify-center gap-6">
|
||||
<button
|
||||
className="px-4 py-2 bg-red-500 text-white font-medium rounded-lg hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-400 focus:ring-offset-2 transition-colors shadow-md"
|
||||
className="px-4 py-2 bg-red-500 text-on-color font-medium rounded-lg hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-400 focus:ring-offset-2 transition-colors shadow-md"
|
||||
onClick={() => setCount((prev) => Math.max(0, prev - 1))}
|
||||
>
|
||||
-1
|
||||
|
|
@ -35,7 +35,7 @@ export const Default: Story = {
|
|||
<AnimatedCounter {...args} count={count} />
|
||||
</div>
|
||||
<button
|
||||
className="px-4 py-2 bg-green-500 text-white font-medium rounded-lg hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-400 focus:ring-offset-2 transition-colors shadow-md"
|
||||
className="px-4 py-2 bg-green-500 text-on-color font-medium rounded-lg hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-400 focus:ring-offset-2 transition-colors shadow-md"
|
||||
onClick={() => setCount((prev) => prev + 1)}
|
||||
>
|
||||
+1
|
||||
|
|
@ -54,13 +54,13 @@ export const Sizes: Story = {
|
|||
<div className="space-y-6 p-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
className="px-3 py-1 bg-custom-background-80 text-sm rounded hover:bg-custom-background-90"
|
||||
className="px-3 py-1 bg-layer-1 text-13 rounded-sm hover:bg-surface-2"
|
||||
onClick={() => setCount((prev) => Math.max(0, prev - 1))}
|
||||
>
|
||||
-1
|
||||
</button>
|
||||
<button
|
||||
className="px-3 py-1 bg-custom-background-80 text-sm rounded hover:bg-custom-background-90"
|
||||
className="px-3 py-1 bg-layer-1 text-13 rounded-sm hover:bg-surface-2"
|
||||
onClick={() => setCount((prev) => prev + 1)}
|
||||
>
|
||||
+1
|
||||
|
|
@ -68,20 +68,20 @@ export const Sizes: Story = {
|
|||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-sm text-custom-text-400 w-20">Small:</span>
|
||||
<div className="flex items-center justify-center min-w-[40px] h-8 bg-custom-background-80 border border-custom-border-200 rounded">
|
||||
<span className="text-13 text-placeholder w-20">Small:</span>
|
||||
<div className="flex items-center justify-center min-w-[40px] h-8 bg-layer-1 border border-subtle rounded-sm">
|
||||
<AnimatedCounter count={count} size="sm" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-sm text-custom-text-400 w-20">Medium:</span>
|
||||
<div className="flex items-center justify-center min-w-[50px] h-10 bg-custom-background-80 border border-custom-border-200 rounded">
|
||||
<span className="text-13 text-placeholder w-20">Medium:</span>
|
||||
<div className="flex items-center justify-center min-w-[50px] h-10 bg-layer-1 border border-subtle rounded-sm">
|
||||
<AnimatedCounter count={count} size="md" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-sm text-custom-text-400 w-20">Large:</span>
|
||||
<div className="flex items-center justify-center min-w-[60px] h-12 bg-custom-background-80 border border-custom-border-200 rounded">
|
||||
<span className="text-13 text-placeholder w-20">Large:</span>
|
||||
<div className="flex items-center justify-center min-w-[60px] h-12 bg-layer-1 border border-subtle rounded-sm">
|
||||
<AnimatedCounter count={count} size="lg" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -99,19 +99,19 @@ export const LargeNumbers: Story = {
|
|||
<div className="space-y-6 p-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
className="px-3 py-1 bg-red-500 text-white text-sm rounded hover:bg-red-600"
|
||||
className="px-3 py-1 bg-red-500 text-on-color text-13 rounded-sm hover:bg-red-600"
|
||||
onClick={() => setCount((prev) => Math.max(0, prev - 1000))}
|
||||
>
|
||||
-1000
|
||||
</button>
|
||||
<button
|
||||
className="px-3 py-1 bg-green-500 text-white text-sm rounded hover:bg-green-600"
|
||||
className="px-3 py-1 bg-green-500 text-on-color text-13 rounded-sm hover:bg-green-600"
|
||||
onClick={() => setCount((prev) => prev + 1000)}
|
||||
>
|
||||
+1000
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center justify-center min-w-[100px] h-12 bg-custom-background-80 border border-custom-border-200 rounded-lg">
|
||||
<div className="flex items-center justify-center min-w-[100px] h-12 bg-layer-1 border border-subtle rounded-lg">
|
||||
<AnimatedCounter count={count} size="lg" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -142,11 +142,11 @@ export const Countdown: Story = {
|
|||
return (
|
||||
<div className="space-y-6 p-4">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="flex items-center justify-center min-w-[60px] h-16 bg-custom-background-80 border-2 border-custom-border-200 rounded-lg">
|
||||
<AnimatedCounter count={count} size="lg" className="text-2xl" />
|
||||
<div className="flex items-center justify-center min-w-[60px] h-16 bg-layer-1 border-2 border-subtle rounded-lg">
|
||||
<AnimatedCounter count={count} size="lg" className="text-20" />
|
||||
</div>
|
||||
<button
|
||||
className="px-6 py-2 bg-custom-primary-100 text-white font-medium rounded-lg hover:bg-custom-primary-200"
|
||||
className="px-6 py-2 bg-accent-primary text-on-color font-medium rounded-lg hover:bg-accent-primary/80"
|
||||
onClick={handleStart}
|
||||
disabled={isRunning}
|
||||
>
|
||||
|
|
@ -173,26 +173,26 @@ export const LiveCounter: Story = {
|
|||
return (
|
||||
<div className="space-y-6 p-4">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="flex items-center justify-center min-w-[80px] h-16 bg-custom-background-80 border-2 border-custom-border-200 rounded-lg">
|
||||
<AnimatedCounter count={count} size="lg" className="text-2xl" />
|
||||
<div className="flex items-center justify-center min-w-[80px] h-16 bg-layer-1 border-2 border-subtle rounded-lg">
|
||||
<AnimatedCounter count={count} size="lg" className="text-20" />
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
className="px-4 py-2 bg-green-500 text-white font-medium rounded hover:bg-green-600"
|
||||
className="px-4 py-2 bg-green-500 text-on-color font-medium rounded-sm hover:bg-green-600"
|
||||
onClick={() => setIsRunning(true)}
|
||||
disabled={isRunning}
|
||||
>
|
||||
Start
|
||||
</button>
|
||||
<button
|
||||
className="px-4 py-2 bg-red-500 text-white font-medium rounded hover:bg-red-600"
|
||||
className="px-4 py-2 bg-red-500 text-on-color font-medium rounded-sm hover:bg-red-600"
|
||||
onClick={() => setIsRunning(false)}
|
||||
disabled={!isRunning}
|
||||
>
|
||||
Stop
|
||||
</button>
|
||||
<button
|
||||
className="px-4 py-2 bg-gray-500 text-white font-medium rounded hover:bg-gray-600"
|
||||
className="px-4 py-2 bg-gray-500 text-on-color font-medium rounded-sm hover:bg-gray-600"
|
||||
onClick={() => {
|
||||
setIsRunning(false);
|
||||
setCount(0);
|
||||
|
|
@ -215,49 +215,49 @@ export const MultipleCounters: Story = {
|
|||
|
||||
return (
|
||||
<div className="space-y-6 p-4">
|
||||
<div className="max-w-md border border-custom-border-200 rounded-lg p-4">
|
||||
<div className="max-w-md border border-subtle rounded-lg p-4">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="font-medium">Engagement Stats</h3>
|
||||
</div>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex-1 flex flex-col items-center gap-2">
|
||||
<div className="text-custom-text-400 text-sm">Likes</div>
|
||||
<div className="text-placeholder text-13">Likes</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
className="w-8 h-8 flex items-center justify-center bg-custom-background-80 rounded hover:bg-custom-background-90"
|
||||
className="w-8 h-8 flex items-center justify-center bg-layer-1 rounded-sm hover:bg-surface-2"
|
||||
onClick={() => setLikes((prev) => prev + 1)}
|
||||
>
|
||||
+
|
||||
</button>
|
||||
<div className="flex items-center justify-center min-w-[40px] h-10 bg-custom-background-80 border border-custom-border-200 rounded">
|
||||
<div className="flex items-center justify-center min-w-[40px] h-10 bg-layer-1 border border-subtle rounded-sm">
|
||||
<AnimatedCounter count={likes} size="md" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 flex flex-col items-center gap-2">
|
||||
<div className="text-custom-text-400 text-sm">Comments</div>
|
||||
<div className="text-placeholder text-13">Comments</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
className="w-8 h-8 flex items-center justify-center bg-custom-background-80 rounded hover:bg-custom-background-90"
|
||||
className="w-8 h-8 flex items-center justify-center bg-layer-1 rounded-sm hover:bg-surface-2"
|
||||
onClick={() => setComments((prev) => prev + 1)}
|
||||
>
|
||||
+
|
||||
</button>
|
||||
<div className="flex items-center justify-center min-w-[40px] h-10 bg-custom-background-80 border border-custom-border-200 rounded">
|
||||
<div className="flex items-center justify-center min-w-[40px] h-10 bg-layer-1 border border-subtle rounded-sm">
|
||||
<AnimatedCounter count={comments} size="md" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 flex flex-col items-center gap-2">
|
||||
<div className="text-custom-text-400 text-sm">Shares</div>
|
||||
<div className="text-placeholder text-13">Shares</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
className="w-8 h-8 flex items-center justify-center bg-custom-background-80 rounded hover:bg-custom-background-90"
|
||||
className="w-8 h-8 flex items-center justify-center bg-layer-1 rounded-sm hover:bg-surface-2"
|
||||
onClick={() => setShares((prev) => prev + 1)}
|
||||
>
|
||||
+
|
||||
</button>
|
||||
<div className="flex items-center justify-center min-w-[40px] h-10 bg-custom-background-80 border border-custom-border-200 rounded">
|
||||
<div className="flex items-center justify-center min-w-[40px] h-10 bg-layer-1 border border-subtle rounded-sm">
|
||||
<AnimatedCounter count={shares} size="md" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -277,15 +277,13 @@ export const InBadge: Story = {
|
|||
<div className="space-y-6 p-4">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="relative">
|
||||
<button className="px-4 py-2 bg-custom-background-80 border border-custom-border-200 rounded-lg">
|
||||
Notifications
|
||||
</button>
|
||||
<div className="absolute -top-2 -right-2 min-w-[24px] h-6 flex items-center justify-center bg-red-500 text-white rounded-full px-1.5">
|
||||
<AnimatedCounter count={notifications} size="sm" className="text-xs font-medium" />
|
||||
<button className="px-4 py-2 bg-layer-1 border border-subtle rounded-lg">Notifications</button>
|
||||
<div className="absolute -top-2 -right-2 min-w-[24px] h-6 flex items-center justify-center bg-red-500 text-on-color rounded-full px-1.5">
|
||||
<AnimatedCounter count={notifications} size="sm" className="text-11 font-medium" />
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="px-4 py-2 bg-custom-primary-100 text-white rounded hover:bg-custom-primary-200"
|
||||
className="px-4 py-2 bg-accent-primary text-on-color rounded-sm hover:bg-accent-primary/80"
|
||||
onClick={() => setNotifications((prev) => prev + 1)}
|
||||
>
|
||||
Add Notification
|
||||
|
|
@ -309,20 +307,17 @@ export const FastAnimation: Story = {
|
|||
return (
|
||||
<div className="space-y-6 p-4">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="flex items-center justify-center min-w-[60px] h-12 bg-custom-background-80 border border-custom-border-200 rounded-lg">
|
||||
<div className="flex items-center justify-center min-w-[60px] h-12 bg-layer-1 border border-subtle rounded-lg">
|
||||
<AnimatedCounter count={count} size="lg" />
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
className="px-4 py-2 bg-custom-primary-100 text-white rounded hover:bg-custom-primary-200"
|
||||
className="px-4 py-2 bg-accent-primary text-on-color rounded-sm hover:bg-accent-primary/80"
|
||||
onClick={incrementFast}
|
||||
>
|
||||
+10 Fast
|
||||
</button>
|
||||
<button
|
||||
className="px-4 py-2 bg-custom-background-80 rounded hover:bg-custom-background-90"
|
||||
onClick={() => setCount(0)}
|
||||
>
|
||||
<button className="px-4 py-2 bg-layer-1 rounded-sm hover:bg-surface-2" onClick={() => setCount(0)}>
|
||||
Reset
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ export interface AnimatedCounterProps {
|
|||
}
|
||||
|
||||
const sizeClasses = {
|
||||
sm: "text-xs",
|
||||
md: "text-sm",
|
||||
lg: "text-base",
|
||||
sm: "text-11",
|
||||
md: "text-13",
|
||||
lg: "text-14",
|
||||
};
|
||||
|
||||
export function AnimatedCounter({ count, className, size = "md" }: AnimatedCounterProps) {
|
||||
|
|
@ -51,17 +51,15 @@ export function AnimatedCounter({ count, className, size = "md" }: AnimatedCount
|
|||
key={`prev-${animationKey}`}
|
||||
className={cn(
|
||||
"absolute inset-0 flex items-center justify-center font-medium",
|
||||
"animate-[slideOut_0.25s_ease-out_forwards]",
|
||||
"animate-slide-out",
|
||||
direction === "up" && "[--slide-out-dir:-100%]",
|
||||
direction === "down" && "[--slide-out-dir:100%]",
|
||||
sizeClass
|
||||
sizeClass,
|
||||
{
|
||||
"animate-slide-out animate-fade-out": isAnimating && direction === "up",
|
||||
"animate-slide-out-down animate-fade-out": isAnimating && direction === "down",
|
||||
}
|
||||
)}
|
||||
style={{
|
||||
animation:
|
||||
direction === "up"
|
||||
? "slideOut 0.25s ease-out forwards, fadeOut 0.25s ease-out forwards"
|
||||
: "slideOutDown 0.25s ease-out forwards, fadeOut 0.25s ease-out forwards",
|
||||
}}
|
||||
>
|
||||
{prevCount}
|
||||
</span>
|
||||
|
|
@ -72,21 +70,14 @@ export function AnimatedCounter({ count, className, size = "md" }: AnimatedCount
|
|||
key={`current-${animationKey}`}
|
||||
className={cn(
|
||||
"flex items-center justify-center font-medium",
|
||||
isAnimating && "animate-[slideIn_0.25s_ease-out_forwards]",
|
||||
!isAnimating && "opacity-100",
|
||||
sizeClass,
|
||||
{
|
||||
"animate-slide-in-from-bottom": isAnimating && direction === "up",
|
||||
"animate-slide-in-from-top": isAnimating && direction === "down",
|
||||
},
|
||||
className
|
||||
)}
|
||||
style={
|
||||
isAnimating
|
||||
? {
|
||||
animation:
|
||||
direction === "up"
|
||||
? "slideInFromBottom 0.25s ease-out forwards"
|
||||
: "slideInFromTop 0.25s ease-out forwards",
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{displayCount}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -26,31 +26,31 @@ export const getSizeInfo = (size: TAvatarSize) => {
|
|||
case "sm":
|
||||
return {
|
||||
avatarSize: "h-4 w-4",
|
||||
fontSize: "text-xs",
|
||||
fontSize: "text-11",
|
||||
spacing: "-space-x-1",
|
||||
};
|
||||
case "md":
|
||||
return {
|
||||
avatarSize: "h-5 w-5",
|
||||
fontSize: "text-xs",
|
||||
fontSize: "text-11",
|
||||
spacing: "-space-x-1",
|
||||
};
|
||||
case "base":
|
||||
return {
|
||||
avatarSize: "h-6 w-6",
|
||||
fontSize: "text-sm",
|
||||
fontSize: "text-13",
|
||||
spacing: "-space-x-1.5",
|
||||
};
|
||||
case "lg":
|
||||
return {
|
||||
avatarSize: "h-7 w-7",
|
||||
fontSize: "text-sm",
|
||||
fontSize: "text-13",
|
||||
spacing: "-space-x-1.5",
|
||||
};
|
||||
default:
|
||||
return {
|
||||
avatarSize: "h-5 w-5",
|
||||
fontSize: "text-xs",
|
||||
fontSize: "text-11",
|
||||
spacing: "-space-x-1",
|
||||
};
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ export const getBorderRadius = (shape: "circle" | "square") => {
|
|||
case "circle":
|
||||
return "rounded-full";
|
||||
case "square":
|
||||
return "rounded";
|
||||
return "rounded-sm";
|
||||
default:
|
||||
return "rounded-full";
|
||||
}
|
||||
|
|
|
|||
148
packages/propel/src/badge/badge.stories.tsx
Normal file
148
packages/propel/src/badge/badge.stories.tsx
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { Badge } from "./badge";
|
||||
|
||||
const meta = {
|
||||
title: "Components/Badge",
|
||||
component: Badge,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
tags: ["autodocs"],
|
||||
args: {
|
||||
children: "Badge",
|
||||
},
|
||||
} satisfies Meta<typeof Badge>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
export const Neutral: Story = {
|
||||
args: {
|
||||
variant: "neutral",
|
||||
children: "Neutral Badge",
|
||||
},
|
||||
};
|
||||
|
||||
export const Brand: Story = {
|
||||
args: {
|
||||
variant: "brand",
|
||||
children: "Brand Badge",
|
||||
},
|
||||
};
|
||||
|
||||
export const Warning: Story = {
|
||||
args: {
|
||||
variant: "warning",
|
||||
children: "Warning Badge",
|
||||
},
|
||||
};
|
||||
|
||||
export const Success: Story = {
|
||||
args: {
|
||||
variant: "success",
|
||||
children: "Success Badge",
|
||||
},
|
||||
};
|
||||
|
||||
export const Danger: Story = {
|
||||
args: {
|
||||
variant: "danger",
|
||||
children: "Danger Badge",
|
||||
},
|
||||
};
|
||||
|
||||
export const Small: Story = {
|
||||
args: {
|
||||
size: "sm",
|
||||
children: "Small Badge",
|
||||
},
|
||||
};
|
||||
|
||||
export const Base: Story = {
|
||||
args: {
|
||||
size: "base",
|
||||
children: "Base Badge",
|
||||
},
|
||||
};
|
||||
|
||||
export const Large: Story = {
|
||||
args: {
|
||||
size: "lg",
|
||||
children: "Large Badge",
|
||||
},
|
||||
};
|
||||
|
||||
export const WithPrependIcon: Story = {
|
||||
args: {
|
||||
prependIcon: (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M12 5v14m-7-7h14" />
|
||||
</svg>
|
||||
),
|
||||
children: "With Prepend Icon",
|
||||
},
|
||||
};
|
||||
|
||||
export const WithAppendIcon: Story = {
|
||||
args: {
|
||||
appendIcon: (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
),
|
||||
children: "With Append Icon",
|
||||
},
|
||||
};
|
||||
|
||||
export const AllVariants: Story = {
|
||||
render() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-16 font-semibold">Primary Variants</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Badge variant="neutral">Neutral</Badge>
|
||||
<Badge variant="brand">Brand</Badge>
|
||||
<Badge variant="warning">Warning</Badge>
|
||||
<Badge variant="success">Success</Badge>
|
||||
<Badge variant="danger">Danger</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const AllSizes: Story = {
|
||||
render() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge size="sm">Small</Badge>
|
||||
<Badge size="base">Base</Badge>
|
||||
<Badge size="lg">Large</Badge>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
22
packages/propel/src/badge/badge.tsx
Normal file
22
packages/propel/src/badge/badge.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import * as React from "react";
|
||||
import { cn } from "../utils";
|
||||
import type { BadgeProps } from "./helper";
|
||||
import { getBadgeIconStyling, badgeVariants } from "./helper";
|
||||
|
||||
const Badge = React.forwardRef(function Badge(props: BadgeProps, ref: React.ForwardedRef<HTMLSpanElement>) {
|
||||
const { variant = "neutral", size = "base", prependIcon = null, appendIcon = null, children, ...rest } = props;
|
||||
|
||||
const badgeIconStyle = getBadgeIconStyling(size ?? "base");
|
||||
|
||||
return (
|
||||
<span ref={ref} className={cn(badgeVariants({ variant, size }))} {...rest}>
|
||||
{prependIcon && React.cloneElement(prependIcon, { className: cn("shrink-0", badgeIconStyle), strokeWidth: 2 })}
|
||||
{children}
|
||||
{appendIcon && React.cloneElement(appendIcon, { className: cn("shrink-0", badgeIconStyle), strokeWidth: 2 })}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
|
||||
Badge.displayName = "plane-ui-badge";
|
||||
|
||||
export { Badge };
|
||||
46
packages/propel/src/badge/helper.tsx
Normal file
46
packages/propel/src/badge/helper.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import type { VariantProps } from "class-variance-authority";
|
||||
import { cva } from "class-variance-authority";
|
||||
|
||||
export const badgeVariants = cva("inline-flex items-center justify-center gap-1 whitespace-nowrap transition-colors", {
|
||||
variants: {
|
||||
variant: {
|
||||
neutral: "bg-layer-3 text-tertiary",
|
||||
brand: "bg-accent-subtle-hover text-accent-primary",
|
||||
warning: "bg-warning-subtle text-warning",
|
||||
success: "bg-success-subtle-1 text-success",
|
||||
danger: "bg-danger-subtle text-danger",
|
||||
},
|
||||
size: {
|
||||
sm: "h-4 px-1 text-caption-sm-medium rounded-sm",
|
||||
base: "h-5 px-1.5 text-caption-sm-medium rounded-md",
|
||||
lg: "h-6 px-2 text-caption-md-medium rounded-md",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "neutral",
|
||||
size: "base",
|
||||
},
|
||||
});
|
||||
|
||||
export type BadgeProps = Omit<React.HTMLAttributes<HTMLSpanElement>, "className"> &
|
||||
VariantProps<typeof badgeVariants> & {
|
||||
appendIcon?: React.ReactElement;
|
||||
prependIcon?: React.ReactElement;
|
||||
};
|
||||
|
||||
export type TBadgeVariant = NonNullable<BadgeProps["variant"]>;
|
||||
export type TBadgeSize = NonNullable<BadgeProps["size"]>;
|
||||
|
||||
const badgeIconStyling: Record<TBadgeSize, string> = {
|
||||
sm: "size-3.5",
|
||||
base: "size-3.5",
|
||||
lg: "size-4",
|
||||
};
|
||||
|
||||
export function getBadgeIconStyling(size: TBadgeSize): string {
|
||||
return badgeIconStyling[size];
|
||||
}
|
||||
|
||||
export function getBadgeStyling(variant: TBadgeVariant, size: TBadgeSize): string {
|
||||
return badgeVariants({ variant, size });
|
||||
}
|
||||
3
packages/propel/src/badge/index.ts
Normal file
3
packages/propel/src/badge/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export { Badge } from "./badge";
|
||||
export { getBadgeStyling } from "./helper";
|
||||
export type { BadgeProps, TBadgeVariant, TBadgeSize } from "./helper";
|
||||
|
|
@ -116,7 +116,7 @@ function CloseButton({ onClick }: { onClick?: () => void }) {
|
|||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className="rounded p-1 hover:bg-black/5 dark:hover:bg-white/5 transition-colors"
|
||||
className="rounded-sm p-1 hover:bg-black/5 dark:hover:bg-white/5 transition-colors"
|
||||
aria-label="Dismiss"
|
||||
>
|
||||
<svg
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export interface IBannerStyling {
|
|||
export const bannerSizeStyling = {
|
||||
container: "py-3 px-6 h-12",
|
||||
icon: "w-5 h-5",
|
||||
title: "text-sm",
|
||||
title: "text-13",
|
||||
action: "gap-2",
|
||||
};
|
||||
|
||||
|
|
@ -33,14 +33,14 @@ export const getBannerStyling = (variant: TBannerVariant): string => {
|
|||
|
||||
// Get title styling
|
||||
export const getBannerTitleStyling = (): string =>
|
||||
`font-medium text-custom-text-200 flex-1 min-w-0 ${bannerSizeStyling.title}`;
|
||||
`font-medium text-secondary flex-1 min-w-0 ${bannerSizeStyling.title}`;
|
||||
|
||||
// Get action container styling
|
||||
export const getBannerActionStyling = (): string => `flex items-center flex-shrink-0 ${bannerSizeStyling.action}`;
|
||||
|
||||
// Get dismiss button styling
|
||||
export const getBannerDismissStyling = (): string =>
|
||||
"rounded p-1 hover:bg-custom-background-90 transition-colors flex-shrink-0";
|
||||
"rounded-sm p-1 hover:bg-surface-2 transition-colors flex-shrink-0";
|
||||
|
||||
// Get dismiss icon styling
|
||||
export const getBannerDismissIconStyling = (): string => "text-custom-text-200";
|
||||
export const getBannerDismissIconStyling = (): string => "text-secondary";
|
||||
|
|
|
|||
|
|
@ -25,73 +25,45 @@ export const Primary: Story = {
|
|||
},
|
||||
};
|
||||
|
||||
export const AccentPrimary: Story = {
|
||||
export const ErrorFill: Story = {
|
||||
args: {
|
||||
variant: "accent-primary",
|
||||
children: "Accent Primary Button",
|
||||
variant: "error-fill",
|
||||
children: "Error Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const OutlinePrimary: Story = {
|
||||
export const ErrorOutline: Story = {
|
||||
args: {
|
||||
variant: "outline-primary",
|
||||
children: "Outline Primary Button",
|
||||
variant: "error-outline",
|
||||
children: "Error Outline Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const NeutralPrimary: Story = {
|
||||
export const Secondary: Story = {
|
||||
args: {
|
||||
variant: "neutral-primary",
|
||||
children: "Neutral Primary Button",
|
||||
variant: "secondary",
|
||||
children: "Secondary Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const LinkPrimary: Story = {
|
||||
export const Tertiary: Story = {
|
||||
args: {
|
||||
variant: "link-primary",
|
||||
children: "Link Primary Button",
|
||||
variant: "tertiary",
|
||||
children: "Tertiary Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const Danger: Story = {
|
||||
export const Ghost: Story = {
|
||||
args: {
|
||||
variant: "danger",
|
||||
children: "Danger Button",
|
||||
variant: "ghost",
|
||||
children: "Ghost Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const AccentDanger: Story = {
|
||||
export const Link: Story = {
|
||||
args: {
|
||||
variant: "accent-danger",
|
||||
children: "Accent Danger Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const OutlineDanger: Story = {
|
||||
args: {
|
||||
variant: "outline-danger",
|
||||
children: "Outline Danger Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const LinkDanger: Story = {
|
||||
args: {
|
||||
variant: "link-danger",
|
||||
children: "Link Danger Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const TertiaryDanger: Story = {
|
||||
args: {
|
||||
variant: "tertiary-danger",
|
||||
children: "Tertiary Danger Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const LinkNeutral: Story = {
|
||||
args: {
|
||||
variant: "link-neutral",
|
||||
children: "Link Neutral Button",
|
||||
variant: "link",
|
||||
children: "Link Button",
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -102,10 +74,10 @@ export const Small: Story = {
|
|||
},
|
||||
};
|
||||
|
||||
export const Medium: Story = {
|
||||
export const Base: Story = {
|
||||
args: {
|
||||
size: "md",
|
||||
children: "Medium Button",
|
||||
size: "base",
|
||||
children: "Base Button",
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -182,29 +154,15 @@ export const AllVariants: Story = {
|
|||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-lg font-semibold">Primary Variants</h3>
|
||||
<h3 className="text-16 font-semibold">Primary Variants</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button variant="primary">Primary</Button>
|
||||
<Button variant="accent-primary">Accent Primary</Button>
|
||||
<Button variant="outline-primary">Outline Primary</Button>
|
||||
<Button variant="neutral-primary">Neutral Primary</Button>
|
||||
<Button variant="link-primary">Link Primary</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-lg font-semibold">Danger Variants</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button variant="danger">Danger</Button>
|
||||
<Button variant="accent-danger">Accent Danger</Button>
|
||||
<Button variant="outline-danger">Outline Danger</Button>
|
||||
<Button variant="link-danger">Link Danger</Button>
|
||||
<Button variant="tertiary-danger">Tertiary Danger</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-lg font-semibold">Other Variants</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button variant="link-neutral">Link Neutral</Button>
|
||||
<Button variant="error-fill">Error Fill</Button>
|
||||
<Button variant="error-outline">Error Outline</Button>
|
||||
<Button variant="secondary">Secondary</Button>
|
||||
<Button variant="tertiary">Tertiary</Button>
|
||||
<Button variant="ghost">Ghost</Button>
|
||||
<Button variant="link">Link</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -218,7 +176,7 @@ export const AllSizes: Story = {
|
|||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Button size="sm">Small</Button>
|
||||
<Button size="md">Medium</Button>
|
||||
<Button size="base">Base</Button>
|
||||
<Button size="lg">Large</Button>
|
||||
<Button size="xl">Extra Large</Button>
|
||||
</div>
|
||||
|
|
@ -232,7 +190,7 @@ export const AllStates: Story = {
|
|||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-lg font-semibold">Button States</h3>
|
||||
<h3 className="text-16 font-semibold">Button States</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button>Default</Button>
|
||||
<Button loading>Loading</Button>
|
||||
|
|
|
|||
|
|
@ -1,23 +1,12 @@
|
|||
import * as React from "react";
|
||||
import { cn } from "../utils";
|
||||
import type { TButtonVariant, TButtonSizes } from "./helper";
|
||||
import { getIconStyling, getButtonStyling } from "./helper";
|
||||
|
||||
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
variant?: TButtonVariant;
|
||||
size?: TButtonSizes;
|
||||
className?: string;
|
||||
loading?: boolean;
|
||||
disabled?: boolean;
|
||||
appendIcon?: any;
|
||||
prependIcon?: any;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
import type { ButtonProps } from "./helper";
|
||||
import { getIconStyling, buttonVariants } from "./helper";
|
||||
|
||||
const Button = React.forwardRef(function Button(props: ButtonProps, ref: React.ForwardedRef<HTMLButtonElement>) {
|
||||
const {
|
||||
variant = "primary",
|
||||
size = "md",
|
||||
size = "base",
|
||||
className = "",
|
||||
type = "button",
|
||||
loading = false,
|
||||
|
|
@ -28,14 +17,19 @@ const Button = React.forwardRef(function Button(props: ButtonProps, ref: React.F
|
|||
...rest
|
||||
} = props;
|
||||
|
||||
const buttonStyle = getButtonStyling(variant, size, disabled || loading);
|
||||
const buttonIconStyle = getIconStyling(size);
|
||||
const buttonIconStyle = getIconStyling(size ?? "base");
|
||||
|
||||
return (
|
||||
<button ref={ref} type={type} className={cn(buttonStyle, className)} disabled={disabled || loading} {...rest}>
|
||||
{prependIcon && <div className={buttonIconStyle}>{React.cloneElement(prependIcon, { strokeWidth: 2 })}</div>}
|
||||
<button
|
||||
ref={ref}
|
||||
type={type}
|
||||
className={cn(buttonVariants({ variant, size }), className)}
|
||||
disabled={disabled || loading}
|
||||
{...rest}
|
||||
>
|
||||
{prependIcon && React.cloneElement(prependIcon, { className: cn("shrink-0", buttonIconStyle), strokeWidth: 2 })}
|
||||
{children}
|
||||
{appendIcon && <div className={buttonIconStyle}>{React.cloneElement(appendIcon, { strokeWidth: 2 })}</div>}
|
||||
{appendIcon && React.cloneElement(appendIcon, { className: cn("shrink-0", buttonIconStyle), strokeWidth: 2 })}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,126 +1,60 @@
|
|||
export type TButtonVariant =
|
||||
| "primary"
|
||||
| "accent-primary"
|
||||
| "outline-primary"
|
||||
| "neutral-primary"
|
||||
| "link-primary"
|
||||
| "danger"
|
||||
| "accent-danger"
|
||||
| "outline-danger"
|
||||
| "link-danger"
|
||||
| "tertiary-danger"
|
||||
| "link-neutral";
|
||||
import type { VariantProps } from "class-variance-authority";
|
||||
import { cva } from "class-variance-authority";
|
||||
|
||||
export type TButtonSizes = "sm" | "md" | "lg" | "xl";
|
||||
export const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-1 whitespace-nowrap transition-colors focus-visible:outline-none disabled:pointer-events-none",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
primary:
|
||||
"bg-accent-primary hover:bg-accent-primary-hover active:bg-accent-primary-active focus:bg-accent-primary-active disabled:bg-layer-disabled text-on-color disabled:text-disabled",
|
||||
"error-fill":
|
||||
"bg-danger-primary hover:bg-danger-primary-hover active:bg-danger-primary-active focus:bg-danger-primary-active disabled:bg-layer-disabled text-on-color disabled:text-disabled",
|
||||
"error-outline":
|
||||
"bg-layer-2 hover:bg-danger-subtle active:bg-danger-subtle-hover focus:bg-danger-subtle-hover disabled:bg-layer-2 text-danger disabled:text-disabled border border-danger-strong disabled:border-subtle-1",
|
||||
secondary:
|
||||
"bg-layer-2 hover:bg-layer-2-hover active:bg-layer-2-active focus:bg-layer-2-active disabled:bg-layer-transparent text-secondary disabled:text-disabled border border-strong disabled:border-subtle-1 shadow-raised-100",
|
||||
tertiary:
|
||||
"bg-layer-3 hover:bg-layer-3-hover active:bg-layer-3-active focus:bg-layer-3-active disabled:bg-layer-transparent text-secondary disabled:text-disabled",
|
||||
ghost:
|
||||
"bg-layer-transparent hover:bg-layer-transparent-hover active:bg-layer-transparent-active focus:bg-layer-transparent-active disabled:bg-layer-transparent text-secondary disabled:text-disabled",
|
||||
link: "px-0 underline text-link-primary hover:text-link-primary-hover active:text-link-primary-hover focus:text-link-primary-hover disabled:text-disabled",
|
||||
},
|
||||
size: {
|
||||
sm: "h-5 px-1.5 text-caption-md-medium rounded-sm",
|
||||
base: "h-6 px-2 text-body-xs-medium rounded-md",
|
||||
lg: "h-7 px-2 text-body-xs-medium rounded-md",
|
||||
xl: "h-8 px-2 text-body-sm-medium rounded-md",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "primary",
|
||||
size: "base",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export interface IButtonStyling {
|
||||
[key: string]: {
|
||||
default: string;
|
||||
hover: string;
|
||||
pressed: string;
|
||||
disabled: string;
|
||||
export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
|
||||
VariantProps<typeof buttonVariants> & {
|
||||
appendIcon?: React.ReactElement;
|
||||
loading?: boolean;
|
||||
prependIcon?: React.ReactElement;
|
||||
};
|
||||
}
|
||||
|
||||
enum buttonSizeStyling {
|
||||
sm = `px-3 py-1.5 font-medium text-xs rounded flex items-center gap-1.5 whitespace-nowrap transition-all justify-center`,
|
||||
md = `px-4 py-1.5 font-medium text-sm rounded flex items-center gap-1.5 whitespace-nowrap transition-all justify-center`,
|
||||
lg = `px-5 py-2 font-medium text-sm rounded flex items-center gap-1.5 whitespace-nowrap transition-all justify-center`,
|
||||
xl = `px-5 py-3.5 font-medium text-sm rounded flex items-center gap-1.5 whitespace-nowrap transition-all justify-center`,
|
||||
}
|
||||
export type TButtonVariant = NonNullable<ButtonProps["variant"]>;
|
||||
export type TButtonSize = NonNullable<ButtonProps["size"]>;
|
||||
|
||||
enum buttonIconStyling {
|
||||
sm = "h-3 w-3 flex justify-center items-center overflow-hidden my-0.5 flex-shrink-0",
|
||||
md = "h-3.5 w-3.5 flex justify-center items-center overflow-hidden my-0.5 flex-shrink-0",
|
||||
lg = "h-4 w-4 flex justify-center items-center overflow-hidden my-0.5 flex-shrink-0",
|
||||
xl = "h-4 w-4 flex justify-center items-center overflow-hidden my-0.5 flex-shrink-0 ",
|
||||
}
|
||||
|
||||
export const buttonStyling: IButtonStyling = {
|
||||
primary: {
|
||||
default: `text-white bg-custom-primary-100`,
|
||||
hover: `hover:bg-custom-primary-200`,
|
||||
pressed: `focus:text-custom-brand-40 focus:bg-custom-primary-200`,
|
||||
disabled: `cursor-not-allowed !bg-custom-primary-60 hover:bg-custom-primary-60`,
|
||||
},
|
||||
"accent-primary": {
|
||||
default: `bg-custom-primary-100/20 text-custom-primary-100`,
|
||||
hover: `hover:bg-custom-primary-100/10 hover:text-custom-primary-200`,
|
||||
pressed: `focus:bg-custom-primary-100/10`,
|
||||
disabled: `cursor-not-allowed !text-custom-primary-60`,
|
||||
},
|
||||
"outline-primary": {
|
||||
default: `text-custom-primary-100 bg-transparent border border-custom-primary-100`,
|
||||
hover: `hover:bg-custom-primary-100/20`,
|
||||
pressed: `focus:text-custom-primary-100 focus:bg-custom-primary-100/30`,
|
||||
disabled: `cursor-not-allowed !text-custom-primary-60 !border-custom-primary-60 `,
|
||||
},
|
||||
"neutral-primary": {
|
||||
default: `text-custom-text-200 bg-custom-background-100 border border-custom-border-200`,
|
||||
hover: `hover:bg-custom-background-90`,
|
||||
pressed: `focus:text-custom-text-300 focus:bg-custom-background-90`,
|
||||
disabled: `cursor-not-allowed !text-custom-text-400`,
|
||||
},
|
||||
"link-primary": {
|
||||
default: `text-custom-primary-100 bg-custom-background-100`,
|
||||
hover: `hover:text-custom-primary-200`,
|
||||
pressed: `focus:text-custom-primary-80 `,
|
||||
disabled: `cursor-not-allowed !text-custom-primary-60`,
|
||||
},
|
||||
|
||||
danger: {
|
||||
default: `text-white bg-red-500`,
|
||||
hover: ` hover:bg-red-600`,
|
||||
pressed: `focus:text-red-200 focus:bg-red-600`,
|
||||
disabled: `cursor-not-allowed !bg-red-300`,
|
||||
},
|
||||
"accent-danger": {
|
||||
default: `text-red-500 bg-red-50`,
|
||||
hover: `hover:text-red-600 hover:bg-red-100`,
|
||||
pressed: `focus:text-red-500 focus:bg-red-100`,
|
||||
disabled: `cursor-not-allowed !text-red-300`,
|
||||
},
|
||||
"outline-danger": {
|
||||
default: `text-red-500 bg-transparent border border-red-500`,
|
||||
hover: `hover:text-red-400 hover:border-red-400`,
|
||||
pressed: `focus:text-red-400 focus:border-red-400`,
|
||||
disabled: `cursor-not-allowed !text-red-300 !border-red-300`,
|
||||
},
|
||||
"link-danger": {
|
||||
default: `text-red-500 bg-custom-background-100`,
|
||||
hover: `hover:text-red-400`,
|
||||
pressed: `focus:text-red-400`,
|
||||
disabled: `cursor-not-allowed !text-red-300`,
|
||||
},
|
||||
"tertiary-danger": {
|
||||
default: `text-red-500 bg-custom-background-100 border border-red-200`,
|
||||
hover: `hover:bg-red-50 hover:border-red-300`,
|
||||
pressed: `focus:text-red-400`,
|
||||
disabled: `cursor-not-allowed !text-red-300`,
|
||||
},
|
||||
"link-neutral": {
|
||||
default: `text-custom-text-300`,
|
||||
hover: `hover:text-custom-text-200`,
|
||||
pressed: `focus:text-custom-text-100`,
|
||||
disabled: `cursor-not-allowed !text-custom-text-400`,
|
||||
},
|
||||
const buttonIconStyling: Record<TButtonSize, string> = {
|
||||
sm: "size-3.5",
|
||||
base: "size-3.5",
|
||||
lg: "size-4",
|
||||
xl: "size-4 ",
|
||||
};
|
||||
|
||||
export const getButtonStyling = (variant: TButtonVariant, size: TButtonSizes, disabled: boolean = false): string => {
|
||||
let tempVariant: string = ``;
|
||||
const currentVariant = buttonStyling[variant];
|
||||
export function getIconStyling(size: TButtonSize): string {
|
||||
return buttonIconStyling[size];
|
||||
}
|
||||
|
||||
tempVariant = `${currentVariant.default} ${disabled ? currentVariant.disabled : currentVariant.hover} ${
|
||||
currentVariant.pressed
|
||||
}`;
|
||||
|
||||
let tempSize: string = ``;
|
||||
if (size) tempSize = buttonSizeStyling[size];
|
||||
return `${tempVariant} ${tempSize}`;
|
||||
};
|
||||
|
||||
export const getIconStyling = (size: TButtonSizes): string => {
|
||||
let icon: string = ``;
|
||||
if (size) icon = buttonIconStyling[size];
|
||||
return icon;
|
||||
};
|
||||
export function getButtonStyling(variant: TButtonVariant, size: TButtonSize): string {
|
||||
return buttonVariants({ variant, size });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
export { Button } from "./button";
|
||||
export * from "./helper";
|
||||
export type { ButtonProps } from "./button";
|
||||
export { getButtonStyling } from "./helper";
|
||||
export type { ButtonProps, TButtonVariant, TButtonSize } from "./helper";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react";
|
||||
import { DayPicker } from "react-day-picker";
|
||||
import { ChevronLeftIcon } from "../icons";
|
||||
import { ChevronLeftIcon } from "../icons/arrows/chevron-left";
|
||||
|
||||
import { cn } from "../utils";
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ const meta = {
|
|||
args: {
|
||||
children: (
|
||||
<>
|
||||
<h3 className="text-lg font-semibold">Card Title</h3>
|
||||
<p className="text-sm text-gray-600">This is a default card with shadow and large spacing.</p>
|
||||
<h3 className="text-16 font-semibold">Card Title</h3>
|
||||
<p className="text-13 text-gray-600">This is a default card with shadow and large spacing.</p>
|
||||
</>
|
||||
),
|
||||
},
|
||||
|
|
@ -28,8 +28,8 @@ export const WithShadow: Story = {
|
|||
variant: ECardVariant.WITH_SHADOW,
|
||||
children: (
|
||||
<>
|
||||
<h3 className="text-lg font-semibold">Card with Shadow</h3>
|
||||
<p className="text-sm text-gray-600">Hover over this card to see the shadow effect.</p>
|
||||
<h3 className="text-16 font-semibold">Card with Shadow</h3>
|
||||
<p className="text-13 text-gray-600">Hover over this card to see the shadow effect.</p>
|
||||
</>
|
||||
),
|
||||
},
|
||||
|
|
@ -40,8 +40,8 @@ export const WithoutShadow: Story = {
|
|||
variant: ECardVariant.WITHOUT_SHADOW,
|
||||
children: (
|
||||
<>
|
||||
<h3 className="text-lg font-semibold">Card without Shadow</h3>
|
||||
<p className="text-sm text-gray-600">This card has no shadow effect on hover.</p>
|
||||
<h3 className="text-16 font-semibold">Card without Shadow</h3>
|
||||
<p className="text-13 text-gray-600">This card has no shadow effect on hover.</p>
|
||||
</>
|
||||
),
|
||||
},
|
||||
|
|
@ -52,8 +52,8 @@ export const SmallSpacing: Story = {
|
|||
spacing: ECardSpacing.SM,
|
||||
children: (
|
||||
<>
|
||||
<h3 className="text-lg font-semibold">Small Spacing</h3>
|
||||
<p className="text-sm text-gray-600">This card uses small spacing (p-4).</p>
|
||||
<h3 className="text-16 font-semibold">Small Spacing</h3>
|
||||
<p className="text-13 text-gray-600">This card uses small spacing (p-4).</p>
|
||||
</>
|
||||
),
|
||||
},
|
||||
|
|
@ -64,8 +64,8 @@ export const LargeSpacing: Story = {
|
|||
spacing: ECardSpacing.LG,
|
||||
children: (
|
||||
<>
|
||||
<h3 className="text-lg font-semibold">Large Spacing</h3>
|
||||
<p className="text-sm text-gray-600">This card uses large spacing (p-6).</p>
|
||||
<h3 className="text-16 font-semibold">Large Spacing</h3>
|
||||
<p className="text-13 text-gray-600">This card uses large spacing (p-6).</p>
|
||||
</>
|
||||
),
|
||||
},
|
||||
|
|
@ -76,9 +76,9 @@ export const ColumnDirection: Story = {
|
|||
direction: ECardDirection.COLUMN,
|
||||
children: (
|
||||
<>
|
||||
<h3 className="text-lg font-semibold">Column Direction</h3>
|
||||
<p className="text-sm text-gray-600">Content is arranged vertically.</p>
|
||||
<button className="rounded bg-blue-500 px-4 py-2 text-white">Action</button>
|
||||
<h3 className="text-16 font-semibold">Column Direction</h3>
|
||||
<p className="text-13 text-gray-600">Content is arranged vertically.</p>
|
||||
<button className="rounded-sm bg-blue-500 px-4 py-2 text-on-color">Action</button>
|
||||
</>
|
||||
),
|
||||
},
|
||||
|
|
@ -90,11 +90,11 @@ export const RowDirection: Story = {
|
|||
children: (
|
||||
<>
|
||||
<div className="flex-shrink-0">
|
||||
<div className="h-12 w-12 rounded bg-blue-500" />
|
||||
<div className="h-12 w-12 rounded-sm bg-blue-500" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-lg font-semibold">Row Direction</h3>
|
||||
<p className="text-sm text-gray-600">Content is arranged horizontally.</p>
|
||||
<h3 className="text-16 font-semibold">Row Direction</h3>
|
||||
<p className="text-13 text-gray-600">Content is arranged horizontally.</p>
|
||||
</div>
|
||||
</>
|
||||
),
|
||||
|
|
@ -108,12 +108,12 @@ export const ProductCard: Story = {
|
|||
direction: ECardDirection.COLUMN,
|
||||
children: (
|
||||
<>
|
||||
<div className="h-48 w-full rounded bg-gray-200" />
|
||||
<h3 className="text-xl font-bold">Product Name</h3>
|
||||
<p className="text-sm text-gray-600">A brief description of the product goes here.</p>
|
||||
<div className="h-48 w-full rounded-sm bg-gray-200" />
|
||||
<h3 className="text-18 font-bold">Product Name</h3>
|
||||
<p className="text-13 text-gray-600">A brief description of the product goes here.</p>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-lg font-semibold">$99.99</span>
|
||||
<button className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600">Add to Cart</button>
|
||||
<span className="text-16 font-semibold">$99.99</span>
|
||||
<button className="rounded-sm bg-blue-500 px-4 py-2 text-on-color hover:bg-blue-600">Add to Cart</button>
|
||||
</div>
|
||||
</>
|
||||
),
|
||||
|
|
@ -129,9 +129,9 @@ export const UserCard: Story = {
|
|||
<>
|
||||
<div className="h-16 w-16 flex-shrink-0 rounded-full bg-blue-500" />
|
||||
<div className="flex-1">
|
||||
<h3 className="text-lg font-semibold">John Doe</h3>
|
||||
<p className="text-sm text-gray-600">Software Engineer</p>
|
||||
<p className="text-xs text-gray-500">john.doe@example.com</p>
|
||||
<h3 className="text-16 font-semibold">John Doe</h3>
|
||||
<p className="text-13 text-gray-600">Software Engineer</p>
|
||||
<p className="text-11 text-gray-500">john.doe@example.com</p>
|
||||
</div>
|
||||
</>
|
||||
),
|
||||
|
|
@ -147,9 +147,9 @@ export const NotificationCard: Story = {
|
|||
<>
|
||||
<div className="flex items-start justify-between">
|
||||
<h4 className="font-semibold">New Message</h4>
|
||||
<span className="text-xs text-gray-500">2m ago</span>
|
||||
<span className="text-11 text-gray-500">2m ago</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600">You have received a new message from Alice.</p>
|
||||
<p className="text-13 text-gray-600">You have received a new message from Alice.</p>
|
||||
</>
|
||||
),
|
||||
},
|
||||
|
|
@ -161,11 +161,11 @@ export const AllVariants: Story = {
|
|||
<div className="space-y-4">
|
||||
<Card variant={ECardVariant.WITH_SHADOW}>
|
||||
<h3 className="font-semibold">With Shadow</h3>
|
||||
<p className="text-sm text-gray-600">Hover to see the shadow effect</p>
|
||||
<p className="text-13 text-gray-600">Hover to see the shadow effect</p>
|
||||
</Card>
|
||||
<Card variant={ECardVariant.WITHOUT_SHADOW}>
|
||||
<h3 className="font-semibold">Without Shadow</h3>
|
||||
<p className="text-sm text-gray-600">No shadow on hover</p>
|
||||
<p className="text-13 text-gray-600">No shadow on hover</p>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -178,11 +178,11 @@ export const AllSpacings: Story = {
|
|||
<div className="space-y-4">
|
||||
<Card spacing={ECardSpacing.SM}>
|
||||
<h3 className="font-semibold">Small Spacing (p-4)</h3>
|
||||
<p className="text-sm text-gray-600">Compact padding</p>
|
||||
<p className="text-13 text-gray-600">Compact padding</p>
|
||||
</Card>
|
||||
<Card spacing={ECardSpacing.LG}>
|
||||
<h3 className="font-semibold">Large Spacing (p-6)</h3>
|
||||
<p className="text-sm text-gray-600">More generous padding</p>
|
||||
<p className="text-13 text-gray-600">More generous padding</p>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -195,14 +195,14 @@ export const AllDirections: Story = {
|
|||
<div className="space-y-4">
|
||||
<Card direction={ECardDirection.COLUMN}>
|
||||
<h3 className="font-semibold">Column Direction</h3>
|
||||
<p className="text-sm text-gray-600">Vertical layout</p>
|
||||
<button className="w-fit rounded bg-blue-500 px-4 py-2 text-white">Button</button>
|
||||
<p className="text-13 text-gray-600">Vertical layout</p>
|
||||
<button className="w-fit rounded-sm bg-blue-500 px-4 py-2 text-on-color">Button</button>
|
||||
</Card>
|
||||
<Card direction={ECardDirection.ROW}>
|
||||
<div className="h-12 w-12 flex-shrink-0 rounded bg-blue-500" />
|
||||
<div className="h-12 w-12 flex-shrink-0 rounded-sm bg-blue-500" />
|
||||
<div>
|
||||
<h3 className="font-semibold">Row Direction</h3>
|
||||
<p className="text-sm text-gray-600">Horizontal layout</p>
|
||||
<p className="text-13 text-gray-600">Horizontal layout</p>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@ export interface ICardProperties {
|
|||
[key: string]: string;
|
||||
}
|
||||
|
||||
const DEFAULT_STYLE =
|
||||
"bg-custom-background-100 rounded-lg border-[0.5px] border-custom-border-200 w-full flex flex-col";
|
||||
const DEFAULT_STYLE = "bg-surface-1 rounded-lg border-[0.5px] border-subtle w-full flex flex-col";
|
||||
export const containerStyle: ICardProperties = {
|
||||
[ECardVariant.WITHOUT_SHADOW]: "",
|
||||
[ECardVariant.WITH_SHADOW]: "hover:shadow-custom-shadow-4xl duration-300",
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ export const AreaChart = React.memo(function AreaChart<K extends string, T exten
|
|||
left: margin?.left === undefined ? 20 : margin.left,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid stroke="rgba(var(--color-border-100), 0.8)" vertical={false} />
|
||||
<CartesianGrid stroke="--alpha(var(--border-color-subtle) / 80%)" vertical={false} />
|
||||
<XAxis
|
||||
dataKey={xAxis.key}
|
||||
tick={(props) => {
|
||||
|
|
@ -163,7 +163,7 @@ export const AreaChart = React.memo(function AreaChart<K extends string, T exten
|
|||
{showTooltip && (
|
||||
<Tooltip
|
||||
cursor={{
|
||||
stroke: "rgba(var(--color-text-300))",
|
||||
stroke: "var(--text-color-tertiary)",
|
||||
strokeDasharray: "4 4",
|
||||
}}
|
||||
wrapperStyle={{
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ function PercentageText({
|
|||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<text x={x} y={y} textAnchor="middle" className={cn("text-xs font-medium", className)} fill="currentColor">
|
||||
<text x={x} y={y} textAnchor="middle" className={cn("text-11 font-medium", className)} fill="currentColor">
|
||||
{percentage}%
|
||||
</text>
|
||||
);
|
||||
|
|
@ -176,7 +176,7 @@ export const barShapeVariants: Record<
|
|||
TBarChartShapeVariant,
|
||||
(props: TShapeProps, bar: TBarItem<string>, stackKeys: string[]) => React.ReactNode
|
||||
> = {
|
||||
bar: createShapeVariant(CustomBar), // Standard bar with rounded corners
|
||||
bar: createShapeVariant(CustomBar), // Standard bar with rounded-sm corners
|
||||
lollipop: createShapeVariant(CustomBarLollipop), // Line with circle at top
|
||||
"lollipop-dotted": createShapeVariant(CustomBarLollipop, { dotted: true }), // Dotted line lollipop variant
|
||||
};
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ export const BarChart = React.memo(function BarChart<K extends string, T extends
|
|||
barSize={barSize}
|
||||
className="recharts-wrapper"
|
||||
>
|
||||
<CartesianGrid stroke="rgba(var(--color-border-100), 0.8)" vertical={false} />
|
||||
<CartesianGrid stroke="--alpha(var(--border-color-subtle) / 80%)" vertical={false} />
|
||||
<XAxis
|
||||
dataKey={xAxis.key}
|
||||
tick={(props) => {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ const CustomLegend = React.forwardRef(function CustomLegend(
|
|||
{payload.map((item, index) => (
|
||||
<div
|
||||
key={item.value}
|
||||
className={cn("flex items-center gap-1.5 text-custom-text-300 text-sm font-medium whitespace-nowrap", {
|
||||
className={cn("flex items-center gap-1.5 text-tertiary text-13 font-medium whitespace-nowrap", {
|
||||
"px-2": layout === "horizontal",
|
||||
"py-2": layout === "vertical",
|
||||
"pl-0 pt-0": index === 0,
|
||||
|
|
@ -63,7 +63,7 @@ const CustomLegend = React.forwardRef(function CustomLegend(
|
|||
onMouseLeave={(e) => onMouseLeave?.(item, index, e)}
|
||||
>
|
||||
<div
|
||||
className="flex-shrink-0 size-2 rounded-sm"
|
||||
className="flex-shrink-0 size-2 rounded-xs"
|
||||
style={{
|
||||
backgroundColor: item.color,
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import React from "react";
|
||||
|
||||
// Common classnames
|
||||
const AXIS_TICK_CLASSNAME = "fill-custom-text-300 text-sm";
|
||||
const AXIS_TICK_CLASSNAME = "fill-tertiary text-13";
|
||||
|
||||
export const CustomXAxisTick = React.memo(function CustomXAxisTick({ x, y, payload, getLabel }: any) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -25,31 +25,29 @@ export const CustomTooltip = React.memo(function CustomTooltip(props: Props) {
|
|||
className="flex flex-col max-h-[40vh] w-[12rem] overflow-y-scroll vertical-scrollbar scrollbar-sm"
|
||||
spacing={ECardSpacing.SM}
|
||||
>
|
||||
<p className="flex-shrink-0 text-xs text-custom-text-100 font-medium border-b border-custom-border-200 pb-2 truncate">
|
||||
{label}
|
||||
</p>
|
||||
<p className="flex-shrink-0 text-11 text-primary font-medium border-b border-subtle pb-2 truncate">{label}</p>
|
||||
{filteredPayload.map((item) => {
|
||||
if (!item.dataKey) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={item?.dataKey}
|
||||
className={cn("flex items-center gap-2 text-xs transition-opacity", {
|
||||
className={cn("flex items-center gap-2 text-11 transition-opacity", {
|
||||
"opacity-20": activeKey && item.dataKey !== activeKey,
|
||||
})}
|
||||
>
|
||||
<div className="flex items-center gap-2 truncate">
|
||||
{itemDotColors[item?.dataKey] && (
|
||||
<div
|
||||
className="flex-shrink-0 size-2 rounded-sm"
|
||||
className="flex-shrink-0 size-2 rounded-xs"
|
||||
style={{
|
||||
backgroundColor: itemDotColors[item?.dataKey],
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span className="text-custom-text-300 truncate">{itemLabels[item?.dataKey]}:</span>
|
||||
<span className="text-tertiary truncate">{itemLabels[item?.dataKey]}:</span>
|
||||
</div>
|
||||
<span className="flex-shrink-0 font-medium text-custom-text-200">{item?.value}</span>
|
||||
<span className="flex-shrink-0 font-medium text-secondary">{item?.value}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ export const LineChart = React.memo(function LineChart<K extends string, T exten
|
|||
left: margin?.left === undefined ? 20 : margin.left,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid stroke="rgba(var(--color-border-100), 0.8)" vertical={false} />
|
||||
<CartesianGrid stroke="--alpha(var(--border-color-subtle) / 80%)" vertical={false} />
|
||||
<XAxis
|
||||
dataKey={xAxis.key}
|
||||
tick={(props) => {
|
||||
|
|
@ -149,7 +149,7 @@ export const LineChart = React.memo(function LineChart<K extends string, T exten
|
|||
{showTooltip && (
|
||||
<Tooltip
|
||||
cursor={{
|
||||
stroke: "rgba(var(--color-text-300))",
|
||||
stroke: "var(--text-color-tertiary)",
|
||||
strokeDasharray: "4 4",
|
||||
}}
|
||||
wrapperStyle={{
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ export const PieChart = React.memo(function PieChart<K extends string, T extends
|
|||
showLabel
|
||||
? ({ payload, ...props }) => (
|
||||
<text
|
||||
className="text-sm font-medium transition-opacity duration-200"
|
||||
className="text-13 font-medium transition-opacity duration-200"
|
||||
cx={props.cx}
|
||||
cy={props.cy}
|
||||
x={props.x}
|
||||
|
|
|
|||
|
|
@ -17,21 +17,19 @@ export const CustomPieChartTooltip = React.memo(function CustomPieChartTooltip(p
|
|||
className="flex flex-col max-h-[40vh] w-[12rem] overflow-y-scroll vertical-scrollbar scrollbar-sm"
|
||||
spacing={ECardSpacing.SM}
|
||||
>
|
||||
<p className="flex-shrink-0 text-xs text-custom-text-100 font-medium border-b border-custom-border-200 pb-2 truncate">
|
||||
{label}
|
||||
</p>
|
||||
<p className="flex-shrink-0 text-11 text-primary font-medium border-b border-subtle pb-2 truncate">{label}</p>
|
||||
{payload?.map((item) => (
|
||||
<div key={item?.dataKey} className="flex items-center gap-2 text-xs capitalize">
|
||||
<div key={item?.dataKey} className="flex items-center gap-2 text-11 capitalize">
|
||||
<div className="flex items-center gap-2 truncate">
|
||||
<div
|
||||
className="flex-shrink-0 size-2 rounded-sm"
|
||||
className="flex-shrink-0 size-2 rounded-xs"
|
||||
style={{
|
||||
backgroundColor: dotColor,
|
||||
}}
|
||||
/>
|
||||
<span className="text-custom-text-300 truncate">{item?.name}:</span>
|
||||
<span className="text-tertiary truncate">{item?.name}:</span>
|
||||
</div>
|
||||
<span className="flex-shrink-0 font-medium text-custom-text-200">{item?.value}</span>
|
||||
<span className="flex-shrink-0 font-medium text-secondary">{item?.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -37,12 +37,12 @@ function RadarChart<T extends string, K extends string>(props: TRadarChartProps<
|
|||
<div className={className}>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<CoreRadarChart cx="50%" cy="50%" outerRadius="80%" data={data} margin={margin}>
|
||||
<PolarGrid stroke="rgba(var(--color-border-100), 0.9)" />
|
||||
<PolarGrid stroke="--alpha(var(--border-color-subtle) / 90%)" />
|
||||
<PolarAngleAxis dataKey={angleAxis.key} tick={(props) => <CustomRadarAxisTick {...props} />} />
|
||||
{showTooltip && (
|
||||
<Tooltip
|
||||
cursor={{
|
||||
stroke: "rgba(var(--color-text-300))",
|
||||
stroke: "var(--text-color-tertiary)",
|
||||
strokeDasharray: "4 4",
|
||||
}}
|
||||
wrapperStyle={{
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ export const ScatterChart = React.memo(function ScatterChart<K extends string, T
|
|||
left: margin?.left === undefined ? 20 : margin.left,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid stroke="rgba(var(--color-border-100), 0.8)" vertical={false} />
|
||||
<CartesianGrid stroke="--alpha(var(--border-color-subtle) / 80%)" vertical={false} />
|
||||
<XAxis
|
||||
dataKey={xAxis.key}
|
||||
tick={(props) => {
|
||||
|
|
@ -133,7 +133,7 @@ export const ScatterChart = React.memo(function ScatterChart<K extends string, T
|
|||
{showTooltip && (
|
||||
<Tooltip
|
||||
cursor={{
|
||||
stroke: "rgba(var(--color-text-300))",
|
||||
stroke: "var(--text-color-tertiary)",
|
||||
strokeDasharray: "4 4",
|
||||
}}
|
||||
wrapperStyle={{
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ export function CustomTreeMapContent({
|
|||
y={pY + LAYOUT.TEXT.PADDING_LEFT}
|
||||
width={LAYOUT.ICON.SIZE}
|
||||
height={LAYOUT.ICON.SIZE}
|
||||
className={textClassName || "text-custom-text-300"}
|
||||
className={textClassName || "text-tertiary"}
|
||||
>
|
||||
{React.cloneElement(icon, {
|
||||
className: cn("size-4", icon?.props?.className),
|
||||
|
|
@ -221,10 +221,7 @@ export function CustomTreeMapContent({
|
|||
x={pX + LAYOUT.TEXT.PADDING_LEFT + iconSpace}
|
||||
y={pY + LAYOUT.TEXT.VERTICAL_OFFSET}
|
||||
textAnchor="start"
|
||||
className={cn(
|
||||
"text-sm font-extralight tracking-wider select-none",
|
||||
textClassName || "text-custom-text-300"
|
||||
)}
|
||||
className={cn("text-13 font-light tracking-wider select-none", textClassName || "text-tertiary")}
|
||||
fill="currentColor"
|
||||
>
|
||||
{top.nameTruncated ? truncateText(name, availableTextWidth, LAYOUT.TEXT.FONT_SIZES.SM, iconSpace) : name}
|
||||
|
|
@ -240,10 +237,7 @@ export function CustomTreeMapContent({
|
|||
x={pX + LAYOUT.TEXT.PADDING_LEFT}
|
||||
y={pY + pHeight - LAYOUT.TEXT.PADDING_LEFT}
|
||||
textAnchor="start"
|
||||
className={cn(
|
||||
"text-sm font-extralight tracking-wider select-none",
|
||||
textClassName || "text-custom-text-300"
|
||||
)}
|
||||
className={cn("text-13 font-light tracking-wider select-none", textClassName || "text-tertiary")}
|
||||
fill="currentColor"
|
||||
>
|
||||
{value.toLocaleString()}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export const TreeMapChart = React.memo(function TreeMapChart(props: TreeMapChart
|
|||
nameKey="name"
|
||||
dataKey="value"
|
||||
stroke="currentColor"
|
||||
className="text-custom-background-100 bg-custom-background-100"
|
||||
className="text-custom-background-100 bg-surface-1"
|
||||
content={<CustomTreeMapContent />}
|
||||
animationEasing="ease-out"
|
||||
isUpdateAnimationActive={isAnimationActive}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ export const TreeMapTooltip = React.memo(function TreeMapTooltip({ active, paylo
|
|||
|
||||
return (
|
||||
<Card className="flex flex-col space-y-1.5" spacing={ECardSpacing.SM}>
|
||||
<div className="flex items-center gap-2 border-b border-custom-border-200 pb-2.5">
|
||||
<div className="flex items-center gap-2 border-b border-subtle pb-2.5">
|
||||
{data?.icon}
|
||||
<p className="text-xs text-custom-text-100 font-medium capitalize">{data?.name}</p>
|
||||
<p className="text-11 text-primary font-medium capitalize">{data?.name}</p>
|
||||
</div>
|
||||
<span className="text-xs font-medium text-custom-text-200">
|
||||
<span className="text-11 font-medium text-secondary">
|
||||
{data?.value.toLocaleString()}
|
||||
{data.label && ` ${data.label}`}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ const meta = {
|
|||
</Collapsible.CollapsibleTrigger>
|
||||
<Collapsible.CollapsibleContent className="mt-2">
|
||||
<div className="rounded-md border border-gray-200 p-4">
|
||||
<p className="text-sm">This is the collapsible content that can be shown or hidden.</p>
|
||||
<p className="text-13">This is the collapsible content that can be shown or hidden.</p>
|
||||
</div>
|
||||
</Collapsible.CollapsibleContent>
|
||||
</Collapsible.CollapsibleRoot>
|
||||
|
|
@ -56,13 +56,16 @@ export const Controlled: Story = {
|
|||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex gap-2">
|
||||
<button onClick={() => setIsOpen(true)} className="rounded bg-blue-500 px-4 py-2 text-sm text-white">
|
||||
<button onClick={() => setIsOpen(true)} className="rounded-sm bg-blue-500 px-4 py-2 text-13 text-on-color">
|
||||
Open
|
||||
</button>
|
||||
<button onClick={() => setIsOpen(false)} className="rounded bg-gray-500 px-4 py-2 text-sm text-white">
|
||||
<button onClick={() => setIsOpen(false)} className="rounded-sm bg-gray-500 px-4 py-2 text-13 text-on-color">
|
||||
Close
|
||||
</button>
|
||||
<button onClick={() => setIsOpen(!isOpen)} className="rounded bg-green-500 px-4 py-2 text-sm text-white">
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="rounded-sm bg-green-500 px-4 py-2 text-13 text-on-color"
|
||||
>
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -73,8 +76,8 @@ export const Controlled: Story = {
|
|||
</Collapsible.CollapsibleTrigger>
|
||||
<Collapsible.CollapsibleContent className="mt-2">
|
||||
<div className="rounded-md border border-gray-200 p-4">
|
||||
<p className="text-sm">This collapsible is controlled by external state.</p>
|
||||
<p className="mt-2 text-sm">Current state: {isOpen ? "Open" : "Closed"}</p>
|
||||
<p className="text-13">This collapsible is controlled by external state.</p>
|
||||
<p className="mt-2 text-13">Current state: {isOpen ? "Open" : "Closed"}</p>
|
||||
</div>
|
||||
</Collapsible.CollapsibleContent>
|
||||
</Collapsible.CollapsibleRoot>
|
||||
|
|
@ -95,10 +98,10 @@ export const NestedContent: Story = {
|
|||
<Collapsible.CollapsibleContent className="mt-2">
|
||||
<div className="space-y-2 rounded-md border border-gray-200 p-4">
|
||||
<h4 className="font-semibold">Section 1</h4>
|
||||
<p className="text-sm">This is some content in the first section.</p>
|
||||
<p className="text-13">This is some content in the first section.</p>
|
||||
<h4 className="font-semibold">Section 2</h4>
|
||||
<p className="text-sm">This is some content in the second section.</p>
|
||||
<ul className="list-inside list-disc text-sm">
|
||||
<p className="text-13">This is some content in the second section.</p>
|
||||
<ul className="list-inside list-disc text-13">
|
||||
<li>Item 1</li>
|
||||
<li>Item 2</li>
|
||||
<li>Item 3</li>
|
||||
|
|
@ -115,8 +118,8 @@ export const CustomStyling: Story = {
|
|||
const [isOpen, setIsOpen] = useState(args.isOpen);
|
||||
return (
|
||||
<Collapsible.CollapsibleRoot {...args} isOpen={isOpen} onToggle={() => setIsOpen(!isOpen)} className="w-96">
|
||||
<Collapsible.CollapsibleTrigger className="flex w-full items-center justify-between rounded-lg bg-gradient-to-r from-purple-500 to-pink-500 px-6 py-3 text-white shadow-lg transition-all hover:shadow-xl">
|
||||
<span className="text-lg font-bold">Custom Styled Trigger</span>
|
||||
<Collapsible.CollapsibleTrigger className="flex w-full items-center justify-between rounded-lg bg-gradient-to-r from-purple-500 to-pink-500 px-6 py-3 text-on-color shadow-lg transition-all hover:shadow-xl">
|
||||
<span className="text-16 font-bold">Custom Styled Trigger</span>
|
||||
<ChevronDownIcon className="h-5 w-5 transition-transform group-data-[panel-open]:rotate-180" />
|
||||
</Collapsible.CollapsibleTrigger>
|
||||
<Collapsible.CollapsibleContent className="mt-4">
|
||||
|
|
@ -140,7 +143,7 @@ export const MultipleCollapsibles: Story = {
|
|||
</Collapsible.CollapsibleTrigger>
|
||||
<Collapsible.CollapsibleContent className="mt-2">
|
||||
<div className="rounded-md border border-gray-200 p-4">
|
||||
<p className="text-sm">Content for the first item.</p>
|
||||
<p className="text-13">Content for the first item.</p>
|
||||
</div>
|
||||
</Collapsible.CollapsibleContent>
|
||||
</Collapsible.CollapsibleRoot>
|
||||
|
|
@ -152,7 +155,7 @@ export const MultipleCollapsibles: Story = {
|
|||
</Collapsible.CollapsibleTrigger>
|
||||
<Collapsible.CollapsibleContent className="mt-2">
|
||||
<div className="rounded-md border border-gray-200 p-4">
|
||||
<p className="text-sm">Content for the second item.</p>
|
||||
<p className="text-13">Content for the second item.</p>
|
||||
</div>
|
||||
</Collapsible.CollapsibleContent>
|
||||
</Collapsible.CollapsibleRoot>
|
||||
|
|
@ -164,7 +167,7 @@ export const MultipleCollapsibles: Story = {
|
|||
</Collapsible.CollapsibleTrigger>
|
||||
<Collapsible.CollapsibleContent className="mt-2">
|
||||
<div className="rounded-md border border-gray-200 p-4">
|
||||
<p className="text-sm">Content for the third item.</p>
|
||||
<p className="text-13">Content for the third item.</p>
|
||||
</div>
|
||||
</Collapsible.CollapsibleContent>
|
||||
</Collapsible.CollapsibleRoot>
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ function Content({ children, className }: ContentProps) {
|
|||
return (
|
||||
<BaseCollapsible.Panel
|
||||
className={clsx(
|
||||
"flex h-[var(--collapsible-panel-height)] flex-col overflow-hidden text-sm transition-all ease-out data-[ending-style]:h-0 data-[starting-style]:h-0",
|
||||
"flex h-[var(--collapsible-panel-height)] flex-col overflow-hidden text-13 transition-all ease-out data-[ending-style]:h-0 data-[starting-style]:h-0",
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ export const MultiSelectWithLimit: Story = {
|
|||
))}
|
||||
</Combobox.Options>
|
||||
</Combobox>
|
||||
<p className="text-xs text-gray-500">Maximum 3 selections allowed</p>
|
||||
<p className="text-11 text-gray-500">Maximum 3 selections allowed</p>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -168,13 +168,13 @@ function ComboboxOptions({
|
|||
<BaseCombobox.Portal>
|
||||
<BaseCombobox.Positioner sideOffset={8} className={positionerClassName}>
|
||||
<BaseCombobox.Popup
|
||||
className={cn("rounded-md border border-custom-border-200 bg-custom-background-100 p-1 shadow-lg", className)}
|
||||
className={cn("rounded-md border border-subtle bg-surface-1 p-1 shadow-lg", className)}
|
||||
data-prevent-outside-click={dataPreventOutsideClick}
|
||||
>
|
||||
<div className="flex flex-col gap-1">
|
||||
{showSearch && (
|
||||
<div className="relative">
|
||||
<Search className="absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2 text-custom-text-400" />
|
||||
<Search className="absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2 text-placeholder" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder={searchPlaceholder}
|
||||
|
|
@ -182,7 +182,7 @@ function ComboboxOptions({
|
|||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
onKeyDown={onSearchQueryKeyDown}
|
||||
className={cn(
|
||||
"w-full rounded border border-custom-border-100 bg-custom-background-90 py-1.5 pl-8 pr-2 text-sm outline-none placeholder:text-custom-text-400",
|
||||
"w-full rounded-sm border border-subtle bg-surface-2 py-1.5 pl-8 pr-2 text-13 outline-none placeholder:text-placeholder",
|
||||
inputClassName
|
||||
)}
|
||||
/>
|
||||
|
|
@ -198,7 +198,7 @@ function ComboboxOptions({
|
|||
React.Children.toArray(filteredChildren).filter(
|
||||
(child) => React.isValidElement(child) && child.type === ComboboxOption
|
||||
)
|
||||
) === 0 && <div className="px-2 py-1.5 text-sm text-custom-text-400">{emptyMessage}</div>}
|
||||
) === 0 && <div className="px-2 py-1.5 text-13 text-placeholder">{emptyMessage}</div>}
|
||||
</BaseCombobox.List>
|
||||
</div>
|
||||
</BaseCombobox.Popup>
|
||||
|
|
@ -213,7 +213,7 @@ function ComboboxOption({ value, children, disabled, className }: ComboboxOption
|
|||
<BaseCombobox.Item
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
className={cn("cursor-pointer rounded px-2 py-1.5 text-sm outline-none transition-colors", className)}
|
||||
className={cn("cursor-pointer rounded-sm px-2 py-1.5 text-13 outline-none transition-colors", className)}
|
||||
>
|
||||
{children}
|
||||
</BaseCombobox.Item>
|
||||
|
|
|
|||
|
|
@ -24,13 +24,13 @@ export const Default: Story = {
|
|||
render() {
|
||||
return (
|
||||
<Command className="w-96 rounded-lg border border-gray-200 p-2">
|
||||
<Command.Input placeholder="Search..." className="h-9 w-full bg-transparent py-3 text-sm outline-none" />
|
||||
<Command.Input placeholder="Search..." className="h-9 w-full bg-transparent py-3 text-13 outline-none" />
|
||||
<Command.List className="max-h-80 overflow-auto py-2">
|
||||
<Command.Item className="cursor-pointer rounded px-3 py-2 hover:bg-gray-100">Item 1</Command.Item>
|
||||
<Command.Item className="cursor-pointer rounded px-3 py-2 hover:bg-gray-100">Item 2</Command.Item>
|
||||
<Command.Item className="cursor-pointer rounded px-3 py-2 hover:bg-gray-100">Item 3</Command.Item>
|
||||
<Command.Item className="cursor-pointer rounded-sm px-3 py-2 hover:bg-gray-100">Item 1</Command.Item>
|
||||
<Command.Item className="cursor-pointer rounded-sm px-3 py-2 hover:bg-gray-100">Item 2</Command.Item>
|
||||
<Command.Item className="cursor-pointer rounded-sm px-3 py-2 hover:bg-gray-100">Item 3</Command.Item>
|
||||
</Command.List>
|
||||
<Command.Empty className="py-6 text-center text-sm text-gray-500">No results found.</Command.Empty>
|
||||
<Command.Empty className="py-6 text-center text-13 text-gray-500">No results found.</Command.Empty>
|
||||
</Command>
|
||||
);
|
||||
},
|
||||
|
|
@ -42,27 +42,27 @@ export const WithIcons: Story = {
|
|||
<Command className="w-96 rounded-lg border border-gray-200 p-2">
|
||||
<Command.Input
|
||||
placeholder="Search files and folders..."
|
||||
className="h-9 w-full bg-transparent py-3 text-sm outline-none"
|
||||
className="h-9 w-full bg-transparent py-3 text-13 outline-none"
|
||||
/>
|
||||
<Command.List className="max-h-80 overflow-auto py-2">
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded px-3 py-2 hover:bg-gray-100">
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded-sm px-3 py-2 hover:bg-gray-100">
|
||||
<Folder className="h-4 w-4" />
|
||||
<span>Documents</span>
|
||||
</Command.Item>
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded px-3 py-2 hover:bg-gray-100">
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded-sm px-3 py-2 hover:bg-gray-100">
|
||||
<Folder className="h-4 w-4" />
|
||||
<span>Downloads</span>
|
||||
</Command.Item>
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded px-3 py-2 hover:bg-gray-100">
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded-sm px-3 py-2 hover:bg-gray-100">
|
||||
<File className="h-4 w-4" />
|
||||
<span>README.md</span>
|
||||
</Command.Item>
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded px-3 py-2 hover:bg-gray-100">
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded-sm px-3 py-2 hover:bg-gray-100">
|
||||
<File className="h-4 w-4" />
|
||||
<span>package.json</span>
|
||||
</Command.Item>
|
||||
</Command.List>
|
||||
<Command.Empty className="py-6 text-center text-sm text-gray-500">No files or folders found.</Command.Empty>
|
||||
<Command.Empty className="py-6 text-center text-13 text-gray-500">No files or folders found.</Command.Empty>
|
||||
</Command>
|
||||
);
|
||||
},
|
||||
|
|
@ -74,30 +74,30 @@ export const WithCategories: Story = {
|
|||
<Command className="w-96 rounded-lg border border-gray-200 p-2">
|
||||
<Command.Input
|
||||
placeholder="Search commands..."
|
||||
className="h-9 w-full bg-transparent py-3 text-sm outline-none"
|
||||
className="h-9 w-full bg-transparent py-3 text-13 outline-none"
|
||||
/>
|
||||
<Command.List className="max-h-80 overflow-auto py-2">
|
||||
<div className="px-2 py-1.5 text-xs font-semibold text-gray-500">User</div>
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded px-3 py-2 hover:bg-gray-100">
|
||||
<div className="px-2 py-1.5 text-11 font-semibold text-gray-500">User</div>
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded-sm px-3 py-2 hover:bg-gray-100">
|
||||
<User className="h-4 w-4" />
|
||||
<span>Profile</span>
|
||||
</Command.Item>
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded px-3 py-2 hover:bg-gray-100">
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded-sm px-3 py-2 hover:bg-gray-100">
|
||||
<Settings className="h-4 w-4" />
|
||||
<span>Settings</span>
|
||||
</Command.Item>
|
||||
|
||||
<div className="mt-2 px-2 py-1.5 text-xs font-semibold text-gray-500">Files</div>
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded px-3 py-2 hover:bg-gray-100">
|
||||
<div className="mt-2 px-2 py-1.5 text-11 font-semibold text-gray-500">Files</div>
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded-sm px-3 py-2 hover:bg-gray-100">
|
||||
<Folder className="h-4 w-4" />
|
||||
<span>Open Folder</span>
|
||||
</Command.Item>
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded px-3 py-2 hover:bg-gray-100">
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded-sm px-3 py-2 hover:bg-gray-100">
|
||||
<File className="h-4 w-4" />
|
||||
<span>New File</span>
|
||||
</Command.Item>
|
||||
</Command.List>
|
||||
<Command.Empty className="py-6 text-center text-sm text-gray-500">No commands found.</Command.Empty>
|
||||
<Command.Empty className="py-6 text-center text-13 text-gray-500">No commands found.</Command.Empty>
|
||||
</Command>
|
||||
);
|
||||
},
|
||||
|
|
@ -107,11 +107,11 @@ export const EmptyState: Story = {
|
|||
render() {
|
||||
return (
|
||||
<Command className="w-96 rounded-lg border border-gray-200 p-2">
|
||||
<Command.Input placeholder="Search..." className="h-9 w-full bg-transparent py-3 text-sm outline-none" />
|
||||
<Command.Input placeholder="Search..." className="h-9 w-full bg-transparent py-3 text-13 outline-none" />
|
||||
<Command.List className="max-h-80 overflow-auto py-2">{/* No items - will show empty state */}</Command.List>
|
||||
<Command.Empty className="py-6 text-center text-sm text-gray-500">
|
||||
<Command.Empty className="py-6 text-center text-13 text-gray-500">
|
||||
<p className="font-semibold">No results found</p>
|
||||
<p className="mt-1 text-xs">Try searching for something else</p>
|
||||
<p className="mt-1 text-11">Try searching for something else</p>
|
||||
</Command.Empty>
|
||||
</Command>
|
||||
);
|
||||
|
|
@ -122,15 +122,15 @@ export const LongList: Story = {
|
|||
render() {
|
||||
return (
|
||||
<Command className="w-96 rounded-lg border border-gray-200 p-2">
|
||||
<Command.Input placeholder="Search items..." className="h-9 w-full bg-transparent py-3 text-sm outline-none" />
|
||||
<Command.Input placeholder="Search items..." className="h-9 w-full bg-transparent py-3 text-13 outline-none" />
|
||||
<Command.List className="max-h-60 overflow-auto py-2">
|
||||
{Array.from({ length: 20 }, (_, i) => (
|
||||
<Command.Item key={i} className="cursor-pointer rounded px-3 py-2 hover:bg-gray-100">
|
||||
<Command.Item key={i} className="cursor-pointer rounded-sm px-3 py-2 hover:bg-gray-100">
|
||||
Item {i + 1}
|
||||
</Command.Item>
|
||||
))}
|
||||
</Command.List>
|
||||
<Command.Empty className="py-6 text-center text-sm text-gray-500">No results found.</Command.Empty>
|
||||
<Command.Empty className="py-6 text-center text-13 text-gray-500">No results found.</Command.Empty>
|
||||
</Command>
|
||||
);
|
||||
},
|
||||
|
|
@ -141,15 +141,15 @@ export const WithoutSearch: Story = {
|
|||
return (
|
||||
<Command className="w-96 rounded-lg border border-gray-200 p-2">
|
||||
<Command.List className="max-h-80 overflow-auto py-2">
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded px-3 py-2 hover:bg-gray-100">
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded-sm px-3 py-2 hover:bg-gray-100">
|
||||
<User className="h-4 w-4" />
|
||||
<span>Profile</span>
|
||||
</Command.Item>
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded px-3 py-2 hover:bg-gray-100">
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded-sm px-3 py-2 hover:bg-gray-100">
|
||||
<Settings className="h-4 w-4" />
|
||||
<span>Settings</span>
|
||||
</Command.Item>
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded px-3 py-2 hover:bg-gray-100">
|
||||
<Command.Item className="flex cursor-pointer items-center gap-2 rounded-sm px-3 py-2 hover:bg-gray-100">
|
||||
<Folder className="h-4 w-4" />
|
||||
<span>Files</span>
|
||||
</Command.Item>
|
||||
|
|
@ -165,20 +165,20 @@ export const CustomStyling: Story = {
|
|||
<Command className="w-96 rounded-lg border-2 border-blue-300 bg-blue-50 p-2 shadow-lg">
|
||||
<Command.Input
|
||||
placeholder="Search with custom styling..."
|
||||
className="h-9 w-full bg-transparent py-3 text-sm text-blue-900 outline-none placeholder:text-blue-400"
|
||||
className="h-9 w-full bg-transparent py-3 text-13 text-blue-900 outline-none placeholder:text-blue-400"
|
||||
/>
|
||||
<Command.List className="max-h-80 overflow-auto py-2">
|
||||
<Command.Item className="cursor-pointer rounded px-3 py-2 text-blue-900 hover:bg-blue-200">
|
||||
<Command.Item className="cursor-pointer rounded-sm px-3 py-2 text-blue-900 hover:bg-blue-200">
|
||||
Custom Item 1
|
||||
</Command.Item>
|
||||
<Command.Item className="cursor-pointer rounded px-3 py-2 text-blue-900 hover:bg-blue-200">
|
||||
<Command.Item className="cursor-pointer rounded-sm px-3 py-2 text-blue-900 hover:bg-blue-200">
|
||||
Custom Item 2
|
||||
</Command.Item>
|
||||
<Command.Item className="cursor-pointer rounded px-3 py-2 text-blue-900 hover:bg-blue-200">
|
||||
<Command.Item className="cursor-pointer rounded-sm px-3 py-2 text-blue-900 hover:bg-blue-200">
|
||||
Custom Item 3
|
||||
</Command.Item>
|
||||
</Command.List>
|
||||
<Command.Empty className="py-6 text-center text-sm text-blue-500">No matching items found.</Command.Empty>
|
||||
<Command.Empty className="py-6 text-center text-13 text-blue-500">No matching items found.</Command.Empty>
|
||||
</Command>
|
||||
);
|
||||
},
|
||||
|
|
@ -188,15 +188,15 @@ export const DisabledItems: Story = {
|
|||
render() {
|
||||
return (
|
||||
<Command className="w-96 rounded-lg border border-gray-200 p-2">
|
||||
<Command.Input placeholder="Search..." className="h-9 w-full bg-transparent py-3 text-sm outline-none" />
|
||||
<Command.Input placeholder="Search..." className="h-9 w-full bg-transparent py-3 text-13 outline-none" />
|
||||
<Command.List className="max-h-80 overflow-auto py-2">
|
||||
<Command.Item className="cursor-pointer rounded px-3 py-2 hover:bg-gray-100">Active Item 1</Command.Item>
|
||||
<Command.Item disabled className="cursor-not-allowed rounded px-3 py-2 opacity-50">
|
||||
<Command.Item className="cursor-pointer rounded-sm px-3 py-2 hover:bg-gray-100">Active Item 1</Command.Item>
|
||||
<Command.Item disabled className="cursor-not-allowed rounded-sm px-3 py-2 opacity-50">
|
||||
Disabled Item
|
||||
</Command.Item>
|
||||
<Command.Item className="cursor-pointer rounded px-3 py-2 hover:bg-gray-100">Active Item 2</Command.Item>
|
||||
<Command.Item className="cursor-pointer rounded-sm px-3 py-2 hover:bg-gray-100">Active Item 2</Command.Item>
|
||||
</Command.List>
|
||||
<Command.Empty className="py-6 text-center text-sm text-gray-500">No results found.</Command.Empty>
|
||||
<Command.Empty className="py-6 text-center text-13 text-gray-500">No results found.</Command.Empty>
|
||||
</Command>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ function CommandInput({ className, ...props }: React.ComponentProps<typeof Comma
|
|||
return (
|
||||
<div
|
||||
data-slot="command-input-wrapper"
|
||||
className="flex items-center gap-1.5 rounded border border-custom-border-100 bg-custom-background-90 px-2"
|
||||
className="flex items-center gap-1.5 rounded-sm border border-subtle bg-surface-2 px-2"
|
||||
>
|
||||
<SearchIcon className="size-3.5 flex-shrink-0 text-custom-text-400" strokeWidth={1.5} />
|
||||
<SearchIcon className="size-3.5 flex-shrink-0 text-placeholder" strokeWidth={1.5} />
|
||||
<CommandPrimitive.Input data-slot="command-input" className={cn(className)} {...props} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export const Default: Story = {
|
|||
return (
|
||||
<ContextMenu>
|
||||
<ContextMenu.Trigger>
|
||||
<div className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-custom-border-300 text-sm">
|
||||
<div className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-strong text-13">
|
||||
Right click here
|
||||
</div>
|
||||
</ContextMenu.Trigger>
|
||||
|
|
@ -56,7 +56,7 @@ export const WithIcons: Story = {
|
|||
return (
|
||||
<ContextMenu>
|
||||
<ContextMenu.Trigger>
|
||||
<div className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-custom-border-300 text-sm">
|
||||
<div className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-strong text-13">
|
||||
Right click here
|
||||
</div>
|
||||
</ContextMenu.Trigger>
|
||||
|
|
@ -95,7 +95,7 @@ export const WithSubmenus: Story = {
|
|||
return (
|
||||
<ContextMenu>
|
||||
<ContextMenu.Trigger>
|
||||
<div className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-custom-border-300 text-sm">
|
||||
<div className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-strong text-13">
|
||||
Right click here
|
||||
</div>
|
||||
</ContextMenu.Trigger>
|
||||
|
|
@ -141,7 +141,7 @@ export const DisabledItems: Story = {
|
|||
return (
|
||||
<ContextMenu>
|
||||
<ContextMenu.Trigger>
|
||||
<div className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-custom-border-300 text-sm">
|
||||
<div className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-strong text-13">
|
||||
Right click here
|
||||
</div>
|
||||
</ContextMenu.Trigger>
|
||||
|
|
@ -180,14 +180,14 @@ export const OnFileCard: Story = {
|
|||
return (
|
||||
<ContextMenu>
|
||||
<ContextMenu.Trigger>
|
||||
<div className="w-64 p-4 border border-custom-border-200 rounded-lg hover:bg-custom-background-80 cursor-pointer">
|
||||
<div className="w-64 p-4 border border-subtle rounded-lg hover:bg-layer-1 cursor-pointer">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-12 h-12 bg-custom-primary-100 rounded flex items-center justify-center text-white text-lg">
|
||||
<div className="w-12 h-12 bg-accent-primary rounded-sm flex items-center justify-center text-on-color text-16">
|
||||
📄
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="font-medium">Document.pdf</div>
|
||||
<div className="text-sm text-custom-text-400">2.4 MB</div>
|
||||
<div className="text-13 text-placeholder">2.4 MB</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -227,10 +227,8 @@ export const OnImage: Story = {
|
|||
return (
|
||||
<ContextMenu>
|
||||
<ContextMenu.Trigger>
|
||||
<div className="relative w-80 h-56 bg-custom-background-80 rounded-lg overflow-hidden cursor-pointer">
|
||||
<div className="absolute inset-0 flex items-center justify-center text-custom-text-400">
|
||||
Image Placeholder
|
||||
</div>
|
||||
<div className="relative w-80 h-56 bg-layer-1 rounded-lg overflow-hidden cursor-pointer">
|
||||
<div className="absolute inset-0 flex items-center justify-center text-placeholder">Image Placeholder</div>
|
||||
</div>
|
||||
</ContextMenu.Trigger>
|
||||
<ContextMenu.Portal>
|
||||
|
|
@ -261,9 +259,9 @@ export const OnText: Story = {
|
|||
return (
|
||||
<ContextMenu>
|
||||
<ContextMenu.Trigger>
|
||||
<div className="w-96 p-6 border border-custom-border-200 rounded-lg">
|
||||
<h3 className="text-lg font-semibold mb-2">Context Menu on Text</h3>
|
||||
<p className="text-custom-text-300">
|
||||
<div className="w-96 p-6 border border-subtle rounded-lg">
|
||||
<h3 className="text-16 font-semibold mb-2">Context Menu on Text</h3>
|
||||
<p className="text-tertiary">
|
||||
Right click anywhere on this text area to see the context menu. This demonstrates how context menus can be
|
||||
applied to text content areas.
|
||||
</p>
|
||||
|
|
@ -293,7 +291,7 @@ export const NestedSubmenus: Story = {
|
|||
return (
|
||||
<ContextMenu>
|
||||
<ContextMenu.Trigger>
|
||||
<div className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-custom-border-300 text-sm">
|
||||
<div className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-strong text-13">
|
||||
Right click here
|
||||
</div>
|
||||
</ContextMenu.Trigger>
|
||||
|
|
@ -344,7 +342,7 @@ export const WithKeyboardShortcuts: Story = {
|
|||
return (
|
||||
<ContextMenu>
|
||||
<ContextMenu.Trigger>
|
||||
<div className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-custom-border-300 text-sm">
|
||||
<div className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-strong text-13">
|
||||
Right click here
|
||||
</div>
|
||||
</ContextMenu.Trigger>
|
||||
|
|
@ -353,23 +351,23 @@ export const WithKeyboardShortcuts: Story = {
|
|||
<ContextMenu.Item>
|
||||
<Copy className="mr-2 h-4 w-4" />
|
||||
Copy
|
||||
<span className="ml-auto text-xs text-custom-text-400">⌘C</span>
|
||||
<span className="ml-auto text-11 text-placeholder">⌘C</span>
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item>
|
||||
<Edit className="mr-2 h-4 w-4" />
|
||||
Edit
|
||||
<span className="ml-auto text-xs text-custom-text-400">⌘E</span>
|
||||
<span className="ml-auto text-11 text-placeholder">⌘E</span>
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item>
|
||||
<Download className="mr-2 h-4 w-4" />
|
||||
Download
|
||||
<span className="ml-auto text-xs text-custom-text-400">⌘D</span>
|
||||
<span className="ml-auto text-11 text-placeholder">⌘D</span>
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Separator />
|
||||
<ContextMenu.Item>
|
||||
<Trash className="mr-2 h-4 w-4 text-red-500" />
|
||||
<span className="text-red-500">Delete</span>
|
||||
<span className="ml-auto text-xs text-custom-text-400">⌘⌫</span>
|
||||
<span className="ml-auto text-11 text-placeholder">⌘⌫</span>
|
||||
</ContextMenu.Item>
|
||||
</ContextMenu.Content>
|
||||
</ContextMenu.Portal>
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ const ContextMenuContent = React.forwardRef(function ContextMenuContent(
|
|||
>
|
||||
<ContextMenuPrimitive.Popup
|
||||
className={cn(
|
||||
"z-50 min-w-32 overflow-hidden rounded-md border border-custom-border-200 bg-custom-background-100 p-1 shadow-md",
|
||||
"z-50 min-w-32 overflow-hidden rounded-md border border-subtle bg-surface-1 p-1 shadow-md",
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
||||
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
||||
|
|
@ -81,8 +81,8 @@ const ContextMenuItem = React.forwardRef(function ContextMenuItem(
|
|||
<ContextMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
|
||||
"focus:bg-custom-background-90 focus:text-custom-text-100",
|
||||
"relative flex cursor-default select-none items-center rounded-xs px-2 py-1.5 text-13 outline-none",
|
||||
"focus:bg-surface-2 focus:text-primary",
|
||||
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
)}
|
||||
|
|
@ -99,11 +99,7 @@ const ContextMenuSeparator = React.forwardRef(function ContextMenuSeparator(
|
|||
ref: React.ForwardedRef<React.ElementRef<typeof ContextMenuPrimitive.Separator>>
|
||||
) {
|
||||
return (
|
||||
<ContextMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn("-mx-1 my-1 h-px bg-custom-border-200", className)}
|
||||
{...props}
|
||||
/>
|
||||
<ContextMenuPrimitive.Separator ref={ref} className={cn("-mx-1 my-1 h-px bg-subtle-1", className)} {...props} />
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -117,8 +113,8 @@ const ContextMenuSubmenuTrigger = React.forwardRef(function ContextMenuSubmenuTr
|
|||
<ContextMenuPrimitive.SubmenuTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:outline-none",
|
||||
"focus:bg-custom-background-90 data-[state=open]:bg-custom-background-90",
|
||||
"flex cursor-default select-none items-center rounded-xs px-2 py-1.5 text-13 outline-none focus:outline-none",
|
||||
"focus:bg-surface-2 data-[state=open]:bg-surface-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,428 @@
|
|||
import React from "react";
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
|
||||
const meta = {
|
||||
title: "Design System/Philosophy",
|
||||
parameters: {
|
||||
layout: "fullscreen",
|
||||
docs: {
|
||||
description: {
|
||||
component: `
|
||||
# Design System Philosophy
|
||||
|
||||
Reusable, composable Storybook stories that demonstrate Canvas, Surface, and Layer concepts.
|
||||
|
||||
Key concepts and rules are preserved, but the implementation is componentized for DRYness and clarity.
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
tags: ["autodocs"],
|
||||
} satisfies Meta;
|
||||
|
||||
export default meta;
|
||||
export type Story = StoryObj<typeof meta>;
|
||||
|
||||
/* -----------------------------
|
||||
Reusable UI building blocks
|
||||
-----------------------------*/
|
||||
|
||||
type ContainerProps = {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const DemoRoot: React.FC<ContainerProps> = ({ children, className = "" }) => (
|
||||
<div className={`p-8 ${className}`}>{children}</div>
|
||||
);
|
||||
|
||||
const Info: React.FC<{ title: string; children?: React.ReactNode; tone?: "info" | "warn" }> = ({
|
||||
title,
|
||||
children,
|
||||
tone = "info",
|
||||
}) => (
|
||||
<div
|
||||
className={`mb-4 rounded-md border ${tone === "warn" ? "border-red-500 bg-red-50 p-4" : "border-subtle bg-layer-1 p-4"}`}
|
||||
>
|
||||
<h3 className={`text-primary mb-2 text-16 font-semibold`}>{title}</h3>
|
||||
<div className="text-secondary text-13">{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const Surface: React.FC<ContainerProps> = ({ children, className = "bg-surface-1 rounded-md p-6" }) => (
|
||||
<div className={className}>{children}</div>
|
||||
);
|
||||
|
||||
const Layer: React.FC<ContainerProps & { hover?: boolean }> = ({
|
||||
children,
|
||||
className = "bg-layer-1 hover:bg-layer-1-hover rounded-md p-4",
|
||||
}) => <div className={`${className} transition-colors`}>{children}</div>;
|
||||
|
||||
/* Small helpers to keep stories concise */
|
||||
const TwoColGrid: React.FC<{ children: React.ReactNode }> = ({ children }) => (
|
||||
<div className="grid grid-cols-2 gap-6">{children}</div>
|
||||
);
|
||||
|
||||
/* -----------------------------
|
||||
Stories (using the building blocks)
|
||||
-----------------------------*/
|
||||
|
||||
export const ApplicationRoot: Story = {
|
||||
render: () => (
|
||||
<DemoRoot>
|
||||
<Info title="✅ Application Root Pattern">
|
||||
This is the <strong>bg-canvas</strong> - the application-level background. It should only appear{" "}
|
||||
<strong>once</strong> in your entire application at the root level.
|
||||
</Info>
|
||||
|
||||
<Surface>
|
||||
<h4 className="text-primary mb-2 font-semibold">Page Content (bg-surface-1)</h4>
|
||||
<p className="text-secondary text-13">Pages use surfaces, not canvas. This is a typical page layout.</p>
|
||||
</Surface>
|
||||
</DemoRoot>
|
||||
),
|
||||
};
|
||||
|
||||
export const SurfaceSiblings: Story = {
|
||||
render: () => (
|
||||
<DemoRoot>
|
||||
<Info title="✅ Surface Siblings Pattern">
|
||||
Surfaces are <strong>siblings</strong>, not nested.
|
||||
</Info>
|
||||
|
||||
<TwoColGrid>
|
||||
<Surface>
|
||||
<h4 className="text-primary mb-2 font-semibold">Surface 1</h4>
|
||||
<p className="text-secondary text-13">This is bg-surface-1 - a primary surface</p>
|
||||
</Surface>
|
||||
|
||||
<Surface className="bg-surface-2 rounded-md p-6">
|
||||
<h4 className="text-primary mb-2 font-semibold">Surface 2</h4>
|
||||
<p className="text-secondary text-13">This is bg-surface-2 - a secondary surface (sibling to surface-1)</p>
|
||||
</Surface>
|
||||
</TwoColGrid>
|
||||
</DemoRoot>
|
||||
),
|
||||
};
|
||||
|
||||
export const LayerStacking: Story = {
|
||||
render: () => (
|
||||
<DemoRoot>
|
||||
<Info title="✅ Layer Stacking Pattern">Layers stack to create depth: Surface → Layer 1 → Layer 2 → Layer 3</Info>
|
||||
|
||||
<Surface>
|
||||
<h4 className="text-primary mb-3 font-semibold">Surface 1</h4>
|
||||
|
||||
<Layer className="bg-layer-1 hover:bg-layer-1-hover mb-4 rounded-md p-4">
|
||||
<h5 className="text-primary mb-2 font-medium">Layer 1 (First level of depth)</h5>
|
||||
<p className="text-secondary mb-3 text-13">Hover over me to see the hover state</p>
|
||||
|
||||
<Layer className="bg-layer-2 hover:bg-layer-2-hover rounded-md p-3">
|
||||
<h6 className="text-primary mb-2 text-13 font-medium">Layer 2 (Second level)</h6>
|
||||
<p className="text-secondary mb-2 text-13">Nested within Layer 1</p>
|
||||
|
||||
<Layer className="bg-layer-3 hover:bg-layer-3-hover rounded-md p-2" hover>
|
||||
<p className="text-primary text-11 font-medium">Layer 3 (Third level)</p>
|
||||
<p className="text-secondary text-11">Deepest nesting level</p>
|
||||
</Layer>
|
||||
</Layer>
|
||||
</Layer>
|
||||
</Surface>
|
||||
</DemoRoot>
|
||||
),
|
||||
};
|
||||
|
||||
export const SurfaceLayerAssociation: Story = {
|
||||
render: () => (
|
||||
<DemoRoot>
|
||||
<Info title="✅ Surface-Layer Association">
|
||||
Each surface should use its corresponding layer: surface-1 → layer-1, surface-2 → layer-2. Very rare exception:
|
||||
inputs/buttons can go one level above for visual separation.
|
||||
</Info>
|
||||
|
||||
<TwoColGrid>
|
||||
<Surface>
|
||||
<h4 className="text-primary mb-3 font-semibold">Surface 1</h4>
|
||||
<Layer className="bg-layer-1 hover:bg-layer-1-hover rounded-md p-4">
|
||||
<h5 className="text-primary mb-1 font-medium">Layer 1</h5>
|
||||
<p className="text-secondary text-13">Correctly using layer-1 with surface-1</p>
|
||||
</Layer>
|
||||
</Surface>
|
||||
|
||||
<Surface className="bg-surface-2 rounded-md p-6">
|
||||
<h4 className="text-primary mb-3 font-semibold">Surface 2</h4>
|
||||
<Layer className="bg-layer-2 hover:bg-layer-2-hover rounded-md p-4">
|
||||
<h5 className="text-primary mb-1 font-medium">Layer 2</h5>
|
||||
<p className="text-secondary text-13">Correctly using layer-2 with surface-2</p>
|
||||
</Layer>
|
||||
</Surface>
|
||||
</TwoColGrid>
|
||||
|
||||
<div className="mt-6">
|
||||
<Info title="✅ Rare Exception: Visual Separation for Form Elements">
|
||||
In very rare cases, form elements (inputs, buttons, switches) can use one level above for visual separation.
|
||||
</Info>
|
||||
<Surface>
|
||||
<h4 className="text-primary mb-3 font-semibold">Modal with Input (Rare Exception)</h4>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label htmlFor="example-input" className="text-secondary text-13">
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
id="example-input"
|
||||
className="w-full bg-layer-2 border border-subtle rounded-md px-3 py-2 text-primary mt-1"
|
||||
placeholder="Input uses layer-2 for visual separation"
|
||||
/>
|
||||
<p className="text-tertiary text-11 mt-1">
|
||||
Input uses bg-layer-2 (one level above) for visual separation from modal surface
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Surface>
|
||||
</div>
|
||||
</DemoRoot>
|
||||
),
|
||||
};
|
||||
|
||||
export const ModalException: Story = {
|
||||
render: () => (
|
||||
<DemoRoot>
|
||||
<Info title="✅ Modal Exception Pattern">
|
||||
Modals exist on a <strong>different plane</strong>, so they can use surfaces even when there's a surface
|
||||
below
|
||||
</Info>
|
||||
|
||||
<Surface>
|
||||
<h4 className="text-primary mb-2 font-semibold">Main Page Content</h4>
|
||||
<p className="text-secondary text-13">This is the main page using bg-surface-1</p>
|
||||
</Surface>
|
||||
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div className="bg-backdrop absolute inset-0" />
|
||||
<div className="bg-surface-1 relative z-10 max-w-md rounded-lg p-6 shadow-lg">
|
||||
<h4 className="text-primary mb-3 font-semibold">Modal Dialog</h4>
|
||||
<p className="text-secondary mb-4 text-13">
|
||||
This modal uses bg-surface-1 even though the page below also uses bg-surface-1. This is allowed because
|
||||
they're on different planes.
|
||||
</p>
|
||||
|
||||
<Layer className="bg-layer-1 hover:bg-layer-1-hover rounded-md p-3">
|
||||
<p className="text-primary text-13">Modal content can use layers as normal</p>
|
||||
</Layer>
|
||||
</div>
|
||||
</div>
|
||||
</DemoRoot>
|
||||
),
|
||||
};
|
||||
|
||||
export const CardListPattern: Story = {
|
||||
render: () => (
|
||||
<DemoRoot>
|
||||
<Info title="✅ Card List Pattern">Common pattern: Surface containing multiple layer-1 cards</Info>
|
||||
|
||||
<Surface>
|
||||
<h4 className="text-primary mb-4 font-semibold">Task List</h4>
|
||||
<div className="space-y-3">
|
||||
{[1, 2, 3].map((item) => (
|
||||
<Layer key={item} className="bg-layer-1 hover:bg-layer-1-hover rounded-md p-4">
|
||||
<h5 className="text-primary mb-1 font-medium">Task {item}</h5>
|
||||
<p className="text-secondary text-13">This is a task card using bg-layer-1 with hover state</p>
|
||||
</Layer>
|
||||
))}
|
||||
</div>
|
||||
</Surface>
|
||||
</DemoRoot>
|
||||
),
|
||||
};
|
||||
|
||||
export const SidebarLayoutPattern: Story = {
|
||||
render: () => (
|
||||
<DemoRoot>
|
||||
<Info title="✅ Sidebar Layout Pattern">
|
||||
Sidebar and main content are both part of the same surface. Sidebar menu items use transparent backgrounds with
|
||||
hover states.
|
||||
</Info>
|
||||
|
||||
<Surface className="bg-surface-1 flex rounded-md">
|
||||
<aside className="border-subtle w-64 border-r p-4">
|
||||
<h4 className="text-primary mb-3 font-semibold">Sidebar</h4>
|
||||
<div className="space-y-2">
|
||||
{["Home", "Projects", "Settings"].map((item) => (
|
||||
<div key={item} className="hover:bg-layer-1-hover rounded-md p-2 transition-colors">
|
||||
<p className="text-primary text-13">{item}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main className="flex-1 p-6">
|
||||
<h4 className="text-primary mb-3 font-semibold">Main Content</h4>
|
||||
<Layer className="bg-layer-1 hover:bg-layer-1-hover rounded-md p-4">
|
||||
<p className="text-primary">Content card using layer-1</p>
|
||||
</Layer>
|
||||
</main>
|
||||
</Surface>
|
||||
</DemoRoot>
|
||||
),
|
||||
};
|
||||
|
||||
export const StateVariants: Story = {
|
||||
render: () => (
|
||||
<DemoRoot>
|
||||
<Info title="✅ State Variants">Demonstrating hover, active, and selected states</Info>
|
||||
|
||||
<Surface>
|
||||
<div className="space-y-4">
|
||||
<Layer className="bg-layer-1 hover:bg-layer-1-hover rounded-md p-4">
|
||||
<h5 className="text-primary mb-1 font-medium">Hover State</h5>
|
||||
<p className="text-secondary text-13">Hover over me to see bg-layer-1-hover</p>
|
||||
</Layer>
|
||||
|
||||
<div className="bg-layer-1-active rounded-md p-4">
|
||||
<h5 className="text-primary mb-1 font-medium">Active State</h5>
|
||||
<p className="text-secondary text-13">Using bg-layer-1-active (pressed/active)</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-layer-1-selected rounded-md p-4">
|
||||
<h5 className="text-primary mb-1 font-medium">Selected State</h5>
|
||||
<p className="text-secondary text-13">Using bg-layer-1-selected (when selected)</p>
|
||||
</div>
|
||||
</div>
|
||||
</Surface>
|
||||
</DemoRoot>
|
||||
),
|
||||
};
|
||||
|
||||
export const TextColorHierarchy: Story = {
|
||||
render: () => (
|
||||
<DemoRoot>
|
||||
<Info title="✅ Text Color Hierarchy">Semantic text colors for different importance levels</Info>
|
||||
|
||||
<Surface>
|
||||
<div className="bg-layer-1 rounded-md p-4">
|
||||
<h4 className="text-primary mb-3 text-16 font-semibold">Primary Text</h4>
|
||||
<p className="text-secondary mb-3">Secondary text for descriptions and supporting content</p>
|
||||
<p className="text-tertiary mb-3 text-13">Tertiary text for labels and metadata</p>
|
||||
<input
|
||||
className="placeholder-placeholder border-subtle rounded border px-3 py-2"
|
||||
placeholder="Placeholder text for inputs"
|
||||
/>
|
||||
</div>
|
||||
</Surface>
|
||||
</DemoRoot>
|
||||
),
|
||||
};
|
||||
|
||||
export const CompleteExample: Story = {
|
||||
render: () => (
|
||||
<DemoRoot>
|
||||
<Info title="✅ Complete Example">A realistic dashboard layout using all design system concepts</Info>
|
||||
|
||||
<div className="bg-surface-1 mb-6 rounded-md">
|
||||
<div className="border-subtle border-b p-4">
|
||||
<h1 className="text-primary text-18 font-bold">Dashboard</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-6">
|
||||
{[
|
||||
{ label: "Total Users", value: "1,234" },
|
||||
{ label: "Active Projects", value: "42" },
|
||||
{ label: "Completed Tasks", value: "856" },
|
||||
].map((stat, idx) => (
|
||||
<Surface key={idx}>
|
||||
<Layer className="bg-layer-1 hover:bg-layer-1-hover rounded-md p-4">
|
||||
<p className="text-tertiary mb-1 text-13">{stat.label}</p>
|
||||
<p className="text-primary text-20 font-bold">{stat.value}</p>
|
||||
</Layer>
|
||||
</Surface>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-2 gap-6">
|
||||
<Surface>
|
||||
<h3 className="text-primary mb-4 font-semibold">Recent Activity</h3>
|
||||
<div className="space-y-2">
|
||||
{[1, 2, 3].map((item) => (
|
||||
<Layer key={item} className="bg-layer-1 hover:bg-layer-1-hover rounded-md p-3">
|
||||
<p className="text-primary mb-1 text-13 font-medium">Activity {item}</p>
|
||||
<p className="text-secondary text-11">Description of the activity</p>
|
||||
</Layer>
|
||||
))}
|
||||
</div>
|
||||
</Surface>
|
||||
|
||||
<Surface className="bg-surface-2 rounded-md p-6">
|
||||
<h3 className="text-primary mb-4 font-semibold">Quick Actions</h3>
|
||||
<div className="space-y-2">
|
||||
{["Create Project", "Invite Team", "View Reports"].map((action) => (
|
||||
<Layer key={action} className="bg-layer-2 hover:bg-layer-2-hover rounded-md p-3">
|
||||
<p className="text-primary text-13">{action}</p>
|
||||
</Layer>
|
||||
))}
|
||||
</div>
|
||||
</Surface>
|
||||
</div>
|
||||
</DemoRoot>
|
||||
),
|
||||
};
|
||||
|
||||
export const CommonMistakes: Story = {
|
||||
render: () => (
|
||||
<DemoRoot>
|
||||
<Info title="❌ Common Mistakes to Avoid" tone="warn">
|
||||
These examples show incorrect usage patterns
|
||||
</Info>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="border-2 border-red-500 rounded-md p-4">
|
||||
<h4 className="text-primary mb-2 font-semibold">❌ Mistake 1: Nested Surfaces (Same Plane)</h4>
|
||||
<Surface>
|
||||
<p className="text-secondary mb-2 text-13">Surface 1</p>
|
||||
<div className="bg-surface-2 rounded-md p-4">
|
||||
<p className="text-secondary text-13">Surface 2 nested inside Surface 1 - WRONG!</p>
|
||||
</div>
|
||||
</Surface>
|
||||
<p className="text-tertiary mt-2 text-11">
|
||||
✅ Fix: Use bg-layer-1 for nested elements, or make them sibling surfaces
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="border-2 border-red-500 rounded-md p-4">
|
||||
<h4 className="text-primary mb-2 font-semibold">❌ Mistake 2: Wrong Layer-Surface Association</h4>
|
||||
<Surface>
|
||||
<p className="text-secondary mb-2 text-13">Surface 1</p>
|
||||
<div className="bg-layer-2 rounded-md p-4">
|
||||
<p className="text-secondary text-13">Using layer-2 with surface-1 for content box - WRONG!</p>
|
||||
</div>
|
||||
</Surface>
|
||||
<p className="text-tertiary mt-2 text-11">
|
||||
✅ Fix: Use bg-layer-1 with bg-surface-1 for content boxes. Exception: Very rare cases for inputs/buttons
|
||||
that need visual separation (e.g., input in modal can use bg-layer-2 for separation).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="border-2 border-red-500 rounded-md p-4">
|
||||
<h4 className="text-primary mb-2 font-semibold">❌ Mistake 3: Mismatched Hover State</h4>
|
||||
<Surface>
|
||||
<div className="bg-layer-1 hover:bg-layer-2-hover rounded-md p-4 transition-colors">
|
||||
<p className="text-secondary text-13">bg-layer-1 with hover:bg-layer-2-hover - WRONG!</p>
|
||||
</div>
|
||||
</Surface>
|
||||
<p className="text-tertiary mt-2 text-11">✅ Fix: Use bg-layer-1 hover:bg-layer-1-hover</p>
|
||||
</div>
|
||||
|
||||
<div className="border-2 border-red-500 rounded-md p-4">
|
||||
<h4 className="text-primary mb-2 font-semibold">❌ Mistake 4: Canvas for Pages</h4>
|
||||
<div className="bg-canvas rounded-md p-4">
|
||||
<p className="text-secondary text-13">Using bg-canvas for a page or component - WRONG!</p>
|
||||
</div>
|
||||
<p className="text-tertiary mt-2 text-11">
|
||||
✅ Fix: Canvas should only be at application root. Use bg-surface-1 for pages
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</DemoRoot>
|
||||
),
|
||||
};
|
||||
|
|
@ -26,7 +26,10 @@ const meta = {
|
|||
|
||||
return (
|
||||
<>
|
||||
<button onClick={() => setOpen(true)} className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600">
|
||||
<button
|
||||
onClick={() => setOpen(true)}
|
||||
className="rounded-sm bg-blue-500 px-4 py-2 text-on-color hover:bg-blue-600"
|
||||
>
|
||||
Open Dialog
|
||||
</button>
|
||||
{open && (
|
||||
|
|
@ -35,18 +38,18 @@ const meta = {
|
|||
<div className="p-6">
|
||||
<Dialog.Title>Dialog Title</Dialog.Title>
|
||||
<div className="mt-4">
|
||||
<p className="text-sm text-gray-600">This is the dialog content. You can put any content here.</p>
|
||||
<p className="text-13 text-gray-600">This is the dialog content. You can put any content here.</p>
|
||||
</div>
|
||||
<div className="mt-6 flex justify-end gap-2">
|
||||
<button
|
||||
onClick={() => setOpen(false)}
|
||||
className="rounded bg-gray-200 px-4 py-2 text-sm hover:bg-gray-300"
|
||||
className="rounded-sm bg-gray-200 px-4 py-2 text-13 hover:bg-gray-300"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setOpen(false)}
|
||||
className="rounded bg-blue-500 px-4 py-2 text-sm text-white hover:bg-blue-600"
|
||||
className="rounded-sm bg-blue-500 px-4 py-2 text-13 text-on-color hover:bg-blue-600"
|
||||
>
|
||||
Confirm
|
||||
</button>
|
||||
|
|
@ -74,7 +77,10 @@ export const TopPosition: Story = {
|
|||
const [open, setOpen] = useState(args.open);
|
||||
return (
|
||||
<>
|
||||
<button onClick={() => setOpen(true)} className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600">
|
||||
<button
|
||||
onClick={() => setOpen(true)}
|
||||
className="rounded-sm bg-blue-500 px-4 py-2 text-on-color hover:bg-blue-600"
|
||||
>
|
||||
Open Dialog (Top)
|
||||
</button>
|
||||
{open && (
|
||||
|
|
@ -83,14 +89,14 @@ export const TopPosition: Story = {
|
|||
<div className="p-6">
|
||||
<Dialog.Title>Top Positioned Dialog</Dialog.Title>
|
||||
<div className="mt-4">
|
||||
<p className="text-sm text-gray-600">
|
||||
<p className="text-13 text-gray-600">
|
||||
This dialog appears at the top of the screen instead of centered.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-6 flex justify-end gap-2">
|
||||
<button
|
||||
onClick={() => setOpen(false)}
|
||||
className="rounded bg-gray-200 px-4 py-2 text-sm hover:bg-gray-300"
|
||||
className="rounded-sm bg-gray-200 px-4 py-2 text-13 hover:bg-gray-300"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
|
|
@ -109,7 +115,10 @@ export const SmallWidth: Story = {
|
|||
const [open, setOpen] = useState(args.open);
|
||||
return (
|
||||
<>
|
||||
<button onClick={() => setOpen(true)} className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600">
|
||||
<button
|
||||
onClick={() => setOpen(true)}
|
||||
className="rounded-sm bg-blue-500 px-4 py-2 text-on-color hover:bg-blue-600"
|
||||
>
|
||||
Open Small Dialog
|
||||
</button>
|
||||
{open && (
|
||||
|
|
@ -118,12 +127,12 @@ export const SmallWidth: Story = {
|
|||
<div className="p-6">
|
||||
<Dialog.Title>Small Dialog</Dialog.Title>
|
||||
<div className="mt-4">
|
||||
<p className="text-sm text-gray-600">This is a small dialog.</p>
|
||||
<p className="text-13 text-gray-600">This is a small dialog.</p>
|
||||
</div>
|
||||
<div className="mt-6 flex justify-end">
|
||||
<button
|
||||
onClick={() => setOpen(false)}
|
||||
className="rounded bg-blue-500 px-4 py-2 text-sm text-white hover:bg-blue-600"
|
||||
className="rounded-sm bg-blue-500 px-4 py-2 text-13 text-on-color hover:bg-blue-600"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
|
|
@ -142,7 +151,10 @@ export const LargeWidth: Story = {
|
|||
const [open, setOpen] = useState(args.open);
|
||||
return (
|
||||
<>
|
||||
<button onClick={() => setOpen(true)} className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600">
|
||||
<button
|
||||
onClick={() => setOpen(true)}
|
||||
className="rounded-sm bg-blue-500 px-4 py-2 text-on-color hover:bg-blue-600"
|
||||
>
|
||||
Open Large Dialog
|
||||
</button>
|
||||
{open && (
|
||||
|
|
@ -151,14 +163,14 @@ export const LargeWidth: Story = {
|
|||
<div className="p-6">
|
||||
<Dialog.Title>Large Dialog</Dialog.Title>
|
||||
<div className="mt-4">
|
||||
<p className="text-sm text-gray-600">
|
||||
<p className="text-13 text-gray-600">
|
||||
This is a large dialog with more horizontal space for content.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-6 flex justify-end">
|
||||
<button
|
||||
onClick={() => setOpen(false)}
|
||||
className="rounded bg-blue-500 px-4 py-2 text-sm text-white hover:bg-blue-600"
|
||||
className="rounded-sm bg-blue-500 px-4 py-2 text-13 text-on-color hover:bg-blue-600"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
|
|
@ -177,7 +189,10 @@ export const WithCloseButton: Story = {
|
|||
const [open, setOpen] = useState(args.open);
|
||||
return (
|
||||
<>
|
||||
<button onClick={() => setOpen(true)} className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600">
|
||||
<button
|
||||
onClick={() => setOpen(true)}
|
||||
className="rounded-sm bg-blue-500 px-4 py-2 text-on-color hover:bg-blue-600"
|
||||
>
|
||||
Open Dialog with Close Button
|
||||
</button>
|
||||
{open && (
|
||||
|
|
@ -191,7 +206,7 @@ export const WithCloseButton: Story = {
|
|||
</button>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<p className="text-sm text-gray-600">This dialog has a close button in the header.</p>
|
||||
<p className="text-13 text-gray-600">This dialog has a close button in the header.</p>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
|
|
@ -211,7 +226,10 @@ export const ConfirmationDialog: Story = {
|
|||
};
|
||||
return (
|
||||
<>
|
||||
<button onClick={() => setOpen(true)} className="rounded bg-red-500 px-4 py-2 text-white hover:bg-red-600">
|
||||
<button
|
||||
onClick={() => setOpen(true)}
|
||||
className="rounded-sm bg-red-500 px-4 py-2 text-on-color hover:bg-red-600"
|
||||
>
|
||||
Delete Item
|
||||
</button>
|
||||
{open && (
|
||||
|
|
@ -220,20 +238,20 @@ export const ConfirmationDialog: Story = {
|
|||
<div className="p-6">
|
||||
<Dialog.Title>Confirm Deletion</Dialog.Title>
|
||||
<div className="mt-4">
|
||||
<p className="text-sm text-gray-600">
|
||||
<p className="text-13 text-gray-600">
|
||||
Are you sure you want to delete this item? This action cannot be undone.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-6 flex justify-end gap-2">
|
||||
<button
|
||||
onClick={() => setOpen(false)}
|
||||
className="rounded bg-gray-200 px-4 py-2 text-sm hover:bg-gray-300"
|
||||
className="rounded-sm bg-gray-200 px-4 py-2 text-13 hover:bg-gray-300"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={handleConfirm}
|
||||
className="rounded bg-red-500 px-4 py-2 text-sm text-white hover:bg-red-600"
|
||||
className="rounded-sm bg-red-500 px-4 py-2 text-13 text-on-color hover:bg-red-600"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
|
|
@ -257,7 +275,10 @@ export const FormDialog: Story = {
|
|||
};
|
||||
return (
|
||||
<>
|
||||
<button onClick={() => setOpen(true)} className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600">
|
||||
<button
|
||||
onClick={() => setOpen(true)}
|
||||
className="rounded-sm bg-blue-500 px-4 py-2 text-on-color hover:bg-blue-600"
|
||||
>
|
||||
Open Form
|
||||
</button>
|
||||
{open && (
|
||||
|
|
@ -267,24 +288,24 @@ export const FormDialog: Story = {
|
|||
<Dialog.Title>Create New Item</Dialog.Title>
|
||||
<div className="mt-4 space-y-4">
|
||||
<div>
|
||||
<label htmlFor="name" className="block text-sm font-medium text-gray-700">
|
||||
<label htmlFor="name" className="block text-13 font-medium text-gray-700">
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
className="mt-1 w-full rounded border border-gray-300 px-3 py-2 text-sm"
|
||||
className="mt-1 w-full rounded-sm border border-gray-300 px-3 py-2 text-13"
|
||||
placeholder="Enter name"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="description" className="block text-sm font-medium text-gray-700">
|
||||
<label htmlFor="description" className="block text-13 font-medium text-gray-700">
|
||||
Description
|
||||
</label>
|
||||
<textarea
|
||||
id="description"
|
||||
rows={3}
|
||||
className="mt-1 w-full rounded border border-gray-300 px-3 py-2 text-sm"
|
||||
className="mt-1 w-full rounded-sm border border-gray-300 px-3 py-2 text-13"
|
||||
placeholder="Enter description"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -293,11 +314,14 @@ export const FormDialog: Story = {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(false)}
|
||||
className="rounded bg-gray-200 px-4 py-2 text-sm hover:bg-gray-300"
|
||||
className="rounded-sm bg-gray-200 px-4 py-2 text-13 hover:bg-gray-300"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" className="rounded bg-blue-500 px-4 py-2 text-sm text-white hover:bg-blue-600">
|
||||
<button
|
||||
type="submit"
|
||||
className="rounded-sm bg-blue-500 px-4 py-2 text-13 text-on-color hover:bg-blue-600"
|
||||
>
|
||||
Create
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -315,7 +339,10 @@ export const ScrollableContent: Story = {
|
|||
const [open, setOpen] = useState(args.open);
|
||||
return (
|
||||
<>
|
||||
<button onClick={() => setOpen(true)} className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600">
|
||||
<button
|
||||
onClick={() => setOpen(true)}
|
||||
className="rounded-sm bg-blue-500 px-4 py-2 text-on-color hover:bg-blue-600"
|
||||
>
|
||||
Open Scrollable Dialog
|
||||
</button>
|
||||
{open && (
|
||||
|
|
@ -325,7 +352,7 @@ export const ScrollableContent: Story = {
|
|||
<Dialog.Title>Scrollable Content</Dialog.Title>
|
||||
<div className="mt-4 max-h-96 overflow-y-auto">
|
||||
{Array.from({ length: 20 }, (_, i) => (
|
||||
<p key={i} className="mb-2 text-sm text-gray-600">
|
||||
<p key={i} className="mb-2 text-13 text-gray-600">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.
|
||||
</p>
|
||||
|
|
@ -334,7 +361,7 @@ export const ScrollableContent: Story = {
|
|||
<div className="mt-6 flex justify-end">
|
||||
<button
|
||||
onClick={() => setOpen(false)}
|
||||
className="rounded bg-blue-500 px-4 py-2 text-sm text-white hover:bg-blue-600"
|
||||
className="rounded-sm bg-blue-500 px-4 py-2 text-13 text-on-color hover:bg-blue-600"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
|
|
@ -368,7 +395,7 @@ export const AllWidths: Story = {
|
|||
<button
|
||||
key={width}
|
||||
onClick={() => setOpenWidth(width)}
|
||||
className="rounded bg-blue-500 px-4 py-2 text-sm text-white hover:bg-blue-600"
|
||||
className="rounded-sm bg-blue-500 px-4 py-2 text-13 text-on-color hover:bg-blue-600"
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
|
|
@ -379,12 +406,12 @@ export const AllWidths: Story = {
|
|||
<div className="p-6">
|
||||
<Dialog.Title>{label} Dialog</Dialog.Title>
|
||||
<div className="mt-4">
|
||||
<p className="text-sm text-gray-600">This dialog uses the {label} width variant.</p>
|
||||
<p className="text-13 text-gray-600">This dialog uses the {label} width variant.</p>
|
||||
</div>
|
||||
<div className="mt-6 flex justify-end">
|
||||
<button
|
||||
onClick={() => setOpenWidth(null)}
|
||||
className="rounded bg-blue-500 px-4 py-2 text-sm text-white hover:bg-blue-600"
|
||||
className="rounded-sm bg-blue-500 px-4 py-2 text-13 text-on-color hover:bg-blue-600"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ export interface DialogTitleProps extends React.ComponentProps<typeof BaseDialog
|
|||
}
|
||||
|
||||
// Constants
|
||||
const OVERLAY_CLASSNAME = cn("fixed inset-0 z-backdrop bg-custom-backdrop");
|
||||
const BASE_CLASSNAME = "relative text-left bg-custom-background-100 rounded-lg shadow-md w-full z-modal";
|
||||
const OVERLAY_CLASSNAME = cn("fixed inset-0 z-backdrop bg-backdrop");
|
||||
const BASE_CLASSNAME = "relative text-left bg-surface-1 rounded-lg shadow-md w-full z-modal";
|
||||
|
||||
// Utility functions
|
||||
const getPositionClassNames = (position: DialogPosition) =>
|
||||
|
|
@ -113,7 +113,7 @@ const DialogTitle = memo(function DialogTitle({ className, children, ...props }:
|
|||
return (
|
||||
<BaseDialog.Title
|
||||
data-slot="dialog-title"
|
||||
className={cn("text-lg leading-none font-semibold", className)}
|
||||
className={cn("text-16 leading-none font-semibold", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@ export const Default: Story = {
|
|||
closeOnSelect
|
||||
/>
|
||||
{selectedValue && (
|
||||
<div className="text-sm p-4 bg-custom-background-80 rounded border border-custom-border-200">
|
||||
<div className="text-13 p-4 bg-layer-1 rounded-sm border border-subtle">
|
||||
<div className="font-medium mb-2">Selected:</div>
|
||||
<pre className="text-xs">{JSON.stringify(selectedValue, null, 2)}</pre>
|
||||
<pre className="text-11">{JSON.stringify(selectedValue, null, 2)}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -70,7 +70,7 @@ export const OpenToEmojiTab: Story = {
|
|||
closeOnSelect
|
||||
/>
|
||||
{selectedValue && (
|
||||
<div className="text-sm">Selected: {selectedValue.type === "emoji" ? selectedValue.value : "Icon"}</div>
|
||||
<div className="text-13">Selected: {selectedValue.type === "emoji" ? selectedValue.value : "Icon"}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
@ -99,7 +99,7 @@ export const OpenToIconTab: Story = {
|
|||
closeOnSelect
|
||||
/>
|
||||
{selectedValue && (
|
||||
<div className="text-sm">
|
||||
<div className="text-13">
|
||||
Selected:{" "}
|
||||
{selectedValue.type === "icon" && typeof selectedValue.value === "object"
|
||||
? selectedValue.value.name
|
||||
|
|
@ -134,9 +134,9 @@ export const LucideIcons: Story = {
|
|||
iconType="lucide"
|
||||
/>
|
||||
{selectedValue && (
|
||||
<div className="text-sm p-4 bg-custom-background-80 rounded border border-custom-border-200">
|
||||
<div className="text-13 p-4 bg-layer-1 rounded-sm border border-subtle">
|
||||
<div className="font-medium mb-2">Selected Icon:</div>
|
||||
<pre className="text-xs">{JSON.stringify(selectedValue, null, 2)}</pre>
|
||||
<pre className="text-11">{JSON.stringify(selectedValue, null, 2)}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -167,9 +167,9 @@ export const MaterialIcons: Story = {
|
|||
iconType="material"
|
||||
/>
|
||||
{selectedValue && (
|
||||
<div className="text-sm p-4 bg-custom-background-80 rounded border border-custom-border-200">
|
||||
<div className="text-13 p-4 bg-layer-1 rounded-sm border border-subtle">
|
||||
<div className="font-medium mb-2">Selected Icon:</div>
|
||||
<pre className="text-xs">{JSON.stringify(selectedValue, null, 2)}</pre>
|
||||
<pre className="text-11">{JSON.stringify(selectedValue, null, 2)}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -204,18 +204,18 @@ export const CloseOnSelectDisabled: Story = {
|
|||
closeOnSelect={false}
|
||||
/>
|
||||
<button
|
||||
className="px-3 py-1.5 text-sm bg-custom-background-80 rounded hover:bg-custom-background-90"
|
||||
className="px-3 py-1.5 text-13 bg-layer-1 rounded-sm hover:bg-surface-2"
|
||||
onClick={() => setSelectedValues([])}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
</div>
|
||||
{selectedValues.length > 0 && (
|
||||
<div className="text-sm p-4 bg-custom-background-80 rounded border border-custom-border-200">
|
||||
<div className="text-13 p-4 bg-layer-1 rounded-sm border border-subtle">
|
||||
<div className="font-medium mb-2">Selected ({selectedValues.length}):</div>
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{selectedValues.map((val, idx) => (
|
||||
<span key={idx} className="text-lg">
|
||||
<span key={idx} className="text-16">
|
||||
{val.type === "emoji" ? val.value : "🎨"}
|
||||
</span>
|
||||
))}
|
||||
|
|
@ -249,7 +249,7 @@ export const CustomSearchPlaceholder: Story = {
|
|||
closeOnSelect
|
||||
searchPlaceholder="Type to find emojis..."
|
||||
/>
|
||||
{selectedValue && <div className="text-sm">Selected: {JSON.stringify(selectedValue)}</div>}
|
||||
{selectedValue && <div className="text-13">Selected: {JSON.stringify(selectedValue)}</div>}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
@ -277,7 +277,7 @@ export const SearchDisabled: Story = {
|
|||
closeOnSelect
|
||||
searchDisabled
|
||||
/>
|
||||
{selectedValue && <div className="text-sm">Selected: {JSON.stringify(selectedValue)}</div>}
|
||||
{selectedValue && <div className="text-13">Selected: {JSON.stringify(selectedValue)}</div>}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
@ -306,8 +306,8 @@ export const CustomIconColor: Story = {
|
|||
defaultIconColor="#FF5733"
|
||||
/>
|
||||
{selectedValue && (
|
||||
<div className="text-sm p-4 bg-custom-background-80 rounded border border-custom-border-200">
|
||||
<pre className="text-xs">{JSON.stringify(selectedValue, null, 2)}</pre>
|
||||
<div className="text-13 p-4 bg-layer-1 rounded-sm border border-subtle">
|
||||
<pre className="text-11">{JSON.stringify(selectedValue, null, 2)}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -331,7 +331,7 @@ export const DifferentPlacements: Story = {
|
|||
return (
|
||||
<div className="p-8 space-y-8">
|
||||
<div className="flex gap-4 items-center">
|
||||
<span className="text-sm w-32">Bottom Start:</span>
|
||||
<span className="text-13 w-32">Bottom Start:</span>
|
||||
<EmojiPicker
|
||||
isOpen={isOpen1}
|
||||
handleToggle={setIsOpen1}
|
||||
|
|
@ -341,7 +341,7 @@ export const DifferentPlacements: Story = {
|
|||
/>
|
||||
</div>
|
||||
<div className="flex gap-4 items-center">
|
||||
<span className="text-sm w-32">Bottom End:</span>
|
||||
<span className="text-13 w-32">Bottom End:</span>
|
||||
<EmojiPicker
|
||||
isOpen={isOpen2}
|
||||
handleToggle={setIsOpen2}
|
||||
|
|
@ -351,7 +351,7 @@ export const DifferentPlacements: Story = {
|
|||
/>
|
||||
</div>
|
||||
<div className="flex gap-4 items-center">
|
||||
<span className="text-sm w-32">Top Start:</span>
|
||||
<span className="text-13 w-32">Top Start:</span>
|
||||
<EmojiPicker
|
||||
isOpen={isOpen3}
|
||||
handleToggle={setIsOpen3}
|
||||
|
|
@ -361,7 +361,7 @@ export const DifferentPlacements: Story = {
|
|||
/>
|
||||
</div>
|
||||
<div className="flex gap-4 items-center">
|
||||
<span className="text-sm w-32">Top End:</span>
|
||||
<span className="text-13 w-32">Top End:</span>
|
||||
<EmojiPicker
|
||||
isOpen={isOpen4}
|
||||
handleToggle={setIsOpen4}
|
||||
|
|
@ -400,19 +400,19 @@ export const InFormContext: Story = {
|
|||
|
||||
return (
|
||||
<div className="max-w-md p-4">
|
||||
<form onSubmit={handleSubmit} className="space-y-4 p-6 border border-custom-border-200 rounded-lg">
|
||||
<form onSubmit={handleSubmit} className="space-y-4 p-6 border border-subtle rounded-lg">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Project Title</label>
|
||||
<label className="block text-13 font-medium mb-2">Project Title</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.title}
|
||||
onChange={(e) => setFormData((prev) => ({ ...prev, title: e.target.value }))}
|
||||
className="w-full px-3 py-2 bg-custom-background-80 border border-custom-border-200 rounded"
|
||||
className="w-full px-3 py-2 bg-layer-1 border border-subtle rounded-sm"
|
||||
placeholder="Enter project title"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Project Icon</label>
|
||||
<label className="block text-13 font-medium mb-2">Project Icon</label>
|
||||
<EmojiPicker
|
||||
isOpen={isOpen}
|
||||
handleToggle={setIsOpen}
|
||||
|
|
@ -420,12 +420,12 @@ export const InFormContext: Story = {
|
|||
label={formData.emoji && formData.emoji.type === "emoji" ? formData.emoji.value : "Click to select icon"}
|
||||
defaultOpen={EmojiIconPickerTypes.EMOJI}
|
||||
closeOnSelect
|
||||
buttonClassName="px-4 py-2 bg-custom-background-80 border border-custom-border-200 rounded hover:bg-custom-background-90 w-full text-left"
|
||||
buttonClassName="px-4 py-2 bg-layer-1 border border-subtle rounded-sm hover:bg-surface-2 w-full text-left"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full px-4 py-2 bg-custom-primary-100 text-white rounded hover:bg-custom-primary-200"
|
||||
className="w-full px-4 py-2 bg-accent-primary text-on-color rounded-sm hover:bg-accent-primary/80"
|
||||
>
|
||||
Create Project
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -100,10 +100,7 @@ export function EmojiPicker(props: TCustomEmojiPicker) {
|
|||
</Popover.Button>
|
||||
<Popover.Panel
|
||||
positionerClassName="z-50"
|
||||
className={cn(
|
||||
"w-80 bg-custom-background-100 rounded-md border-[0.5px] border-custom-border-300 overflow-hidden",
|
||||
dropdownClassName
|
||||
)}
|
||||
className={cn("w-80 bg-surface-1 rounded-md border-[0.5px] border-strong overflow-hidden", dropdownClassName)}
|
||||
side={finalSide}
|
||||
align={finalAlign}
|
||||
sideOffset={8}
|
||||
|
|
@ -129,9 +126,9 @@ export function EmojiPicker(props: TCustomEmojiPicker) {
|
|||
key={tab.key}
|
||||
value={tab.key}
|
||||
className={({ selected }) =>
|
||||
cn("py-1 text-sm rounded border border-custom-border-200 bg-custom-background-80", {
|
||||
"bg-custom-background-100 text-custom-text-100": selected,
|
||||
"text-custom-text-400 hover:text-custom-text-300 hover:bg-custom-background-80/60": !selected,
|
||||
cn("py-1 text-13 rounded-sm border border-subtle bg-layer-1", {
|
||||
"bg-surface-1 text-primary": selected,
|
||||
"text-placeholder hover:text-tertiary hover:bg-layer-1/60": !selected,
|
||||
})
|
||||
}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -31,17 +31,17 @@ export function EmojiRoot(props: EmojiRootProps) {
|
|||
className="isolate flex flex-col rounded-md h-full w-full border-none p-2"
|
||||
onEmojiSelect={(val) => onChange(val.emoji)}
|
||||
>
|
||||
<div className="flex items-center gap-2 justify-between [&>[data-slot='emoji-picker-search-wrapper']]:flex-grow [&>[data-slot='emoji-picker-search-wrapper']]:p-0 px-1.5 py-2 sticky top-0 z-10 bg-custom-background-100">
|
||||
<div className="flex items-center gap-2 justify-between [&>[data-slot='emoji-picker-search-wrapper']]:flex-grow [&>[data-slot='emoji-picker-search-wrapper']]:p-0 px-1.5 py-2 sticky top-0 z-10 bg-surface-1">
|
||||
<div ref={searchWrapperRef} data-slot="emoji-picker-search-wrapper" className="p-2">
|
||||
<EmojiPicker.Search
|
||||
placeholder={searchPlaceholder}
|
||||
disabled={searchDisabled}
|
||||
className="block rounded-md bg-transparent placeholder-custom-text-400 focus:outline-none px-3 py-2 border-[0.5px] border-custom-border-200 text-[1rem] p-0 h-full w-full flex-grow-0 focus:border-custom-primary-100"
|
||||
className="block rounded-md bg-transparent placeholder-custom-text-400 focus:outline-none px-3 py-2 border-[0.5px] border-subtle text-16 p-0 h-full w-full flex-grow-0 focus:border-accent-strong"
|
||||
/>
|
||||
</div>
|
||||
<EmojiPicker.SkinToneSelector
|
||||
data-slot="emoji-picker-skin-tone-selector"
|
||||
className="bg-custom-background-100 hover:bg-accent mx-2 mb-1.5 size-8 rounded-md text-lg flex-shrink-0"
|
||||
className="bg-surface-1 hover:bg-accent mx-2 mb-1.5 size-8 rounded-md text-16 flex-shrink-0"
|
||||
/>
|
||||
</div>
|
||||
<EmojiPicker.Viewport data-slot="emoji-picker-content" className={cn("relative flex-1 outline-none")}>
|
||||
|
|
@ -52,7 +52,7 @@ export function EmojiRoot(props: EmojiRootProps) {
|
|||
CategoryHeader: ({ category, ...props }) => (
|
||||
<div
|
||||
data-slot="emoji-picker-list-category-header"
|
||||
className="bg-custom-background-100 text-custom-text-300 px-3 pb-1.5 text-xs font-medium"
|
||||
className="bg-surface-1 text-tertiary px-3 pb-1.5 text-11 font-medium"
|
||||
{...props}
|
||||
>
|
||||
{category.label}
|
||||
|
|
@ -68,7 +68,7 @@ export function EmojiRoot(props: EmojiRootProps) {
|
|||
type="button"
|
||||
aria-label={emoji?.label ?? emoji?.emoji}
|
||||
data-slot="emoji-picker-list-emoji"
|
||||
className="data-active:bg-accent flex size-8 items-center justify-center rounded-md text-lg"
|
||||
className="data-active:bg-accent flex size-8 items-center justify-center rounded-md text-16"
|
||||
{...props}
|
||||
>
|
||||
{emoji.emoji}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue