lucentia-ui 0.2.23 → 1.0.0
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/LICENSE +1 -1
- package/README.ja.md +121 -0
- package/README.md +120 -3
- package/dist/components/Feedback/Modal/Modal.module.css +1 -1
- package/dist/components/Input/Input.module.css +2 -3
- package/dist/components/Menu/Menu.module.css +0 -1
- package/dist/components/Select/Select.d.ts +1 -1
- package/dist/components/Select/Select.js +114 -4
- package/dist/components/Select/Select.module.css +84 -20
- package/dist/components/Select/Select.stories.d.ts +1 -0
- package/dist/components/Select/Select.stories.js +34 -5
- package/dist/components/Select/types.d.ts +11 -1
- package/dist/components/Textarea/Textarea.module.css +2 -2
- package/dist/components/Typography/Heading.d.ts +1 -1
- package/dist/components/Typography/types.d.ts +1 -1
- package/dist/components/Typography/typography.module.css +18 -8
- package/dist/styles/tokens.css +122 -129
- package/package.json +2 -1
package/LICENSE
CHANGED
package/README.ja.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
|
|
2
|
+
# lucentia-ui
|
|
3
|
+
|
|
4
|
+
ニューモーフィズムを基調としたReact UI デザイントークン&コンポーネント。ライト/ダークの2カラーテーマを保持。
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## インストール方法
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install lucentia-ui
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
yarnの場合
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
yarn add lucentia-ui
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 対応環境
|
|
24
|
+
|
|
25
|
+
- React 18+
|
|
26
|
+
- CSS Modules 対応環境(Next.js / Vite / CRA など)
|
|
27
|
+
|
|
28
|
+
## 基本の使い方
|
|
29
|
+
|
|
30
|
+
### 1.グローバルスタイルを import(必須)
|
|
31
|
+
|
|
32
|
+
※lucentia-uiを使用する場合、グローバルスタイルの import が必須です。
|
|
33
|
+
※ Next.js の場合は `_app.tsx` や `layout.tsx` で import してください
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import "lucentia-ui/styles";
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
lucentia-ui のコンポーネントは
|
|
42
|
+
CSS Variables(Design Tokens)を前提に設計されています。
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
```css
|
|
46
|
+
:root {
|
|
47
|
+
--color-background: #ffffff;
|
|
48
|
+
--color-text: #111111;
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
import "lucentia-ui/styles" によって:
|
|
53
|
+
|
|
54
|
+
- Token 定義
|
|
55
|
+
- ベーススタイル
|
|
56
|
+
- 各コンポーネントの見た目
|
|
57
|
+
が有効化されます。
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
### 2.コンポーネントを使用
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
import { Button, Checkbox, Radio } from "lucentia-ui";
|
|
64
|
+
|
|
65
|
+
export default function Example() {
|
|
66
|
+
return (
|
|
67
|
+
<>
|
|
68
|
+
<Button variant="primary">Save</Button>
|
|
69
|
+
<Checkbox label="Accept terms" />
|
|
70
|
+
<Radio label="Option A" />
|
|
71
|
+
</>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- 追加の CSS import は不要
|
|
77
|
+
- デフォルトでは light テーマ が適用されます
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
### ダークテーマ
|
|
83
|
+
|
|
84
|
+
lucentia-ui では、
|
|
85
|
+
ダークテーマやテーマ切り替えを行う場合のみ ThemeProvider を使用します。
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
#### テーマ設定例
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
import { ThemeProvider } from "lucentia-ui";
|
|
92
|
+
|
|
93
|
+
export default function App({ children }: { children: React.ReactNode }) {
|
|
94
|
+
return (
|
|
95
|
+
<ThemeProvider defaultTheme="dark">
|
|
96
|
+
{children}
|
|
97
|
+
</ThemeProvider>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## コンポーネント設計方針
|
|
103
|
+
|
|
104
|
+
- **HTML ネイティブ要素を尊重**
|
|
105
|
+
- **props extends HTMLAttributes**
|
|
106
|
+
- **状態・色・サイズは Design Tokens に依存**
|
|
107
|
+
- **Tailwind / inline style 不使用**
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## リリースバージョン
|
|
112
|
+
|
|
113
|
+
lucentia-ui はセマンティック バージョニングに従っています。
|
|
114
|
+
破壊的変更はメジャーバージョンとしてリリースされます。
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
MIT
|
package/README.md
CHANGED
|
@@ -1,9 +1,126 @@
|
|
|
1
|
+
|
|
1
2
|
# lucentia-ui
|
|
2
3
|
|
|
3
|
-
React UI design token and component
|
|
4
|
+
A React UI design token and component library based on neumorphism, featuring both light and dark color themes.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
日本語版はこちら → README.ja.md
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
Using npm:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install lucentia-ui
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Using yarn:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
yarn add lucentia-ui
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Requirements
|
|
29
|
+
|
|
30
|
+
- React 18+
|
|
31
|
+
- Environment that supports CSS Modules(Next.js / Vite / CRA, etc.)
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
### Import global styles (required)
|
|
36
|
+
|
|
37
|
+
Important: Importing global styles is required when using lucentia-ui.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import "lucentia-ui/styles";
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
lucentia-ui components are designed around
|
|
45
|
+
CSS Variables (Design Tokens).
|
|
46
|
+
|
|
47
|
+
```css
|
|
48
|
+
:root {
|
|
49
|
+
--color-background: #ffffff;
|
|
50
|
+
--color-text: #111111;
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
By importing import "lucentia-ui/styles";
|
|
55
|
+
|
|
56
|
+
- the following are enabled:
|
|
57
|
+
- Design token definitions
|
|
58
|
+
- Base styles
|
|
59
|
+
|
|
60
|
+
Default component appearance
|
|
61
|
+
|
|
62
|
+
> For Next.js, import this in app/layout.tsx or pages/_app.tsx.
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
### 2.Use components
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
import { Button, Checkbox, Radio } from "lucentia-ui";
|
|
69
|
+
|
|
70
|
+
export default function Example() {
|
|
71
|
+
return (
|
|
72
|
+
<>
|
|
73
|
+
<Button variant="primary">Save</Button>
|
|
74
|
+
<Checkbox label="Accept terms" />
|
|
75
|
+
<Radio label="Option A" />
|
|
76
|
+
</>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- No additional CSS imports are required
|
|
82
|
+
- The light theme is applied by default
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
### ダークテーマ
|
|
89
|
+
|
|
90
|
+
Use ThemeProvider only when enabling dark mode or theme switching.
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
#### Theme example
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
import { ThemeProvider } from "lucentia-ui";
|
|
97
|
+
|
|
98
|
+
export default function App({ children }: { children: React.ReactNode }) {
|
|
99
|
+
return (
|
|
100
|
+
<ThemeProvider defaultTheme="dark">
|
|
101
|
+
{children}
|
|
102
|
+
</ThemeProvider>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## コンポーネント設計方針
|
|
108
|
+
|
|
109
|
+
- **Respect native HTML elements**
|
|
110
|
+
- **props extends HTMLAttributes**
|
|
111
|
+
- **Props extend standard HTML attributes**
|
|
112
|
+
- **No Tailwind CSS or inline styles**
|
|
4
113
|
|
|
5
114
|
---
|
|
6
115
|
|
|
7
|
-
|
|
116
|
+
## Versioning
|
|
117
|
+
|
|
118
|
+
lucentia-ui follows Semantic Versioning.
|
|
119
|
+
Breaking changes are released as major versions.
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
## License
|
|
8
125
|
|
|
9
|
-
|
|
126
|
+
MIT
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
.input {
|
|
2
2
|
font-family: var(--font);
|
|
3
|
-
padding: var(--space-md) var(--space-lg);
|
|
4
3
|
border-radius: var(--radius-sm);
|
|
5
4
|
border: 2px solid var(--color-background);
|
|
6
5
|
outline: none;
|
|
7
6
|
background: var(--color-surface-container);
|
|
8
7
|
color: var(--color-on-surface);
|
|
9
|
-
|
|
8
|
+
box-sizing: border-box;
|
|
10
9
|
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
|
11
10
|
}
|
|
12
11
|
|
|
@@ -23,7 +22,7 @@
|
|
|
23
22
|
cursor: text;
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
.input:read-only:not(:disabled)
|
|
25
|
+
.input:read-only:not(:disabled):focus {
|
|
27
26
|
border: 2px solid var(--color-surface);
|
|
28
27
|
}
|
|
29
28
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { SelectProps } from "./types";
|
|
2
|
-
export declare const Select: ({ state,
|
|
2
|
+
export declare const Select: ({ options, value, placeholder, state, disabled, onChange, className, }: SelectProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,6 +1,116 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useRef, useEffect } from "react";
|
|
3
4
|
import clsx from "clsx";
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
import styles from "./Select.module.css";
|
|
6
|
+
export const Select = ({ options, value, placeholder = "Select...", state = "default", disabled, onChange, className, }) => {
|
|
7
|
+
var _a;
|
|
8
|
+
const [open, setOpen] = useState(false);
|
|
9
|
+
const [activeIndex, setActiveIndex] = useState(-1);
|
|
10
|
+
const wrapperRef = useRef(null);
|
|
11
|
+
const triggerRef = useRef(null);
|
|
12
|
+
const selectedIndex = options.findIndex((o) => o.value === value);
|
|
13
|
+
const selected = selectedIndex >= 0 ? options[selectedIndex] : undefined;
|
|
14
|
+
const optionRefs = useRef([]);
|
|
15
|
+
const resetActiveIndex = () => {
|
|
16
|
+
setActiveIndex(selectedIndex >= 0 ? selectedIndex : -1);
|
|
17
|
+
};
|
|
18
|
+
/* --------------------
|
|
19
|
+
* 外クリックで閉じる
|
|
20
|
+
* -------------------*/
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const onClick = (e) => {
|
|
23
|
+
var _a;
|
|
24
|
+
if (!((_a = wrapperRef.current) === null || _a === void 0 ? void 0 : _a.contains(e.target))) {
|
|
25
|
+
setOpen(false);
|
|
26
|
+
resetActiveIndex();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
document.addEventListener("mousedown", onClick);
|
|
30
|
+
return () => document.removeEventListener("mousedown", onClick);
|
|
31
|
+
}, [selectedIndex]);
|
|
32
|
+
/* --------------------
|
|
33
|
+
* キーボード操作
|
|
34
|
+
* -------------------*/
|
|
35
|
+
const onKeyDown = (e) => {
|
|
36
|
+
var _a, _b;
|
|
37
|
+
if (disabled)
|
|
38
|
+
return;
|
|
39
|
+
switch (e.key) {
|
|
40
|
+
case "ArrowDown": {
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
setOpen(true);
|
|
43
|
+
setActiveIndex((i) => {
|
|
44
|
+
if (i === -1) {
|
|
45
|
+
return selectedIndex >= 0 ? selectedIndex : 0;
|
|
46
|
+
}
|
|
47
|
+
return i < options.length - 1 ? i + 1 : 0;
|
|
48
|
+
});
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
case "ArrowUp": {
|
|
52
|
+
e.preventDefault();
|
|
53
|
+
setOpen(true);
|
|
54
|
+
setActiveIndex((i) => {
|
|
55
|
+
if (i === -1) {
|
|
56
|
+
return selectedIndex >= 0 ? selectedIndex : options.length - 1;
|
|
57
|
+
}
|
|
58
|
+
return i > 0 ? i - 1 : options.length - 1;
|
|
59
|
+
});
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
case "Enter": {
|
|
63
|
+
e.preventDefault();
|
|
64
|
+
if (!open) {
|
|
65
|
+
setOpen(true);
|
|
66
|
+
resetActiveIndex();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (activeIndex >= 0) {
|
|
70
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(options[activeIndex].value);
|
|
71
|
+
setOpen(false);
|
|
72
|
+
resetActiveIndex();
|
|
73
|
+
(_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case "Escape": {
|
|
78
|
+
setOpen(false);
|
|
79
|
+
resetActiveIndex();
|
|
80
|
+
(_b = triggerRef.current) === null || _b === void 0 ? void 0 : _b.focus();
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case "Tab": {
|
|
84
|
+
setOpen(false);
|
|
85
|
+
resetActiveIndex();
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!open)
|
|
92
|
+
return;
|
|
93
|
+
if (activeIndex < 0)
|
|
94
|
+
return;
|
|
95
|
+
const el = optionRefs.current[activeIndex];
|
|
96
|
+
el === null || el === void 0 ? void 0 : el.scrollIntoView({
|
|
97
|
+
block: "center",
|
|
98
|
+
});
|
|
99
|
+
}, [activeIndex, open]);
|
|
100
|
+
return (_jsxs("div", { ref: wrapperRef, className: clsx(styles.wrapper, styles[state], disabled && styles.disabled, className), children: [_jsxs("button", { ref: triggerRef, type: "button", className: styles.trigger, disabled: disabled, onKeyDown: onKeyDown, onClick: () => {
|
|
101
|
+
setOpen((v) => {
|
|
102
|
+
const next = !v;
|
|
103
|
+
if (next)
|
|
104
|
+
resetActiveIndex();
|
|
105
|
+
return next;
|
|
106
|
+
});
|
|
107
|
+
}, "aria-haspopup": "listbox", "aria-expanded": open, children: [_jsx("span", { className: clsx(!selected && styles.placeholder), children: (_a = selected === null || selected === void 0 ? void 0 : selected.label) !== null && _a !== void 0 ? _a : placeholder }), _jsx("span", { className: clsx(styles.arrow, open && styles.open), children: _jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M16.9697 8.96973C17.2626 8.67684 17.7374 8.67684 18.0303 8.96973C18.3232 9.26262 18.3232 9.73738 18.0303 10.0303L12.5303 15.5303C12.2374 15.8232 11.7626 15.8232 11.4697 15.5303L5.96973 10.0303C5.67684 9.73738 5.67684 9.26262 5.96973 8.96973C6.26262 8.67684 6.73738 8.67684 7.03028 8.96973L12 13.9395L16.9697 8.96973Z", fill: "currentColor" }) }) })] }), open && (_jsx("ul", { className: styles.dropdown, role: "listbox", children: options.map((option, index) => (_jsx("li", { ref: (el) => {
|
|
108
|
+
optionRefs.current[index] = el;
|
|
109
|
+
}, role: "option", "aria-selected": option.value === value, className: clsx(styles.option, index === activeIndex && styles.active, option.value === value && styles.selected), onMouseDown: (e) => e.preventDefault(), onClick: () => {
|
|
110
|
+
var _a;
|
|
111
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(option.value);
|
|
112
|
+
setOpen(false);
|
|
113
|
+
resetActiveIndex();
|
|
114
|
+
(_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
115
|
+
}, children: option.label }, option.value))) }))] }));
|
|
6
116
|
};
|
|
@@ -1,43 +1,107 @@
|
|
|
1
|
-
.
|
|
2
|
-
|
|
1
|
+
.wrapper {
|
|
2
|
+
position: relative;
|
|
3
|
+
display: inline-block;
|
|
4
|
+
width: auto;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.trigger {
|
|
8
|
+
/* all: unset; は避け、必要なものだけリセットする */
|
|
9
|
+
width: 100%;
|
|
10
|
+
appearance: none;
|
|
11
|
+
background: none;
|
|
12
|
+
color: inherit;
|
|
13
|
+
border: none;
|
|
14
|
+
padding: 0;
|
|
15
|
+
font: inherit;
|
|
16
|
+
cursor: pointer;
|
|
17
|
+
outline: none;
|
|
18
|
+
/* 以降、既存のスタイルを調整 */
|
|
19
|
+
display: flex; /* 中の要素を整列させやすくするため */
|
|
20
|
+
align-items: center;
|
|
21
|
+
justify-content: space-between;
|
|
22
|
+
gap: var(--gap-sm);
|
|
23
|
+
box-sizing: border-box;
|
|
3
24
|
padding: var(--space-sm) var(--space-md);
|
|
4
25
|
border: 2px solid transparent;
|
|
5
26
|
border-radius: var(--radius-sm);
|
|
6
27
|
box-shadow: var(--shadow-md);
|
|
7
|
-
|
|
8
28
|
background: var(--color-surface-container);
|
|
9
29
|
color: var(--color-on-surface);
|
|
30
|
+
}
|
|
10
31
|
|
|
11
|
-
|
|
12
|
-
|
|
32
|
+
.trigger:not(:disabled):hover {
|
|
33
|
+
border: 2px solid var(--color-border);
|
|
34
|
+
box-shadow:
|
|
35
|
+
0 0 4px var(--color-border),
|
|
36
|
+
0 0 8px var(--color-border);
|
|
37
|
+
}
|
|
13
38
|
|
|
14
|
-
|
|
39
|
+
.trigger:not(:disabled):active{
|
|
40
|
+
box-shadow: none;
|
|
41
|
+
}
|
|
15
42
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
43
|
+
.placeholder {
|
|
44
|
+
color: var(--color-on-surface);
|
|
45
|
+
}
|
|
19
46
|
|
|
20
|
-
|
|
47
|
+
.arrow {
|
|
48
|
+
transition: transform 0.2s ease;
|
|
49
|
+
color: var(--color-on-surface);
|
|
50
|
+
}
|
|
21
51
|
|
|
52
|
+
.arrow.open {
|
|
53
|
+
transform: rotate(180deg);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.dropdown {
|
|
57
|
+
position: absolute;
|
|
58
|
+
z-index: 10;
|
|
59
|
+
width: 100%;
|
|
60
|
+
max-height: 300px;
|
|
61
|
+
margin-top: var(--space-sm);
|
|
62
|
+
border-radius: var(--radius-sm);
|
|
63
|
+
background: var(--color-surface);
|
|
64
|
+
box-shadow: var(--shadow-lg);
|
|
65
|
+
backdrop-filter: var(--blur);
|
|
66
|
+
overflow: hidden;
|
|
67
|
+
overflow-y: auto;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.option {
|
|
71
|
+
padding: 8px 12px;
|
|
22
72
|
cursor: pointer;
|
|
73
|
+
list-style: none;
|
|
23
74
|
}
|
|
24
75
|
|
|
25
|
-
|
|
26
|
-
|
|
76
|
+
.option:hover {
|
|
77
|
+
background: var(--color-surface-container);
|
|
27
78
|
}
|
|
28
79
|
|
|
29
|
-
/*
|
|
30
|
-
.
|
|
31
|
-
|
|
80
|
+
/* ★重要:キーボードで選択中の項目のスタイル */
|
|
81
|
+
.option.active {
|
|
82
|
+
background: var(--color-primary-container);
|
|
83
|
+
color: var(--color-on-primary-container);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.selected {
|
|
87
|
+
background: var(--color-primary-container);
|
|
88
|
+
color: var(--color-on-primary-container);
|
|
32
89
|
}
|
|
33
90
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
91
|
+
.dropdown:has(.option.active) .option.selected:not(.active) {
|
|
92
|
+
background: transparent;
|
|
93
|
+
color: var(--color-on-surface); /* 通常の文字色に戻す */
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* states */
|
|
97
|
+
|
|
98
|
+
.error .trigger {
|
|
99
|
+
border-color: var(--color-error);
|
|
37
100
|
}
|
|
38
101
|
|
|
39
|
-
|
|
40
|
-
.
|
|
102
|
+
.disabled .trigger:hover,
|
|
103
|
+
.trigger:disabled:hover,
|
|
104
|
+
.disabled, .disabled > .trigger {
|
|
41
105
|
opacity: 0.5;
|
|
42
106
|
cursor: not-allowed;
|
|
43
107
|
}
|
|
@@ -1,22 +1,51 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
2
3
|
import { Select } from "./Select";
|
|
3
4
|
const meta = {
|
|
4
5
|
title: "Components/Select",
|
|
5
6
|
component: Select,
|
|
6
7
|
};
|
|
7
8
|
export default meta;
|
|
9
|
+
const OPTIONS = [
|
|
10
|
+
{ label: "Option A", value: "a" },
|
|
11
|
+
{ label: "Option B", value: "b" },
|
|
12
|
+
{ label: "Option C", value: "c" },
|
|
13
|
+
];
|
|
14
|
+
/* --------------------------------
|
|
15
|
+
* Default
|
|
16
|
+
* -------------------------------- */
|
|
8
17
|
export const Default = {
|
|
9
|
-
|
|
10
|
-
|
|
18
|
+
render: () => {
|
|
19
|
+
const [value, setValue] = useState();
|
|
20
|
+
return (_jsx("div", { style: {
|
|
21
|
+
display: "flex",
|
|
22
|
+
flexDirection: "column",
|
|
23
|
+
gap: 32,
|
|
24
|
+
width: 240,
|
|
25
|
+
}, children: _jsx(Select, { options: OPTIONS, value: value, placeholder: "\u672A\u9078\u629E", onChange: setValue }) }));
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
/* --------------------------------
|
|
29
|
+
* With Selected Value
|
|
30
|
+
* -------------------------------- */
|
|
31
|
+
export const Selected = {
|
|
32
|
+
render: () => {
|
|
33
|
+
const [value, setValue] = useState("b");
|
|
34
|
+
return _jsx(Select, { options: OPTIONS, value: value, onChange: setValue });
|
|
11
35
|
},
|
|
12
36
|
};
|
|
37
|
+
/* --------------------------------
|
|
38
|
+
* States
|
|
39
|
+
* -------------------------------- */
|
|
13
40
|
export const State = {
|
|
14
41
|
render: () => {
|
|
42
|
+
const [value1, setValue1] = useState();
|
|
43
|
+
const [value2, setValue2] = useState();
|
|
15
44
|
return (_jsxs("div", { style: {
|
|
16
45
|
display: "flex",
|
|
17
46
|
flexDirection: "column",
|
|
18
|
-
alignItems: "flex-start",
|
|
19
47
|
gap: 32,
|
|
20
|
-
|
|
48
|
+
width: 240,
|
|
49
|
+
}, children: [_jsx(Select, { options: OPTIONS, value: value1, placeholder: "Default State", onChange: setValue1 }), _jsx(Select, { options: OPTIONS, value: value2, placeholder: "Error State", state: "error", onChange: setValue2 }), _jsx(Select, { options: OPTIONS, placeholder: "Disabled State", disabled: true })] }));
|
|
21
50
|
},
|
|
22
51
|
};
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
export type SelectState = "default" | "error";
|
|
2
|
-
export
|
|
2
|
+
export type SelectOption = {
|
|
3
|
+
label: string;
|
|
4
|
+
value: string;
|
|
5
|
+
};
|
|
6
|
+
export interface SelectProps {
|
|
7
|
+
options: SelectOption[];
|
|
8
|
+
value?: string;
|
|
9
|
+
placeholder?: string;
|
|
3
10
|
state?: SelectState;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
onChange?: (value: string) => void;
|
|
13
|
+
className?: string;
|
|
4
14
|
}
|
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/* ===== Readonly ===== */
|
|
28
|
-
.textarea:read-only
|
|
28
|
+
.textarea:read-only:not(:disabled) {
|
|
29
29
|
box-shadow: none;
|
|
30
30
|
cursor: text;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
.textarea:read-only:focus {
|
|
33
|
+
.textarea:read-only:not(:disabled):focus {
|
|
34
34
|
border: 2px solid var(--color-surface);
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { TypographyVariant, TypographyColor } from "./types";
|
|
2
2
|
import type React from "react";
|
|
3
3
|
type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
4
|
-
type HeadingVariant = Extract<TypographyVariant, "display-lg" | "display-md" | "heading-xl" | "heading-lg" | "heading-md" | "heading-sm" | "heading-xs">;
|
|
4
|
+
type HeadingVariant = Extract<TypographyVariant, "display-lg" | "display-md" | "display-sm" | "heading-xl" | "heading-lg" | "heading-md" | "heading-sm" | "heading-xs">;
|
|
5
5
|
type HeadingProps = {
|
|
6
6
|
level: HeadingLevel;
|
|
7
7
|
variant?: HeadingVariant;
|
|
@@ -3,5 +3,5 @@ export type AsProp<E extends React.ElementType> = {
|
|
|
3
3
|
as?: E;
|
|
4
4
|
};
|
|
5
5
|
export type PolymorphicProps<E extends React.ElementType, P> = P & AsProp<E> & Omit<React.ComponentPropsWithoutRef<E>, keyof P | "as">;
|
|
6
|
-
export type TypographyVariant = "display-lg" | "display-md" | "heading-xl" | "heading-lg" | "heading-md" | "heading-sm" | "heading-xs" | "body-md" | "body-sm" | "label-md" | "label-sm";
|
|
6
|
+
export type TypographyVariant = "display-lg" | "display-md" | "display-sm" | "heading-xl" | "heading-lg" | "heading-md" | "heading-sm" | "heading-xs" | "body-md" | "body-sm" | "label-md" | "label-sm";
|
|
7
7
|
export type TypographyColor = "default" | "muted" | "primary" | "secondary" | "error" | "success";
|
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
.display-md {
|
|
12
|
+
font-size: var(--font-size-40);
|
|
13
|
+
line-height: var(--line-tight);
|
|
14
|
+
font-weight: var(--font-weight-bold);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.display-sm {
|
|
12
18
|
font-size: var(--font-size-32);
|
|
13
19
|
line-height: var(--line-tight);
|
|
14
20
|
font-weight: var(--font-weight-bold);
|
|
@@ -27,25 +33,25 @@
|
|
|
27
33
|
.heading-lg {
|
|
28
34
|
font-size: var(--font-size-24);
|
|
29
35
|
line-height: var(--line-snug);
|
|
30
|
-
font-weight: var(--font-weight-
|
|
36
|
+
font-weight: var(--font-weight-bold);
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
.heading-md {
|
|
34
40
|
font-size: var(--font-size-20);
|
|
35
41
|
line-height: var(--line-snug);
|
|
36
|
-
font-weight: var(--font-weight-
|
|
42
|
+
font-weight: var(--font-weight-bold);
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
.heading-sm {
|
|
40
46
|
font-size: var(--font-size-18);
|
|
41
47
|
line-height: var(--line-snug);
|
|
42
|
-
font-weight: var(--font-weight-
|
|
48
|
+
font-weight: var(--font-weight-bold);
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
.heading-xs {
|
|
46
52
|
font-size: var(--font-size-16);
|
|
47
53
|
line-height: var(--line-snug);
|
|
48
|
-
font-weight: var(--font-weight-
|
|
54
|
+
font-weight: var(--font-weight-bold);
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
/* ========================================
|
|
@@ -55,13 +61,13 @@
|
|
|
55
61
|
.body-md {
|
|
56
62
|
font-size: var(--font-size-16);
|
|
57
63
|
line-height: var(--line-normal);
|
|
58
|
-
font-weight: var(--font-weight-
|
|
64
|
+
font-weight: var(--font-weight-medium);
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
.body-sm {
|
|
62
68
|
font-size: var(--font-size-14);
|
|
63
69
|
line-height: var(--line-normal);
|
|
64
|
-
font-weight: var(--font-weight-
|
|
70
|
+
font-weight: var(--font-weight-medium);
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
/* ========================================
|
|
@@ -71,13 +77,13 @@
|
|
|
71
77
|
.label-md {
|
|
72
78
|
font-size: var(--font-size-16);
|
|
73
79
|
line-height: var(--line-snug);
|
|
74
|
-
font-weight: var(--font-weight-
|
|
80
|
+
font-weight: var(--font-weight-regular);
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
.label-sm {
|
|
78
84
|
font-size: var(--font-size-14);
|
|
79
85
|
line-height: var(--line-snug);
|
|
80
|
-
font-weight: var(--font-weight-
|
|
86
|
+
font-weight: var(--font-weight-regular);
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
/* ========================================
|
|
@@ -88,6 +94,10 @@
|
|
|
88
94
|
color: var(--color-on-background);
|
|
89
95
|
}
|
|
90
96
|
|
|
97
|
+
.color-surface {
|
|
98
|
+
color: var(--color-on-surface);
|
|
99
|
+
}
|
|
100
|
+
|
|
91
101
|
.color-muted {
|
|
92
102
|
color: var(--color-on-surface-variant);
|
|
93
103
|
}
|
package/dist/styles/tokens.css
CHANGED
|
@@ -1,133 +1,126 @@
|
|
|
1
1
|
:root {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
-4px -4px 16px 8px var(--color-shadow-l),
|
|
96
|
-
4px 4px 16px 8px var(--color-shadow-d);
|
|
97
|
-
|
|
98
|
-
--shadow-surface-lg:
|
|
99
|
-
0px 4px 16px 8px var(--color-shadow-d);
|
|
2
|
+
/* font */
|
|
3
|
+
--font-size-12: 12px;
|
|
4
|
+
--font-size-14: 14px;
|
|
5
|
+
--font-size-16: 16px;
|
|
6
|
+
--font-size-18: 18px;
|
|
7
|
+
--font-size-20: 20px;
|
|
8
|
+
--font-size-24: 24px;
|
|
9
|
+
--font-size-28: 28px;
|
|
10
|
+
--font-size-32: 32px;
|
|
11
|
+
--font-size-40: 40px;
|
|
12
|
+
--font-size-48: 48px;
|
|
13
|
+
|
|
14
|
+
--line-tight: 1.2;
|
|
15
|
+
--line-snug: 1.35;
|
|
16
|
+
--line-normal: 1.6;
|
|
17
|
+
|
|
18
|
+
--font-weight-regular: 400;
|
|
19
|
+
--font-weight-medium: 500;
|
|
20
|
+
--font-weight-bold: 600;
|
|
21
|
+
|
|
22
|
+
/* ===== Radius ===== */
|
|
23
|
+
--radius-sm: 8px;
|
|
24
|
+
--radius-md: 16px;
|
|
25
|
+
--radius-max: 999px;
|
|
26
|
+
|
|
27
|
+
/* ===== Space ===== */
|
|
28
|
+
--space-xs: 4px;
|
|
29
|
+
--space-sm: 8px;
|
|
30
|
+
--space-md: 12px;
|
|
31
|
+
--space-lg: 16px;
|
|
32
|
+
--space-xl: 24px;
|
|
33
|
+
--space-2xl: 32px;
|
|
34
|
+
--space-3xl: 48px;
|
|
35
|
+
--space-4xl: 64px;
|
|
36
|
+
--space-5xl: 96px;
|
|
37
|
+
--gap-sm: 16px;
|
|
38
|
+
--gap-md: 32px;
|
|
39
|
+
--gap-lg: 64px;
|
|
40
|
+
|
|
41
|
+
/* ===== Color ===== */
|
|
42
|
+
--color-primary: #00a1a1;
|
|
43
|
+
--color-on-primary: #ffffff;
|
|
44
|
+
--color-primary-container: #9cf1f0;
|
|
45
|
+
--color-on-primary-container: #004f4f;
|
|
46
|
+
|
|
47
|
+
--color-secondary: #7b9695;
|
|
48
|
+
--color-on-secondary: #ffffff;
|
|
49
|
+
--color-secondary-container: #cce8e7;
|
|
50
|
+
--color-on-secondary-container: #324b4b;
|
|
51
|
+
|
|
52
|
+
--color-error: #ff5449;
|
|
53
|
+
--color-on-error: #ffffff;
|
|
54
|
+
--color-error-container: #ffdad6;
|
|
55
|
+
--color-on-error-container: #93000a;
|
|
56
|
+
|
|
57
|
+
--color-background: #f4fbfa;
|
|
58
|
+
--color-on-background: #161d1d;
|
|
59
|
+
|
|
60
|
+
--color-surface: #f4fbfabf;
|
|
61
|
+
--color-on-surface: #3a4444;
|
|
62
|
+
--color-on-surface-variant: #161d1d80;
|
|
63
|
+
--color-surface-container: #e9efeebf;
|
|
64
|
+
|
|
65
|
+
--color-border: #bbcccc;
|
|
66
|
+
--color-scrim: rgba(129, 129, 129, 0.25);
|
|
67
|
+
|
|
68
|
+
--color-shadow-l: rgba(255, 255, 255, 1);
|
|
69
|
+
--color-shadow-d: rgba(0, 0, 0, 0.2);
|
|
70
|
+
|
|
71
|
+
/* ===== Effect ===== */
|
|
72
|
+
--blur: blur(8px);
|
|
73
|
+
|
|
74
|
+
--shadow-sm:
|
|
75
|
+
-2px -2px 2px 1px var(--color-shadow-l),
|
|
76
|
+
2px 2px 2px 1px var(--color-shadow-d);
|
|
77
|
+
|
|
78
|
+
--shadow-sm-in:
|
|
79
|
+
inset -2px -2px 2px var(--color-shadow-l),
|
|
80
|
+
inset 2px 2px 2px var(--color-shadow-d);
|
|
81
|
+
|
|
82
|
+
--shadow-md:
|
|
83
|
+
-2px -2px 4px 2px var(--color-shadow-l),
|
|
84
|
+
2px 2px 4px 2px var(--color-shadow-d);
|
|
85
|
+
|
|
86
|
+
--shadow-md-in:
|
|
87
|
+
inset -4px -4px 4px var(--color-shadow-l),
|
|
88
|
+
inset 4px 4px 4px var(--color-shadow-d);
|
|
89
|
+
|
|
90
|
+
--shadow-lg:
|
|
91
|
+
-4px -4px 16px 8px var(--color-shadow-l),
|
|
92
|
+
4px 4px 16px 8px var(--color-shadow-d);
|
|
93
|
+
|
|
94
|
+
--shadow-modal: 0px 4px 16px 8px var(--color-shadow-d);
|
|
100
95
|
}
|
|
101
96
|
|
|
102
|
-
|
|
103
|
-
|
|
104
97
|
html[data-theme="dark"] {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
98
|
+
--color-primary: #80d5d4;
|
|
99
|
+
--color-on-primary: #003737;
|
|
100
|
+
--color-primary-container: #004f4f;
|
|
101
|
+
--color-on-primary-container: #9cf1f0;
|
|
102
|
+
|
|
103
|
+
--color-secondary: #b0cccb;
|
|
104
|
+
--color-on-secondary: #1b3534;
|
|
105
|
+
--color-secondary-container: #324b4b;
|
|
106
|
+
--color-on-secondary-container: #cce8e7;
|
|
107
|
+
|
|
108
|
+
--color-error: #ffb4ab;
|
|
109
|
+
--color-on-error: #690005;
|
|
110
|
+
--color-error-container: #93000a;
|
|
111
|
+
--color-on-error-container: #ffdad6;
|
|
112
|
+
|
|
113
|
+
--color-background: #1e2927;
|
|
114
|
+
--color-on-background: #dde4e3;
|
|
115
|
+
|
|
116
|
+
--color-surface: #1e2927bf;
|
|
117
|
+
--color-on-surface: #c3cbca;
|
|
118
|
+
--color-on-surface-variant: #dde4e380;
|
|
119
|
+
--color-surface-container: #262d2d;
|
|
120
|
+
|
|
121
|
+
--color-border: #889392;
|
|
122
|
+
--color-scrim: rgba(0, 0, 0, 0.25);
|
|
123
|
+
|
|
124
|
+
--color-shadow-l: rgba(255, 255, 255, 0.25);
|
|
125
|
+
--color-shadow-d: rgba(0, 0, 0, 1);
|
|
126
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lucentia-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "React UI design token and component system based on neumorphism, featuring two color themes: light and dark.",
|
|
5
|
+
"homepage": "https://lucentia.rikiyamatsuda.com/en",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
7
8
|
"exports": {
|