lucentia-ui 0.2.6 → 0.2.8

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.
@@ -1,2 +1,2 @@
1
1
  import { ButtonProps } from "./types";
2
- export declare const Button: ({ variant, size, className, ...props }: ButtonProps) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const Button: ({ variant, size, fullWidth, className, ...props }: ButtonProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import styles from "./Button.module.css";
3
3
  import clsx from "clsx";
4
- export const Button = ({ variant = "ghost", size = "md", className, ...props }) => {
5
- return (_jsx("button", { className: clsx(styles.button, styles[variant], styles[size], className), ...props }));
4
+ export const Button = ({ variant = "ghost", size = "md", fullWidth = false, className, ...props }) => {
5
+ return (_jsx("button", { className: clsx(styles.button, styles[variant], styles[size], fullWidth && styles.fullWidth, className), ...props }));
6
6
  };
@@ -109,6 +109,12 @@
109
109
  box-shadow: var(--shadow-md);
110
110
  }
111
111
 
112
+
113
+ /* ===== full width ===== */
114
+ .fullWidth {
115
+ width: 100%;
116
+ }
117
+
112
118
  /* ===== State ===== */
113
119
 
114
120
  .sm.button:active:not(:disabled),
@@ -1,4 +1,5 @@
1
1
  export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
2
2
  variant?: "ghost" | "primary" | "secondary" | "danger";
3
3
  size?: "sm" | "md";
4
+ fullWidth?: boolean;
4
5
  }
@@ -19,21 +19,21 @@
19
19
  }
20
20
 
21
21
  .control {
22
- width: 32px;
23
- height: 32px;
22
+ width: 28px;
23
+ height: 28px;
24
24
  border-radius: var(--radius-sm);
25
25
  background: var(--color-surface-container);
26
26
  box-shadow: var(--shadow-md-in);
27
27
  position: relative;
28
28
  background-repeat: no-repeat;
29
29
  background-position: center;
30
- background-size: 32px 32px;
30
+ background-size: 24px 24px;
31
31
  transition: background-color 0.2s ease;
32
32
  }
33
33
 
34
34
  /* checked */
35
35
  .input:checked + .control {
36
- background-color: var(--color-primary);
36
+ background: var(--color-primary);
37
37
  background-image: var(--icon-check);
38
38
  }
39
39
  /* focus */
@@ -0,0 +1 @@
1
+ export declare function Clock(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,27 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from "react";
4
+ import styles from "./Clock.module.css";
5
+ export function Clock() {
6
+ const [time, setTime] = useState(null);
7
+ useEffect(() => {
8
+ const update = () => setTime(new Date());
9
+ update(); // 初回
10
+ const timer = setInterval(update, 1000);
11
+ return () => clearInterval(timer);
12
+ }, []);
13
+ if (!time) {
14
+ return _jsx("div", { className: styles.clock });
15
+ }
16
+ const seconds = time.getSeconds();
17
+ const minutes = time.getMinutes();
18
+ const hours = time.getHours() % 12;
19
+ const secondDeg = seconds * 6;
20
+ const minuteDeg = minutes * 6 + seconds * 0.1;
21
+ const hourDeg = hours * 30 + minutes * 0.5;
22
+ return (_jsxs("div", { className: styles.clock, children: [_jsx("div", { className: styles.dial, children: Array.from({ length: 60 }).map((_, i) => (_jsx("span", { className: [
23
+ styles.tick,
24
+ i % 5 === 0 ? styles.major : styles.minor,
25
+ i % 15 === 0 ? styles.quarter : "",
26
+ ].join(" "), style: { transform: `rotate(${i * 6}deg)` } }, i))) }), _jsx("div", { className: `${styles.hand} ${styles.hour}`, style: { transform: `rotate(${hourDeg}deg)` } }), _jsx("div", { className: `${styles.hand} ${styles.minute}`, style: { transform: `rotate(${minuteDeg}deg)` } }), _jsx("div", { className: `${styles.hand} ${styles.second}`, style: { transform: `rotate(${secondDeg}deg)` } }), _jsx("div", { className: styles.center })] }));
27
+ }
@@ -0,0 +1,88 @@
1
+ .clock {
2
+ position: relative;
3
+ width: 240px;
4
+ height: 240px;
5
+ border-radius: 50%;
6
+ background: var(--color-background);
7
+ border: 2px solid var(--color-surface);
8
+ box-sizing: border-box;
9
+ box-shadow: var(--shadow-md),var(--shadow-md-in);
10
+ }
11
+
12
+ /* 共通の針 */
13
+ .hand {
14
+ position: absolute;
15
+ left: 50%;
16
+ bottom: 50%;
17
+ transform-origin: bottom center;
18
+ transform: translateX(-50%);
19
+ box-shadow: 0px 0px 4px var(--color-shadow-d);
20
+ border-radius: 2px;
21
+ }
22
+
23
+ /* 時針 */
24
+ .hour {
25
+ width: 5px;
26
+ height: 64px;
27
+ background: var(--color-secondary);
28
+ }
29
+
30
+ /* 分針 */
31
+ .minute {
32
+ width: 5px;
33
+ height: 104px;
34
+ background: var(--color-secondary);
35
+ }
36
+
37
+ /* 秒針 */
38
+ .second {
39
+ width: 2px;
40
+ height: 100px;
41
+ background: var(--color-error);
42
+ }
43
+
44
+ /* 中心の丸 */
45
+ .center {
46
+ position: absolute;
47
+ top: 50%;
48
+ left: 50%;
49
+ width: 12px;
50
+ height: 12px;
51
+ background: var(--color-background);
52
+ box-shadow: var(--shadow-sm);
53
+ border-radius: 50%;
54
+ transform: translate(-50%, -50%);
55
+ z-index: 10;
56
+ }
57
+
58
+
59
+
60
+ /* ===== 文字盤 ===== */
61
+
62
+ .dial {
63
+ position: absolute;
64
+ inset: 0;
65
+ border-radius: 50%;
66
+ }
67
+
68
+ /* 共通目盛り */
69
+ .tick {
70
+ position: absolute;
71
+ left: 50%;
72
+ top: 8px;
73
+ transform-origin: center 112px;
74
+ background: var(--color-on-surface-variant);
75
+ }
76
+
77
+ /* 5分ごとの目盛 */
78
+ .major {
79
+ width: 2.5px;
80
+ height: 14px;
81
+ }
82
+
83
+ /* 12・3・6・9 */
84
+ .quarter {
85
+ height: 21px;
86
+ width: 3px;
87
+ background: var(--color-on-surface-variant);
88
+ }
@@ -0,0 +1,6 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Clock } from "./Clock";
3
+ declare const meta: Meta<typeof Clock>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof Clock>;
6
+ export declare const Default: Story;
@@ -0,0 +1,7 @@
1
+ import { Clock } from "./Clock";
2
+ const meta = {
3
+ title: "Components/Extra/Clock",
4
+ component: Clock,
5
+ };
6
+ export default meta;
7
+ export const Default = {};
@@ -0,0 +1 @@
1
+ export * from "./Clock";
@@ -0,0 +1 @@
1
+ export * from "./Clock";
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import type { ThemeSwitchProps } from "./types";
3
+ export declare const ThemeSwitch: React.FC<ThemeSwitchProps>;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import styles from "./ThemeSwitch.module.css";
3
+ import clsx from "clsx";
4
+ export const ThemeSwitch = ({ checked, defaultChecked, onChange, onCheckedChange, disabled = false, id, name, className, }) => {
5
+ const handleChange = (e) => {
6
+ if (disabled)
7
+ return;
8
+ onChange === null || onChange === void 0 ? void 0 : onChange(e);
9
+ onCheckedChange === null || onCheckedChange === void 0 ? void 0 : onCheckedChange(e.target.checked);
10
+ };
11
+ return (_jsxs("label", { className: clsx(styles.themeSwitch, className), style: {
12
+ opacity: disabled ? 0.6 : 1,
13
+ cursor: disabled ? "not-allowed" : "pointer",
14
+ }, children: [_jsx("input", { type: "checkbox", id: id, name: name,
15
+ // CSS Modules側のクラス名と一致させる(styles.input もしくは styles.themeSwitchCheckbox)
16
+ className: styles.themeSwitchCheckbox, checked: checked, defaultChecked: defaultChecked, disabled: disabled, onChange: handleChange }), _jsx("span", { className: styles.slider, children: _jsxs("span", { className: styles.circle, children: [[...Array(8)].map((_, i) => (_jsx("span", { className: clsx(styles.shine, styles[`shine-${i + 1}`]) }, i))), _jsx("span", { className: styles.moon })] }) })] }));
17
+ };
@@ -0,0 +1,98 @@
1
+ .themeSwitch {
2
+ font-size: 18px;
3
+ position: relative;
4
+ display: inline-block;
5
+ width: 3.5em;
6
+ height: 2em;
7
+ }
8
+
9
+ /* input単体にスタイルを当てる */
10
+ .input {
11
+ opacity: 0;
12
+ width: 0;
13
+ height: 0;
14
+ }
15
+
16
+ .slider {
17
+ position: absolute;
18
+ cursor: pointer;
19
+ top: 0;
20
+ left: 0;
21
+ right: 0;
22
+ bottom: 0;
23
+ background-color: #333;
24
+ transition: 0.4s;
25
+ border-radius: 30px;
26
+ overflow: hidden;
27
+ }
28
+
29
+ .circle {
30
+ position: absolute;
31
+ height: 1.4em;
32
+ width: 1.4em;
33
+ border-radius: 20px;
34
+ left: 0.3em;
35
+ bottom: 0.3em;
36
+ background-color: #fff000;
37
+ transition: 0.4s;
38
+ }
39
+
40
+ /* ThemeSwitch.module.css */
41
+
42
+ /* inputのクラス名をTSXと合わせる */
43
+ .themeSwitchCheckbox {
44
+ opacity: 0;
45
+ width: 0;
46
+ height: 0;
47
+ position: absolute;
48
+ }
49
+
50
+ /* チェック時の隣接セレクタを調整 */
51
+ .themeSwitchCheckbox:checked + .slider .circle {
52
+ transform: rotate(0deg) translateX(1.5em) !important;
53
+ }
54
+
55
+ .themeSwitchCheckbox:checked + .slider .circle .shine {
56
+ transform: translate(0%, 0%) !important;
57
+ }
58
+
59
+ .themeSwitchCheckbox:checked + .slider .circle .moon {
60
+ left: -10%;
61
+ opacity: 1;
62
+ transform: translateY(-60%);
63
+ }
64
+
65
+ .moon {
66
+ position: absolute;
67
+ left: -100%;
68
+ top: 50%;
69
+ opacity: 0;
70
+ background-color: #333;
71
+ width: 1.25rem;
72
+ height: 1.25rem;
73
+ border-radius: 99999px;
74
+ transform: translateY(-50%);
75
+ transition: all 0.4s;
76
+ }
77
+
78
+ .shine {
79
+ display: block;
80
+ position: absolute;
81
+ top: 50%;
82
+ left: 50%;
83
+ width: 0.25rem;
84
+ height: 0.25rem;
85
+ background-color: #fff000;
86
+ border-radius: 1rem;
87
+ transition: all 0.4s;
88
+ }
89
+
90
+ /* 個別のshine配置 */
91
+ .shine-1 { transform: translate(-50%, -375%); }
92
+ .shine-2 { transform: translate(175%, -275%); }
93
+ .shine-3 { transform: translate(275%, -50%); }
94
+ .shine-4 { transform: translate(175%, 175%); }
95
+ .shine-5 { transform: translate(-50%, 275%); }
96
+ .shine-6 { transform: translate(-275%, 175%); }
97
+ .shine-7 { transform: translate(-375%, -50%); }
98
+ .shine-8 { transform: translate(-275%, -275%); }
@@ -0,0 +1,7 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { ThemeSwitch } from "./ThemeSwitch";
3
+ declare const meta: Meta<typeof ThemeSwitch>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof ThemeSwitch>;
6
+ export declare const Default: Story;
7
+ export declare const State: Story;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ThemeSwitch } from "./ThemeSwitch";
3
+ const meta = {
4
+ title: "Components/Extra/ThemeSwitch",
5
+ component: ThemeSwitch,
6
+ };
7
+ export default meta;
8
+ export const Default = {};
9
+ export const State = {
10
+ render: () => {
11
+ return (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [_jsx(ThemeSwitch, { defaultChecked: false }), _jsx(ThemeSwitch, { defaultChecked: true })] }));
12
+ },
13
+ };
@@ -0,0 +1,13 @@
1
+ import type { ChangeEvent } from "react";
2
+ export type ThemeSwitchProps = {
3
+ /** controlled */
4
+ checked?: boolean;
5
+ /** uncontrolled */
6
+ defaultChecked?: boolean;
7
+ onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
8
+ onCheckedChange?: (checked: boolean) => void;
9
+ disabled?: boolean;
10
+ id?: string;
11
+ name?: string;
12
+ className?: string;
13
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -15,8 +15,8 @@
15
15
  }
16
16
 
17
17
  .control {
18
- width: 32px;
19
- height: 32px;
18
+ width: 28px;
19
+ height: 28px;
20
20
  border-radius: 50%;
21
21
  background: var(--color-surface);
22
22
  box-shadow: var(--shadow-md-in);
@@ -1,5 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import styles from "./Switch.module.css";
3
- export const Switch = ({ checked, defaultChecked, onChange, disabled = false, id, name, }) => {
4
- return (_jsxs("label", { className: styles.switch, children: [_jsx("input", { type: "checkbox", id: id, name: name, checked: checked, defaultChecked: defaultChecked, onChange: onChange, disabled: disabled }), _jsx("span", { className: styles.slider })] }));
3
+ export const Switch = ({ checked, defaultChecked, onChange, onCheckedChange, disabled = false, id, name, }) => {
4
+ const handleChange = (e) => {
5
+ onChange === null || onChange === void 0 ? void 0 : onChange(e);
6
+ onCheckedChange === null || onCheckedChange === void 0 ? void 0 : onCheckedChange(e.target.checked);
7
+ };
8
+ return (_jsxs("label", { className: styles.switch, children: [_jsx("input", { type: "checkbox", role: "switch", id: id, name: name, checked: checked, defaultChecked: defaultChecked, onChange: handleChange, disabled: disabled }), _jsx("span", { className: styles.slider })] }));
5
9
  };
@@ -1,8 +1,8 @@
1
1
  .switch {
2
2
  position: relative;
3
3
  display: inline-block;
4
- width: 72px;
5
- height: 40px;
4
+ width: 64px;
5
+ height: 36px;
6
6
  }
7
7
 
8
8
  /* input は見えなくするが状態は保持 */
@@ -27,8 +27,8 @@
27
27
  .slider::before {
28
28
  content: "";
29
29
  position: absolute;
30
- height: 24px;
31
- width: 24px;
30
+ height: 20px;
31
+ width: 20px;
32
32
  left: 8px;
33
33
  top: 8px;
34
34
  background: var(--color-on-primary);
@@ -43,7 +43,7 @@
43
43
  }
44
44
 
45
45
  .switch input:checked + .slider::before {
46
- transform: translateX(32px);
46
+ transform: translateX(28px);
47
47
  }
48
48
 
49
49
  /* ===== Focus ===== */
@@ -5,6 +5,7 @@ export type SwitchProps = {
5
5
  /** uncontrolled */
6
6
  defaultChecked?: boolean;
7
7
  onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
8
+ onCheckedChange?: (checked: boolean) => void;
8
9
  disabled?: boolean;
9
10
  id?: string;
10
11
  name?: string;
@@ -45,7 +45,9 @@
45
45
  --space-3xl: 48px;
46
46
  --space-4xl: 64px;
47
47
  --space-5xl: 96px;
48
- --gap: 32px;
48
+ --gap-sm: 16px;
49
+ --gap-md: 32px;
50
+ --gap-lg: 64px;
49
51
 
50
52
  /* ===== Layout ===== */
51
53
  --container: 1120px;
@@ -128,7 +130,7 @@
128
130
  --color-background: #2b3a38;
129
131
  --color-on-background: #dde4e3;
130
132
 
131
- --color-surface: #1a2120bf;
133
+ --color-surface: #2b3a38bf;
132
134
  --color-on-surface: #dde4e3;
133
135
  --color-on-surface-variant: #dde4e380;
134
136
  --color-surface-container: #262d2d;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lucentia-ui",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "React UI design token and component system based on neumorphism, featuring two color themes: light and dark.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",