@edu-tosel/design 1.0.26 → 1.0.28

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/html/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { default as DatePicker } from "./widget/DatePicker";
2
1
  export { default as Input } from "./widget/Input";
2
+ export { default as Select } from "./template/Select";
3
+ export { default as DatePicker } from "./widget/DatePicker";
3
4
  export { default as Label } from "./template/Label";
4
- export { default as Select } from "./widget/Select";
5
5
  export { default as Toggle } from "./widget/Toggle";
package/html/index.js CHANGED
@@ -1,5 +1,5 @@
1
- export { default as DatePicker } from "./widget/DatePicker";
2
1
  export { default as Input } from "./widget/Input";
2
+ export { default as Select } from "./template/Select";
3
+ export { default as DatePicker } from "./widget/DatePicker";
3
4
  export { default as Label } from "./template/Label";
4
- export { default as Select } from "./widget/Select";
5
5
  export { default as Toggle } from "./widget/Toggle";
@@ -0,0 +1,3 @@
1
+ import { HTMLSelectElement } from "../../interface";
2
+ declare function Select<T extends string>(props: HTMLSelectElement<T>): import("react/jsx-runtime").JSX.Element;
3
+ export default Select;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import SelectDesign from "../widget/Select.design";
3
+ function Select(props) {
4
+ return _jsx(SelectDesign, { ...props });
5
+ }
6
+ export default Select;
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from "react";
3
3
  import { cn } from "../../util";
4
+ import Obstacle from "./Obstacle";
4
5
  const widthSize = {
5
6
  xs: "w-10",
6
7
  sm: "w-22.5",
@@ -29,10 +30,5 @@ export default function LabelDesign({ title, onClick, options, }) {
29
30
  fonts: "font-pretendard-light",
30
31
  styles: height !== "xs" ? "rounded-xl" : "rounded-md text-xs",
31
32
  };
32
- const obstacle = {
33
- positions: "absolute",
34
- sizes: "w-full h-full",
35
- background: "bg-white/80 ",
36
- };
37
- return (_jsx("div", { className: className, children: _jsxs("div", { className: cn(container), children: [_jsx("div", { className: cn(body), onClick: onClick, onMouseEnter: () => setHover(true), onMouseLeave: () => setHover(false), children: title }), disabled && (_jsx("div", { onClick: typeof disabled === "function" ? disabled : undefined, className: cn(obstacle) }))] }) }));
33
+ return (_jsx("div", { className: className, children: _jsxs("div", { className: cn(container), children: [_jsx("div", { className: cn(body), onClick: onClick, onMouseEnter: () => setHover(true), onMouseLeave: () => setHover(false), children: title }), _jsx(Obstacle, { disabled: disabled })] }) }));
38
34
  }
@@ -0,0 +1,2 @@
1
+ import { HTMLSelectElement } from "../../interface";
2
+ export default function SelectDesign<T extends string>({ state, selectOptions, placeholder, options, }: HTMLSelectElement<T>): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,108 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useId, useState } from "react";
3
+ import { cn } from "../../util";
4
+ import { useTransition, animated } from "react-spring";
5
+ const widthSize = {
6
+ xs: "w-10",
7
+ sm: "w-22.5",
8
+ md: "w-40",
9
+ lg: "w-50",
10
+ };
11
+ const heightSize = {
12
+ xs: "h-6.5",
13
+ };
14
+ export default function SelectDesign({ state, selectOptions, placeholder, options, }) {
15
+ const id = useId();
16
+ const [value, setValue] = state;
17
+ const [text, setText] = useState("");
18
+ const [search, setSearch] = useState("");
19
+ const [isOpen, setIsOpen] = useState(false);
20
+ const [filteredOptions, setFilterdOptions] = useState([]);
21
+ const [index, setIndex] = useState();
22
+ const onKeyDown = (e) => {
23
+ if (!filteredOptions)
24
+ return;
25
+ if (typeof index === "undefined")
26
+ return setIndex(0);
27
+ if (e.key === "Enter") {
28
+ setText(filteredOptions[index][1]);
29
+ return setValue(filteredOptions[index][0]);
30
+ }
31
+ if (e.key === "ArrowDown")
32
+ return setIndex((index + 1) % filteredOptions.length);
33
+ if (e.key === "ArrowUp")
34
+ return setIndex((index - 1 + filteredOptions.length) % filteredOptions.length);
35
+ };
36
+ useEffect(() => {
37
+ setFilterdOptions(selectOptions?.filter(([_, text]) => text.includes(search)));
38
+ }, [search, selectOptions]);
39
+ const isLong = selectOptions && selectOptions.length >= 4;
40
+ const container = {
41
+ positions: "relative",
42
+ displays: "flex flex-col",
43
+ };
44
+ const button = {
45
+ displays: "flex items-center",
46
+ sizes: `${widthSize[options?.width ?? "md"]} ${heightSize[options?.height ?? "xs"]}`,
47
+ styles: "pl-1 rounded-md box-shadow focus:outline-none",
48
+ };
49
+ const input = {
50
+ positions: "absolute",
51
+ layouts: cn(button),
52
+ styles: "focus:outline-none text-xs pl-1.5",
53
+ };
54
+ const body = {
55
+ positions: "absolute",
56
+ displays: "flex flex-col gap-1",
57
+ sizes: `${widthSize[options?.width ?? "md"]} min-h-6.5 max-h-23.75`,
58
+ paddings: "pl-1 py-1",
59
+ styles: "bg-white box-shadow rounded-md box-shadow overflow-y-scroll",
60
+ };
61
+ const label = {
62
+ displays: "flex",
63
+ sizes: "h-4.5 hover:w-full",
64
+ paddings: "px-1",
65
+ styles: "rounded-sm duration-500",
66
+ fonts: "text-xs text-gray-dim bg-gray-light",
67
+ };
68
+ const inputTransition = useTransition(isOpen, {
69
+ from: {
70
+ opacity: 0,
71
+ top: 0,
72
+ },
73
+ enter: {
74
+ opacity: 1,
75
+ top: 32,
76
+ config: { duration: 150 },
77
+ },
78
+ leave: {
79
+ opacity: 0,
80
+ top: 0,
81
+ config: { duration: 0 },
82
+ },
83
+ });
84
+ const bodyTransitions = useTransition(isOpen, {
85
+ from: {
86
+ opacity: 0,
87
+ top: 0,
88
+ },
89
+ enter: {
90
+ opacity: 1,
91
+ top: isLong ? 64 : 32,
92
+ config: { duration: 150 },
93
+ },
94
+ leave: {
95
+ opacity: 0,
96
+ top: 0,
97
+ config: { duration: 0 },
98
+ },
99
+ });
100
+ return (_jsxs("div", { className: cn(container), onKeyDown: onKeyDown, children: [_jsx("button", { onClick: () => setIsOpen(!isOpen), className: cn(button), children: value ? (_jsx("div", { className: cn(label), children: selectOptions?.find((option) => value === option[0])?.[1] })) : (_jsx("div", { className: "text-xs pl-1", children: placeholder ?? "선택해주세요" })) }), inputTransition((styles, item) => isLong &&
101
+ item && (_jsx(animated.input, { style: styles, placeholder: "\uAC80\uC0C9\uC5B4\uB97C \uC785\uB825\uD558\uC138\uC694", className: cn(input), onChange: (e) => setSearch(e.target.value) }))), bodyTransitions((styles, item) => {
102
+ return (item && (_jsx(animated.div, { style: styles, className: cn(body), children: filteredOptions?.map(([value, text], order) => (_jsx("section", { children: _jsx("button", { onClick: () => {
103
+ setValue(value);
104
+ setText(text);
105
+ return setIsOpen(false);
106
+ }, className: cn(label, order === index ? "w-full" : "w-auto"), children: text }) }, id + value))) })));
107
+ })] }));
108
+ }
@@ -1,6 +1,6 @@
1
1
  import { DataField, Size, State, Titles } from "./Property";
2
2
  import { EventsProps } from "./Widget";
3
- import { HTMLInputElement, HTMLLabelElement } from "./HTMLElement";
3
+ import { HTMLInputElement, HTMLLabelElement, SelectOption } from "./HTMLElement";
4
4
  interface OptionsProps {
5
5
  classNames?: string;
6
6
  width?: Size;
@@ -25,7 +25,7 @@ export interface BoardHeaderProps {
25
25
  selects?: {
26
26
  title: string;
27
27
  state: State<string>;
28
- options: [string | number, string][];
28
+ options: SelectOption<string>[];
29
29
  width?: Size;
30
30
  }[];
31
31
  inputs?: HTMLInputElement[];
@@ -21,9 +21,11 @@ export interface HTMLLabelElement extends HTMLElement {
21
21
  title: string;
22
22
  onClick?: OnClick;
23
23
  }
24
- export interface HTMLSelectElement extends HTMLElement {
25
- state: State<string>;
26
- selectOptions?: [string | number, string][];
24
+ export type SelectOption<T> = [T, string];
25
+ export interface HTMLSelectElement<T> extends HTMLElement {
26
+ state: State<T>;
27
+ selectOptions?: SelectOption<T>[];
28
+ placeholder?: string;
27
29
  }
28
30
  export interface HTMLInputElement extends HTMLElement {
29
31
  state: State<string> | State<string | undefined>;
@@ -34,3 +36,12 @@ export interface HTMLToggleElement extends HTMLElement {
34
36
  state: State<boolean>;
35
37
  labels: [[true, string], [false, string]];
36
38
  }
39
+ export interface HTMLElementFrame {
40
+ title: string;
41
+ type: HTMLElementType;
42
+ state: State<any>;
43
+ selectOptions?: SelectOption<string | number>[];
44
+ labels?: [string | boolean, string][];
45
+ disabled?: Disabled;
46
+ onClick?: OnClick;
47
+ }
@@ -1,20 +1,11 @@
1
- import { HTMLElementType, HTMLLabelElement } from "./HTMLElement";
2
- import { Disabled, OnClick, State, Titles } from "./Property";
1
+ import { HTMLElementFrame, HTMLLabelElement } from "./HTMLElement";
2
+ import { Titles } from "./Property";
3
3
  export interface OverlayProps {
4
4
  titles: Titles;
5
5
  isVisible?: boolean;
6
6
  event?: string;
7
7
  children?: React.ReactNode;
8
8
  }
9
- export interface HTMLElementFrame {
10
- title: string;
11
- type: HTMLElementType;
12
- state: State<any>;
13
- selectOptions?: [string | number, string][];
14
- labels?: [string | boolean, string][];
15
- disabled?: Disabled;
16
- onClick?: OnClick;
17
- }
18
9
  export interface InfoOverlayProps extends OverlayProps {
19
10
  }
20
11
  export interface ManageOverlayProps extends OverlayProps {
@@ -7,7 +7,7 @@ import { cn } from "../../util";
7
7
  export default function Manage({ titles, isVisible, event, elements, buttons, }) {
8
8
  const id = useId();
9
9
  const body = {
10
- sizes: "w-full xl:h-120 ",
10
+ sizes: "w-full min-h-160 xl:h-120 ",
11
11
  styles: "overflow-y-scroll",
12
12
  };
13
13
  return (_jsxs(OverlayDesign, { titles: titles, event: event, isVisible: isVisible, children: [_jsx("div", { className: cn(body), children: elements?.map(({ title, type, labels, state, selectOptions, disabled }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edu-tosel/design",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
4
4
  "description": "UI components for International TOSEL Committee",
5
5
  "keywords": [
6
6
  "jsx",
@@ -87,11 +87,13 @@ export default {
87
87
  },
88
88
  spacing: {
89
89
  "sun-calc": "calc(50% - 6rem)",
90
+ 0.5: "0.125rem",
90
91
  1: "0.25rem",
91
92
  2.5: "0.625rem",
92
93
  3.5: "0.875rem",
93
94
  3.75: "0.9375rem",
94
95
  4.25: "1.0625rem",
96
+ 4.5: "1.125rem",
95
97
  4.75: "1.1875rem",
96
98
  5.25: "1.3125rem",
97
99
  5.75: "1.4375rem",
@@ -103,6 +105,7 @@ export default {
103
105
  8: "2rem",
104
106
  9: "2.25rem",
105
107
  9.25: "2.3125rem",
108
+ 9.5: "2.375rem",
106
109
  10.25: "2.5625rem",
107
110
  11: "2.75rem",
108
111
  11.25: "2.8125rem",
@@ -129,6 +132,7 @@ export default {
129
132
  22: "5.5rem",
130
133
  22.5: "5.625rem",
131
134
  23: "5.75rem",
135
+ 23.75: "5.9375rem",
132
136
  25: "6.25rem",
133
137
  26: "6.5rem",
134
138
  27: "6.75rem",
package/version.txt CHANGED
@@ -1 +1 @@
1
- 1.0.26
1
+ 1.0.28
@@ -1,2 +0,0 @@
1
- import { HTMLSelectElement } from "../../interface";
2
- export default function Select({ state, selectOptions, options, }: HTMLSelectElement): import("react/jsx-runtime").JSX.Element;
@@ -1,170 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useEffect } from "react";
3
- import { BiChevronDown } from "react-icons/bi";
4
- import { useWidgetStore } from "../../store";
5
- import { cn } from "../../util";
6
- const widthSize = {
7
- xs: "w-32",
8
- sm: "w-48",
9
- md: "w-64",
10
- lg: "w-96",
11
- xl: "w-128",
12
- "2xl": "w-144",
13
- full: "w-full",
14
- };
15
- export default function Select({ state, selectOptions, options, }) {
16
- const [value, setValue] = state;
17
- // isActive 드롭다운 이벤트 통제
18
- const { flag } = useWidgetStore();
19
- useEffect(() => {
20
- if (!isButton)
21
- setIsActive(false);
22
- return setIsButton(false);
23
- }, [flag]);
24
- const levelsKey = {
25
- CoCoon: "CC",
26
- "Pre-Starter": "PS",
27
- Starter: "ST",
28
- Basic: "BA",
29
- Junior: "JR",
30
- "High Junior": "HJ",
31
- };
32
- const [isActive, setIsActive] = useState(false);
33
- const [isButton, setIsButton] = useState(false);
34
- const [selectedItem, setSelectedItem] = useState(null);
35
- const handleItemClick = (title) => {
36
- setSelectedItem(title);
37
- setIsActive(false);
38
- const selectedValue = levelsKey[title] || title;
39
- setValue(selectedValue);
40
- };
41
- // css
42
- const levels = [
43
- "CoCoon",
44
- "Pre-Starter",
45
- "Starter",
46
- "Basic",
47
- "Junior",
48
- "High Junior",
49
- ];
50
- const colorMap = {
51
- CoCoon: "bg-cocoon-green",
52
- "Pre-Starter": "bg-ps-pink",
53
- Starter: "bg-st-orange",
54
- Basic: "bg-ba-yellow",
55
- Junior: "bg-jr-blue",
56
- "High Junior": "bg-hj-blue",
57
- };
58
- const isActiveCss = () => {
59
- if (isActive)
60
- return `shadow-md border-inner rounded-md`;
61
- };
62
- const tagCss = `bg-slate-200 p-1 pl-2 pr-2 rounded-md`;
63
- const dropdownTop = selectOptions && selectOptions.length > 6 ? "top-24" : "top-12";
64
- const { width } = options ?? {};
65
- const dropdown = {
66
- size: `w-40 h-6`,
67
- styles: "cursor-pointer pt-4 pb-4 pl-2 pr-4 font-pretendard-bold text-xs bg-white",
68
- display: `flex items-center`,
69
- hover: `hover:rounded-md`,
70
- effect: `transition-all`,
71
- function: `${isActiveCss()}`,
72
- etc: "box-inner-shadow",
73
- };
74
- const dropdownInput = {
75
- size: `w-40 h-6 `,
76
- styles: "shadow-md text-xs pt-4 pb-4 pl-2 pr-4 font-pretendard-bold bg-white text-left cursor-text border-1 rounded-md ",
77
- display: "absolute top-12 flex items-center z-10 ",
78
- focus: "focus:outline-none focus:border-[#44C5F3] focus:border-1 ",
79
- };
80
- const dropdownContent = {
81
- size: `w-40 min-h-6 max-h-48 overflow-y-auto `,
82
- styles: `text-xs mt-1 border-1 rounded-md shadow-md`,
83
- display: `absolute z-40`,
84
- function: `${dropdownTop}`,
85
- };
86
- const dropdownItem = {
87
- size: `${widthSize[width ?? "full"]} h-6 `,
88
- styles: `text-xs text-left pt-4 pb-4 pl-2 pr-4 font-pretendard-bold cursor-pointer `,
89
- display: `flex items-center z-40`,
90
- hover: `hover:bg-slate-200 `,
91
- effect: `transition-all `,
92
- };
93
- // input
94
- const [searchTerm, setSearchTerm] = useState("");
95
- const filteredOptions = selectOptions?.filter(([_, title]) => title.toLowerCase().includes(searchTerm.toLowerCase())) || [];
96
- // Keyboard event handling
97
- const [selectedIndex, setSelectedIndex] = useState(-1);
98
- useEffect(() => {
99
- const handleKeyDown = (event) => {
100
- if (!isActive)
101
- return;
102
- // Arrow Up
103
- if (event.key === "ArrowUp") {
104
- event.preventDefault();
105
- const currentIndex = filteredOptions.findIndex(([_, title]) => title === selectedItem);
106
- const newIndex = currentIndex === -1
107
- ? 0
108
- : (currentIndex - 1 + filteredOptions.length) %
109
- filteredOptions.length;
110
- setSelectedItem(filteredOptions[newIndex][1]);
111
- setSelectedIndex(newIndex);
112
- }
113
- // Arrow Down
114
- if (event.key === "ArrowDown") {
115
- event.preventDefault();
116
- const currentIndex = filteredOptions.findIndex(([_, title]) => title === selectedItem);
117
- const newIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % filteredOptions.length;
118
- setSelectedItem(filteredOptions[newIndex][1]);
119
- setSelectedIndex(newIndex);
120
- }
121
- // Enter
122
- if (event.key === "Enter") {
123
- if (selectedItem) {
124
- setIsActive(false);
125
- setValue(selectedItem);
126
- }
127
- }
128
- // Esc
129
- if (event.key === "Escape") {
130
- setIsActive(false);
131
- }
132
- };
133
- window.addEventListener("keydown", handleKeyDown);
134
- // 선택된 항목 스크롤을 조정하는 함수
135
- const scrollIntoView = () => {
136
- if (selectedIndex === -1)
137
- return;
138
- const dropdownContent = document.querySelector(".dropdown-content");
139
- if (!dropdownContent)
140
- return;
141
- const selectedItemElement = dropdownContent.children[selectedIndex];
142
- if (!selectedItemElement)
143
- return;
144
- const dropdownRect = dropdownContent.getBoundingClientRect();
145
- const selectedItemRect = selectedItemElement.getBoundingClientRect();
146
- if (selectedItemRect.top < dropdownRect.top) {
147
- dropdownContent.scrollTop -= dropdownRect.top - selectedItemRect.top;
148
- }
149
- else if (selectedItemRect.bottom > dropdownRect.bottom) {
150
- dropdownContent.scrollTop +=
151
- selectedItemRect.bottom - dropdownRect.bottom;
152
- }
153
- };
154
- scrollIntoView();
155
- window.addEventListener("resize", scrollIntoView);
156
- return () => {
157
- window.removeEventListener("keydown", handleKeyDown);
158
- window.removeEventListener("resize", scrollIntoView);
159
- };
160
- }, [isActive, selectedIndex, filteredOptions, selectedItem, setValue]);
161
- return (_jsxs("div", { className: "relative", children: [_jsxs("div", { className: cn(dropdown), onClick: () => {
162
- setIsActive(!isActive);
163
- setIsButton(true);
164
- }, children: [_jsx("div", { className: `w-4 h-4 mr-2 ${selectedItem && colorMap[selectedItem]
165
- ? colorMap[selectedItem]
166
- : "hidden"}` }), _jsx("div", { className: `${!selectedItem || levels.includes(selectedItem) ? "" : tagCss}`, children: selectedItem || (_jsx("span", { className: "text-zinc-400", children: "\uC635\uC158\uC744 \uC120\uD0DD\uD558\uC138\uC694" })) }), isActive && _jsx(BiChevronDown, { className: "ml-auto" })] }), isActive && selectOptions && selectOptions.length > 6 && (_jsx("input", { type: "text", placeholder: "\uAC80\uC0C9\uC5B4\uB97C \uC785\uB825\uD558\uC138\uC694", className: cn(dropdownInput), value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), onClick: (e) => e.stopPropagation() })), isActive && filteredOptions && (_jsx("div", { className: "dropdown-content " + cn(dropdownContent), children: filteredOptions.map(([id, title], index) => (_jsxs("div", { className: cn(dropdownItem) +
167
- (selectedIndex === index ? "bg-slate-200" : "bg-white"), onClick: () => handleItemClick(title), children: [_jsx("div", { className: `w-4 h-4 mr-2 ${colorMap[title] && levels.includes(title)
168
- ? colorMap[title]
169
- : "hidden"}` }), _jsx("div", { className: `${levels.includes(title) ? "" : tagCss}`, children: title })] }, id))) }))] }));
170
- }