@esic-lab/data-core-ui 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +784 -0
- package/dist/index.d.mts +111 -0
- package/dist/index.d.ts +111 -0
- package/dist/index.js +2004 -0
- package/dist/index.mjs +1980 -0
- package/package.json +10 -3
- package/.storybook/main.ts +0 -12
- package/.storybook/preview.ts +0 -16
- package/.storybook/vitest.setup.ts +0 -7
- package/postcss.config.js +0 -5
- package/src/Button/GhostButton/GhostButton.stories.tsx +0 -20
- package/src/Button/GhostButton/GhostButton.tsx +0 -26
- package/src/Button/PrimaryButton/PrimaryButton.stories.tsx +0 -21
- package/src/Button/PrimaryButton/PrimaryButton.tsx +0 -35
- package/src/Button/SecondaryButton/SecondaryButton.stories.tsx +0 -16
- package/src/Button/SecondaryButton/SecondaryButton.tsx +0 -26
- package/src/Checkbox/Checkbox/Checkbox.stories.tsx +0 -17
- package/src/Checkbox/Checkbox/Checkbox.tsx +0 -27
- package/src/Checkbox/CheckboxGroup/CheckboxGroup.stories.tsx +0 -35
- package/src/Checkbox/CheckboxGroup/CheckboxGroup.tsx +0 -23
- package/src/Loader/Loader/Loader.stories.tsx +0 -16
- package/src/Loader/Loader/Loader.tsx +0 -12
- package/src/NavBar/MenuNavBar/MenuNavBar.stories.tsx +0 -85
- package/src/NavBar/MenuNavBar/MenuNavBar.tsx +0 -43
- package/src/NavBar/TopNavBar/TopNavBar.stories.tsx +0 -16
- package/src/NavBar/TopNavBar/TopNavBar.tsx +0 -24
- package/src/Radio/Radio/Radio.stories.tsx +0 -17
- package/src/Radio/Radio/Radio.tsx +0 -15
- package/src/Radio/RadioGroup/RadioGroup.stories.tsx +0 -23
- package/src/Radio/RadioGroup/RadioGroup.tsx +0 -27
- package/src/Switch/Switch/Switch.stories.tsx +0 -18
- package/src/Switch/Switch/Switch.tsx +0 -25
- package/src/index.css +0 -260
- package/src/index.ts +0 -24
- package/stories/Button.stories.ts +0 -49
- package/stories/Button.tsx +0 -33
- package/stories/Configure.mdx +0 -364
- package/stories/Header.stories.ts +0 -29
- package/stories/Header.tsx +0 -47
- package/stories/Page.stories.ts +0 -28
- package/stories/Page.tsx +0 -69
- package/stories/assets/accessibility.png +0 -0
- package/stories/assets/accessibility.svg +0 -1
- package/stories/assets/addon-library.png +0 -0
- package/stories/assets/assets.png +0 -0
- package/stories/assets/avif-test-image.avif +0 -0
- package/stories/assets/context.png +0 -0
- package/stories/assets/discord.svg +0 -1
- package/stories/assets/docs.png +0 -0
- package/stories/assets/figma-plugin.png +0 -0
- package/stories/assets/github.svg +0 -1
- package/stories/assets/share.png +0 -0
- package/stories/assets/styling.png +0 -0
- package/stories/assets/testing.png +0 -0
- package/stories/assets/theming.png +0 -0
- package/stories/assets/tutorials.svg +0 -1
- package/stories/assets/youtube.svg +0 -1
- package/stories/button.css +0 -30
- package/stories/header.css +0 -32
- package/stories/page.css +0 -68
- package/tsconfig.json +0 -16
- package/vitest.config.js +0 -35
- /package/{src/assets/STO-logo.svg → dist/STO-logo-ADYYAPS3.svg} +0 -0
package/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@esic-lab/data-core-ui",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"main": "index.
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"main": "dist/index.cjs",
|
|
5
|
+
"module": "dist/index.mjs",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
5
10
|
"devDependencies": {
|
|
6
11
|
"@chromatic-com/storybook": "^4.1.1",
|
|
7
12
|
"@iconify/react": "^6.0.1",
|
|
@@ -22,13 +27,15 @@
|
|
|
22
27
|
"storybook": "^9.1.3",
|
|
23
28
|
"tailwindcss": "^4.1.12",
|
|
24
29
|
"ts-node": "^10.9.2",
|
|
30
|
+
"tsup": "^8.5.0",
|
|
25
31
|
"typescript": "^5.9.2",
|
|
26
32
|
"vitest": "^3.2.4"
|
|
27
33
|
},
|
|
28
34
|
"scripts": {
|
|
29
35
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
30
36
|
"storybook": "storybook dev -p 6006",
|
|
31
|
-
"build-storybook": "storybook build"
|
|
37
|
+
"build-storybook": "storybook build",
|
|
38
|
+
"build": "tsup src/index.ts --dts --dts-resolve --format esm,cjs --out-dir dist"
|
|
32
39
|
},
|
|
33
40
|
"keywords": [],
|
|
34
41
|
"author": "",
|
package/.storybook/main.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { StorybookConfig } from "@storybook/react-vite";
|
|
2
|
-
|
|
3
|
-
const config: StorybookConfig = {
|
|
4
|
-
stories: ["../src/**/*.stories.@(ts|tsx|js|jsx|mdx)"],
|
|
5
|
-
addons: ["@storybook/addon-links", "@storybook/addon-essentials", "@storybook/addon-interactions"],
|
|
6
|
-
framework: {
|
|
7
|
-
name: "@storybook/react-vite",
|
|
8
|
-
options: {},
|
|
9
|
-
},
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export default config;
|
package/.storybook/preview.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { Preview } from "@storybook/react";
|
|
2
|
-
import "../src/index.css";
|
|
3
|
-
|
|
4
|
-
const preview: Preview = {
|
|
5
|
-
parameters: {
|
|
6
|
-
actions: { argTypesRegex: "^on[A-Z].*" },
|
|
7
|
-
controls: {
|
|
8
|
-
matchers: {
|
|
9
|
-
color: /(background|color)$/i,
|
|
10
|
-
date: /Date$/,
|
|
11
|
-
},
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export default preview;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
|
|
2
|
-
import { setProjectAnnotations } from '@storybook/react-vite';
|
|
3
|
-
import * as projectAnnotations from './preview';
|
|
4
|
-
|
|
5
|
-
// This is an important step to apply the right configuration when testing your stories.
|
|
6
|
-
// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
|
|
7
|
-
setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]);
|
package/postcss.config.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
|
|
3
|
-
import { GhostButton } from "./GhostButton";
|
|
4
|
-
|
|
5
|
-
import { IconPlus } from "@tabler/icons-react";
|
|
6
|
-
|
|
7
|
-
const meta = {
|
|
8
|
-
component: GhostButton,
|
|
9
|
-
} satisfies Meta<typeof GhostButton>;
|
|
10
|
-
|
|
11
|
-
export default meta;
|
|
12
|
-
type Story = StoryObj<typeof meta>;
|
|
13
|
-
|
|
14
|
-
export const Default: Story = {
|
|
15
|
-
args: {
|
|
16
|
-
title: "สร้างโครงการ",
|
|
17
|
-
onClick: () => alert("onClick"),
|
|
18
|
-
iconLeft: <IconPlus size={16} />,
|
|
19
|
-
},
|
|
20
|
-
};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
type ColorScale = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
|
|
2
|
-
type BaseColor = "primary" | "gray" | "green" | "red" | "yellow" | "blue";
|
|
3
|
-
type ThemeColor = `bg-${BaseColor}-${ColorScale}`;
|
|
4
|
-
|
|
5
|
-
interface GhostButtonProps {
|
|
6
|
-
title: string;
|
|
7
|
-
onClick: () => void;
|
|
8
|
-
iconLeft?: React.ReactNode;
|
|
9
|
-
iconRight?: React.ReactNode;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
//TODO: disabled button, on hover, loading
|
|
13
|
-
export function GhostButton({ title, onClick, iconLeft, iconRight }: GhostButtonProps) {
|
|
14
|
-
return (
|
|
15
|
-
<button
|
|
16
|
-
className={`flex justify-center w-full h-[42px] rounded-[6px] p-[10px] cursor-pointer bg-[#E9E9E9]`}
|
|
17
|
-
onClick={onClick}
|
|
18
|
-
>
|
|
19
|
-
<div className="flex justify-center items-center gap-[10px]">
|
|
20
|
-
{iconLeft && <div>{iconLeft}</div>}
|
|
21
|
-
{title}
|
|
22
|
-
{iconRight && <div>{iconRight}</div>}
|
|
23
|
-
</div>
|
|
24
|
-
</button>
|
|
25
|
-
);
|
|
26
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
|
|
3
|
-
import { PrimaryButton } from "./PrimaryButton";
|
|
4
|
-
|
|
5
|
-
import { IconPlus } from "@tabler/icons-react";
|
|
6
|
-
|
|
7
|
-
const meta = {
|
|
8
|
-
component: PrimaryButton,
|
|
9
|
-
} satisfies Meta<typeof PrimaryButton>;
|
|
10
|
-
|
|
11
|
-
export default meta;
|
|
12
|
-
type Story = StoryObj<typeof meta>;
|
|
13
|
-
|
|
14
|
-
export const Default: Story = {
|
|
15
|
-
args: {
|
|
16
|
-
title: "สร้างโครงการ",
|
|
17
|
-
onClick: () => alert("onClick"),
|
|
18
|
-
iconLeft: <IconPlus size={16} />,
|
|
19
|
-
textColor: "white",
|
|
20
|
-
},
|
|
21
|
-
};
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
type ColorScale = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
|
|
2
|
-
type BaseColor = "primary" | "gray" | "green" | "red" | "yellow" | "blue";
|
|
3
|
-
type ThemeColor = `bg-${BaseColor}-${ColorScale}`;
|
|
4
|
-
|
|
5
|
-
interface PrimaryButtonProps {
|
|
6
|
-
title: string;
|
|
7
|
-
onClick: () => void;
|
|
8
|
-
iconLeft?: React.ReactNode;
|
|
9
|
-
iconRight?: React.ReactNode;
|
|
10
|
-
bgColor?: ThemeColor;
|
|
11
|
-
textColor?: "white" | "black";
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
//TODO: disabled button, on hover, loading
|
|
15
|
-
export function PrimaryButton({
|
|
16
|
-
title,
|
|
17
|
-
onClick,
|
|
18
|
-
iconLeft,
|
|
19
|
-
iconRight,
|
|
20
|
-
bgColor = "bg-primary-500",
|
|
21
|
-
textColor = "black",
|
|
22
|
-
}: PrimaryButtonProps) {
|
|
23
|
-
return (
|
|
24
|
-
<button
|
|
25
|
-
className={`flex justify-center w-full h-[42px] rounded-[6px] p-[10px] cursor-pointer ${bgColor} text-${textColor}`}
|
|
26
|
-
onClick={onClick}
|
|
27
|
-
>
|
|
28
|
-
<div className="flex justify-center items-center gap-[10px]">
|
|
29
|
-
{iconLeft && <div>{iconLeft}</div>}
|
|
30
|
-
{title}
|
|
31
|
-
{iconRight && <div>{iconRight}</div>}
|
|
32
|
-
</div>
|
|
33
|
-
</button>
|
|
34
|
-
);
|
|
35
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
|
|
3
|
-
import { SecondaryButton } from "./SecondaryButton";
|
|
4
|
-
|
|
5
|
-
const meta = {
|
|
6
|
-
component: SecondaryButton,
|
|
7
|
-
} satisfies Meta<typeof SecondaryButton>;
|
|
8
|
-
|
|
9
|
-
export default meta;
|
|
10
|
-
type Story = StoryObj<typeof SecondaryButton>;
|
|
11
|
-
|
|
12
|
-
export const Default: Story = {
|
|
13
|
-
render: () => {
|
|
14
|
-
return <SecondaryButton title="Demo" onClick={() => alert("onClick")} />;
|
|
15
|
-
},
|
|
16
|
-
};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
type ColorScale = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
|
|
2
|
-
type BaseColor = "primary" | "gray" | "green" | "red" | "yellow" | "blue";
|
|
3
|
-
type ThemeColor = `bg-${BaseColor}-${ColorScale}`;
|
|
4
|
-
|
|
5
|
-
interface SecondaryButtonProps {
|
|
6
|
-
title: string;
|
|
7
|
-
onClick: () => void;
|
|
8
|
-
iconLeft?: React.ReactNode;
|
|
9
|
-
iconRight?: React.ReactNode;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
//TODO: disabled button, on hover
|
|
13
|
-
export function SecondaryButton({ title, onClick, iconLeft, iconRight }: SecondaryButtonProps) {
|
|
14
|
-
return (
|
|
15
|
-
<button
|
|
16
|
-
className={`flex justify-center w-full h-[42px] rounded-[6px] p-[10px] border-[1px] border-black cursor-pointer bg-white text-black`}
|
|
17
|
-
onClick={onClick}
|
|
18
|
-
>
|
|
19
|
-
<div className="flex justify-center items-center gap-[10px]">
|
|
20
|
-
{iconLeft && <div>{iconLeft}</div>}
|
|
21
|
-
{title}
|
|
22
|
-
{iconRight && <div>{iconRight}</div>}
|
|
23
|
-
</div>
|
|
24
|
-
</button>
|
|
25
|
-
);
|
|
26
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { Checkbox } from "./Checkbox";
|
|
3
|
-
import { useState } from "react";
|
|
4
|
-
|
|
5
|
-
const meta = {
|
|
6
|
-
component: Checkbox,
|
|
7
|
-
} satisfies Meta<typeof Checkbox>;
|
|
8
|
-
|
|
9
|
-
export default meta;
|
|
10
|
-
type Story = StoryObj<typeof Checkbox>;
|
|
11
|
-
|
|
12
|
-
export const Default: Story = {
|
|
13
|
-
render: () => {
|
|
14
|
-
const [checked, setChecked] = useState(false);
|
|
15
|
-
return <Checkbox checked={checked} onChange={setChecked} label="Remember me" />;
|
|
16
|
-
},
|
|
17
|
-
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { IconCheck } from "@tabler/icons-react";
|
|
2
|
-
|
|
3
|
-
interface CheckboxProps {
|
|
4
|
-
label?: string;
|
|
5
|
-
checked: boolean;
|
|
6
|
-
onChange: (checked: boolean) => void;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function Checkbox({ label, checked, onChange }: CheckboxProps) {
|
|
10
|
-
return (
|
|
11
|
-
<div className="flex gap-[10px]">
|
|
12
|
-
<div
|
|
13
|
-
className={`flex justify-center items-center border-[1px] border-black w-[24px] h-[24px] rounded-[8px] cursor-pointer transition-colors duration-100
|
|
14
|
-
${checked ? "bg-black text-white" : "bg-white text-black"}`}
|
|
15
|
-
onClick={() => onChange(!checked)}
|
|
16
|
-
>
|
|
17
|
-
<span
|
|
18
|
-
className={`flex justify-center items-center transition-transform duration-150
|
|
19
|
-
${checked ? "scale-100 opacity-100" : "scale-0 opacity-0"}`}
|
|
20
|
-
>
|
|
21
|
-
<IconCheck size={20} />
|
|
22
|
-
</span>
|
|
23
|
-
</div>
|
|
24
|
-
{label && <p>{label}</p>}
|
|
25
|
-
</div>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
|
|
3
|
-
import { CheckboxGroup } from "./CheckboxGroup";
|
|
4
|
-
import { useState } from "react";
|
|
5
|
-
|
|
6
|
-
const meta = {
|
|
7
|
-
component: CheckboxGroup,
|
|
8
|
-
} satisfies Meta<typeof CheckboxGroup>;
|
|
9
|
-
|
|
10
|
-
export default meta;
|
|
11
|
-
|
|
12
|
-
type Story = StoryObj<typeof CheckboxGroup>;
|
|
13
|
-
|
|
14
|
-
export const Default: Story = {
|
|
15
|
-
render: () => {
|
|
16
|
-
const [options, setOptions] = useState([
|
|
17
|
-
{ checked: false, label: "Apple" },
|
|
18
|
-
{ checked: true, label: "Banana" },
|
|
19
|
-
{ checked: false, label: "Cherry" },
|
|
20
|
-
]);
|
|
21
|
-
|
|
22
|
-
const onChange = (label: string) => {
|
|
23
|
-
setOptions((prev) => prev.map((opt) => (opt.label === label ? { ...opt, checked: !opt.checked } : opt)));
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const selectedItem = options.filter((opt) => opt.checked).map((item) => item.label);
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<div>
|
|
30
|
-
<CheckboxGroup options={options} onChange={onChange} alignment="vertical" />
|
|
31
|
-
<p>Selected items: {selectedItem.join(", ")} </p>
|
|
32
|
-
</div>
|
|
33
|
-
);
|
|
34
|
-
},
|
|
35
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Checkbox } from "../Checkbox/Checkbox";
|
|
3
|
-
|
|
4
|
-
interface CheckboxOption {
|
|
5
|
-
checked: boolean;
|
|
6
|
-
label: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
interface CheckboxGroupProps {
|
|
10
|
-
options: CheckboxOption[];
|
|
11
|
-
onChange: (label: string) => void;
|
|
12
|
-
alignment?: "horizontal" | "vertical";
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function CheckboxGroup({ options, onChange, alignment = "vertical" }: CheckboxGroupProps) {
|
|
16
|
-
return (
|
|
17
|
-
<div className={`flex gap-4 ${alignment === "vertical" ? "flex-col" : ""}`}>
|
|
18
|
-
{options.map((opt) => (
|
|
19
|
-
<Checkbox key={opt.label} checked={opt.checked} label={opt.label} onChange={() => onChange(opt.label)} />
|
|
20
|
-
))}
|
|
21
|
-
</div>
|
|
22
|
-
);
|
|
23
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
|
|
3
|
-
import { Loader } from "./Loader";
|
|
4
|
-
|
|
5
|
-
const meta = {
|
|
6
|
-
component: Loader,
|
|
7
|
-
} satisfies Meta<typeof Loader>;
|
|
8
|
-
|
|
9
|
-
export default meta;
|
|
10
|
-
type Story = StoryObj<typeof Loader>;
|
|
11
|
-
|
|
12
|
-
export const Default: Story = {
|
|
13
|
-
render: () => {
|
|
14
|
-
return <Loader />;
|
|
15
|
-
},
|
|
16
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
interface LoaderProps {
|
|
2
|
-
size?: number;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export function Loader({ size = 25 }: LoaderProps) {
|
|
6
|
-
return (
|
|
7
|
-
<div
|
|
8
|
-
style={{ width: size, height: size }}
|
|
9
|
-
className="border-4 border-black border-t-transparent border-solid rounded-full animate-spin"
|
|
10
|
-
></div>
|
|
11
|
-
);
|
|
12
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { MenuNavBar } from "./MenuNavBar";
|
|
3
|
-
|
|
4
|
-
import { Icon } from "@iconify/react";
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
IconLayoutDashboard,
|
|
8
|
-
IconLayoutDashboardFilled,
|
|
9
|
-
IconFolder,
|
|
10
|
-
IconFolderFilled,
|
|
11
|
-
IconSquareCheck,
|
|
12
|
-
IconSquareCheckFilled,
|
|
13
|
-
IconCalendarMonth,
|
|
14
|
-
IconFileAnalytics,
|
|
15
|
-
IconSettings,
|
|
16
|
-
IconHelp,
|
|
17
|
-
IconCalendarMonthFilled,
|
|
18
|
-
IconFileAnalyticsFilled,
|
|
19
|
-
} from "@tabler/icons-react";
|
|
20
|
-
|
|
21
|
-
const meta = {
|
|
22
|
-
component: MenuNavBar,
|
|
23
|
-
} satisfies Meta<typeof MenuNavBar>;
|
|
24
|
-
|
|
25
|
-
export default meta;
|
|
26
|
-
type Story = StoryObj<typeof MenuNavBar>;
|
|
27
|
-
|
|
28
|
-
export const Default: Story = {
|
|
29
|
-
render: () => {
|
|
30
|
-
const MENU = [
|
|
31
|
-
{
|
|
32
|
-
title: "ภาพรวม",
|
|
33
|
-
subMenus: [
|
|
34
|
-
{
|
|
35
|
-
title: "แดชบอร์ด",
|
|
36
|
-
icon: <IconLayoutDashboard />,
|
|
37
|
-
iconActive: <IconLayoutDashboardFilled />,
|
|
38
|
-
path: "/dashboard",
|
|
39
|
-
},
|
|
40
|
-
{ title: "โครงการ", icon: <IconFolder />, iconActive: <IconFolderFilled />, path: "/project" },
|
|
41
|
-
],
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
title: "การจัดการ",
|
|
45
|
-
subMenus: [
|
|
46
|
-
{
|
|
47
|
-
title: "สิ่งที่ต้องทำ",
|
|
48
|
-
icon: <IconSquareCheck />,
|
|
49
|
-
iconActive: <IconSquareCheckFilled />,
|
|
50
|
-
path: "/todo",
|
|
51
|
-
customNode: (
|
|
52
|
-
<div
|
|
53
|
-
className="flex justify-center items-center w-[30px] h-[30px] rounded-[6px] bg-red-500 text-white
|
|
54
|
-
group-hover:bg-white group-hover:text-primary-500 group-hover:border-[1px] group-hover:border-primary-500
|
|
55
|
-
group-active:bg-red-500 group-active:text-white btn-small"
|
|
56
|
-
>
|
|
57
|
-
5
|
|
58
|
-
</div>
|
|
59
|
-
),
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
title: "กำหนดการ",
|
|
63
|
-
icon: <IconCalendarMonth />,
|
|
64
|
-
iconActive: <IconCalendarMonthFilled />,
|
|
65
|
-
path: "/calendar",
|
|
66
|
-
},
|
|
67
|
-
{ title: "รายงาน", icon: <IconFileAnalytics />, iconActive: <IconFileAnalyticsFilled />, path: "/report" },
|
|
68
|
-
{ title: "ทีม", icon: <Icon icon="ri:team-line" />, iconActive: <Icon icon="ri:team-fill" />, path: "/team" },
|
|
69
|
-
],
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
title: "ตั้งค่า",
|
|
73
|
-
subMenus: [
|
|
74
|
-
{ title: "ตั้งค่า", icon: <IconSettings />, path: "/setting" },
|
|
75
|
-
{ title: "ศูนย์ความช่วยเหลือ", icon: <IconHelp />, path: "/help" },
|
|
76
|
-
],
|
|
77
|
-
},
|
|
78
|
-
];
|
|
79
|
-
return (
|
|
80
|
-
<div className="w-[242px]">
|
|
81
|
-
<MenuNavBar menus={MENU} onClick={(v) => console.log(v)} />
|
|
82
|
-
</div>
|
|
83
|
-
);
|
|
84
|
-
},
|
|
85
|
-
};
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
interface Menu {
|
|
2
|
-
title: string;
|
|
3
|
-
subMenus: {
|
|
4
|
-
title: string;
|
|
5
|
-
icon?: React.ReactNode;
|
|
6
|
-
iconActive?: React.ReactNode;
|
|
7
|
-
path: string;
|
|
8
|
-
customNode?: React.ReactNode;
|
|
9
|
-
}[];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface MenuNavBarProps {
|
|
13
|
-
menus: Menu[];
|
|
14
|
-
onClick: (path: string) => void;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function MenuNavBar({ menus, onClick }: MenuNavBarProps) {
|
|
18
|
-
return (
|
|
19
|
-
<div className="w-full h-full p-[10px]">
|
|
20
|
-
{menus?.map((menu, index) => (
|
|
21
|
-
<div key={`menu_${menu.title}`} className={`p-[10px] ${index !== 0 ? "mt-[10px]" : ""}`}>
|
|
22
|
-
<p className="p-[10px] w-[202px] h-[47px] subtitle-1">{menu.title}</p>
|
|
23
|
-
{menu?.subMenus.map((subMenu) => (
|
|
24
|
-
<div
|
|
25
|
-
key={`sub_${subMenu.title}`}
|
|
26
|
-
className="group flex justify-center items-center gap-[10px] p-[10px] w-[202px] h-[47px] rounded-[6px] subtitle-2 cursor-pointer hover:bg-red-100 active:bg-primary-500 hover:text-white active:text-white"
|
|
27
|
-
onClick={() => onClick(subMenu.path)}
|
|
28
|
-
>
|
|
29
|
-
<span className="flex justify-center items-center w-[24px] h-[24px] text-[20px]">
|
|
30
|
-
{subMenu.icon && (
|
|
31
|
-
<span className={`block ${subMenu.iconActive ? "group-active:hidden" : ""}`}>{subMenu.icon}</span>
|
|
32
|
-
)}
|
|
33
|
-
{subMenu.iconActive && <span className="hidden group-active:block">{subMenu.iconActive}</span>}
|
|
34
|
-
</span>
|
|
35
|
-
{subMenu.title}
|
|
36
|
-
<span className="flex ml-auto">{subMenu.customNode && subMenu.customNode}</span>
|
|
37
|
-
</div>
|
|
38
|
-
))}
|
|
39
|
-
</div>
|
|
40
|
-
))}
|
|
41
|
-
</div>
|
|
42
|
-
);
|
|
43
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
|
|
3
|
-
import { TopNavBar } from "./TopNavBar";
|
|
4
|
-
|
|
5
|
-
const meta = {
|
|
6
|
-
component: TopNavBar,
|
|
7
|
-
} satisfies Meta<typeof TopNavBar>;
|
|
8
|
-
|
|
9
|
-
export default meta;
|
|
10
|
-
type Story = StoryObj<typeof TopNavBar>;
|
|
11
|
-
|
|
12
|
-
export const Default: Story = {
|
|
13
|
-
render: () => {
|
|
14
|
-
return <TopNavBar onClickNoti={() => console.log("noti")} />;
|
|
15
|
-
},
|
|
16
|
-
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import STOLogo from "../../assets/STO-logo.svg";
|
|
2
|
-
import { IconBellRinging } from "@tabler/icons-react";
|
|
3
|
-
|
|
4
|
-
interface TopNavBarProps {
|
|
5
|
-
onClickNoti: () => void;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function TopNavBar({ onClickNoti }: TopNavBarProps) {
|
|
9
|
-
return (
|
|
10
|
-
<div className="w-full h-full flex">
|
|
11
|
-
<div className="flex items-center gap-[20px] p-[10px]">
|
|
12
|
-
<img src={STOLogo} />
|
|
13
|
-
<p className="subtitle-1">Project Management</p>
|
|
14
|
-
</div>
|
|
15
|
-
<div className="flex items-center ml-auto gap-[20px] p-[10px]">
|
|
16
|
-
<div>Search</div>
|
|
17
|
-
<div>
|
|
18
|
-
<IconBellRinging onClick={onClickNoti} className="cursor-pointer" />
|
|
19
|
-
</div>
|
|
20
|
-
<div className="w-[40px] h-[40px] bg-gray-400 rounded-full cursor-pointer"></div>
|
|
21
|
-
</div>
|
|
22
|
-
</div>
|
|
23
|
-
);
|
|
24
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { Radio } from "./Radio";
|
|
3
|
-
import { useState } from "react";
|
|
4
|
-
|
|
5
|
-
const meta = {
|
|
6
|
-
component: Radio,
|
|
7
|
-
} satisfies Meta<typeof Radio>;
|
|
8
|
-
|
|
9
|
-
export default meta;
|
|
10
|
-
type Story = StoryObj<typeof Radio>;
|
|
11
|
-
|
|
12
|
-
export const Default: Story = {
|
|
13
|
-
render: () => {
|
|
14
|
-
const [selected, setSelected] = useState(false);
|
|
15
|
-
return <Radio selected={selected} onChange={setSelected} />;
|
|
16
|
-
},
|
|
17
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
interface RadioProps {
|
|
2
|
-
selected: boolean;
|
|
3
|
-
onChange: (selected: boolean) => void;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export function Radio({ selected, onChange }: RadioProps) {
|
|
7
|
-
return (
|
|
8
|
-
<div
|
|
9
|
-
className="flex justify-center items-center w-[16px] h-[16px] border-[1px] border-black rounded-full cursor-pointer"
|
|
10
|
-
onClick={() => onChange(!selected)}
|
|
11
|
-
>
|
|
12
|
-
{selected && <div className={`bg-black w-[10px] h-[10px] rounded-full transition-all duration-300`} />}
|
|
13
|
-
</div>
|
|
14
|
-
);
|
|
15
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { useState } from "react";
|
|
3
|
-
import { RadioGroup } from "./RadioGroup";
|
|
4
|
-
|
|
5
|
-
const meta = {
|
|
6
|
-
component: RadioGroup,
|
|
7
|
-
} satisfies Meta<typeof RadioGroup>;
|
|
8
|
-
|
|
9
|
-
export default meta;
|
|
10
|
-
type Story = StoryObj<typeof RadioGroup>;
|
|
11
|
-
|
|
12
|
-
export const Default: Story = {
|
|
13
|
-
render: () => {
|
|
14
|
-
const options = [
|
|
15
|
-
{ value: "1", label: "Option 1" },
|
|
16
|
-
{ value: "2", label: "Option 2" },
|
|
17
|
-
{ value: "3", label: "Option 3" },
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
const [selected, setSelected] = useState("1");
|
|
21
|
-
return <RadioGroup onChange={setSelected} options={options} value={selected} alignment="vertical" />;
|
|
22
|
-
},
|
|
23
|
-
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Radio } from "../Radio/Radio";
|
|
2
|
-
|
|
3
|
-
interface RadioOption {
|
|
4
|
-
value: string;
|
|
5
|
-
label: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
interface RadioGroupProps {
|
|
9
|
-
options: RadioOption[];
|
|
10
|
-
value: string;
|
|
11
|
-
onChange: (value: string) => void;
|
|
12
|
-
alignment?: "vertical" | "horizontal";
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
//TODO: ALIGNMENT
|
|
16
|
-
export function RadioGroup({ options, value, onChange, alignment = "horizontal" }: RadioGroupProps) {
|
|
17
|
-
return (
|
|
18
|
-
<div className={`flex gap-2 ${alignment === "vertical" ? "flex-col" : ""}`}>
|
|
19
|
-
{options.map((opt) => (
|
|
20
|
-
<label key={opt.value} className="flex items-center gap-2 cursor-pointer">
|
|
21
|
-
<Radio selected={value === opt.value} onChange={() => onChange(opt.value)} />
|
|
22
|
-
<span>{opt.label}</span>
|
|
23
|
-
</label>
|
|
24
|
-
))}
|
|
25
|
-
</div>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
|
|
3
|
-
import { Switch } from "./Switch";
|
|
4
|
-
import { useState } from "react";
|
|
5
|
-
|
|
6
|
-
const meta = {
|
|
7
|
-
component: Switch,
|
|
8
|
-
} satisfies Meta<typeof Switch>;
|
|
9
|
-
|
|
10
|
-
export default meta;
|
|
11
|
-
type Story = StoryObj<typeof Switch>;
|
|
12
|
-
|
|
13
|
-
export const Default: Story = {
|
|
14
|
-
render: () => {
|
|
15
|
-
const [checked, setChecked] = useState(false);
|
|
16
|
-
return <Switch checked={checked} onChange={setChecked} label="Remember me" />;
|
|
17
|
-
},
|
|
18
|
-
};
|