module-package-comp 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.
Files changed (50) hide show
  1. package/dist/component/ComponentDisplayer.d.ts +9 -0
  2. package/dist/component/ComponentDisplayer.js +59 -0
  3. package/dist/component/ComponentDisplayer.js.map +1 -0
  4. package/dist/component/PrimaryBtn.d.ts +8 -0
  5. package/dist/component/PrimaryBtn.js +7 -0
  6. package/dist/component/PrimaryBtn.js.map +1 -0
  7. package/dist/component/ShowAttr.d.ts +9 -0
  8. package/dist/component/ShowAttr.js +38 -0
  9. package/dist/component/ShowAttr.js.map +1 -0
  10. package/dist/component/inputs/Input.d.ts +13 -0
  11. package/dist/component/inputs/Input.js +7 -0
  12. package/dist/component/inputs/Input.js.map +1 -0
  13. package/dist/component/inputs/LabeledMutliSelect.d.ts +6 -0
  14. package/dist/component/inputs/LabeledMutliSelect.js +18 -0
  15. package/dist/component/inputs/LabeledMutliSelect.js.map +1 -0
  16. package/dist/component/inputs/LableledInput.d.ts +6 -0
  17. package/dist/component/inputs/LableledInput.js +18 -0
  18. package/dist/component/inputs/LableledInput.js.map +1 -0
  19. package/dist/component/inputs/LableledSelect.d.ts +6 -0
  20. package/dist/component/inputs/LableledSelect.js +18 -0
  21. package/dist/component/inputs/LableledSelect.js.map +1 -0
  22. package/dist/component/inputs/MultiSelect.d.ts +8 -0
  23. package/dist/component/inputs/MultiSelect.js +23 -0
  24. package/dist/component/inputs/MultiSelect.js.map +1 -0
  25. package/dist/component/inputs/Select.d.ts +7 -0
  26. package/dist/component/inputs/Select.js +5 -0
  27. package/dist/component/inputs/Select.js.map +1 -0
  28. package/dist/index.css +783 -0
  29. package/dist/index.d.ts +4 -0
  30. package/dist/index.js +5 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/util/type.d.ts +92 -0
  33. package/dist/util/type.js +39 -0
  34. package/dist/util/type.js.map +1 -0
  35. package/package.json +23 -0
  36. package/postcss.config.js +6 -0
  37. package/src/component/ComponentDisplayer.tsx +107 -0
  38. package/src/component/PrimaryBtn.tsx +30 -0
  39. package/src/component/ShowAttr.tsx +120 -0
  40. package/src/component/inputs/Input.tsx +43 -0
  41. package/src/component/inputs/LabeledMutliSelect.tsx +15 -0
  42. package/src/component/inputs/LableledInput.tsx +14 -0
  43. package/src/component/inputs/LableledSelect.tsx +14 -0
  44. package/src/component/inputs/MultiSelect.tsx +86 -0
  45. package/src/component/inputs/Select.tsx +28 -0
  46. package/src/index.css +3 -0
  47. package/src/index.tsx +6 -0
  48. package/src/util/type.ts +107 -0
  49. package/tailwind.config.js +12 -0
  50. package/tsconfig.json +113 -0
@@ -0,0 +1,4 @@
1
+ import "./index.css";
2
+ export * from "./component/ComponentDisplayer";
3
+ export * from "./component/ShowAttr";
4
+ export * from "./util/type";
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import "./index.css";
2
+ export * from "./component/ComponentDisplayer";
3
+ export * from "./component/ShowAttr";
4
+ export * from "./util/type";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAC;AAErB,cAAc,gCAAgC,CAAC;AAC/C,cAAc,sBAAsB,CAAC;AAErC,cAAc,aAAa,CAAC"}
@@ -0,0 +1,92 @@
1
+ export type DataJSON = Record<string, string | object>;
2
+ export type DateTime = string;
3
+ export declare enum ConditionEnum {
4
+ EQ = "equal",
5
+ SUP = "superior",
6
+ INF = "inferior"
7
+ }
8
+ export declare enum AttributeTypeEnum {
9
+ TEXT = "text",
10
+ LIST = "list",
11
+ NUMBER = "number",
12
+ BOOLEAN = "boolean",
13
+ DATE = "date",
14
+ NOTE = "note",
15
+ IMAGE = "image",
16
+ LINK = "link",
17
+ MULTIPLE = "multiple",
18
+ AUTOGEN = "autogen",
19
+ FILE = "file",
20
+ TITRE = "titre",
21
+ TEXT_OUTPUT = "text_output",
22
+ IMAGE_OUTPUT = "image_output",
23
+ VIDEO_OUTPUT = "video_output"
24
+ }
25
+ export declare enum ComponentType {
26
+ GENERAL = "general",
27
+ ACCESS = "access",
28
+ PEC = "PEC",
29
+ DOSSIER = "dossier",
30
+ PATIENT = "patient",
31
+ PRESCRIPTION = "prescription",
32
+ WORKFLOW = "workflow",
33
+ FOLDER = "folder",
34
+ CONSULTATION = "consultation",
35
+ TREE = "tree"
36
+ }
37
+ export interface Condition {
38
+ attribute: string;
39
+ cond: keyof typeof ConditionEnum;
40
+ value: string;
41
+ delimiter: string;
42
+ }
43
+ export interface SettingsAttribute {
44
+ max?: string;
45
+ min?: string;
46
+ color?: string;
47
+ }
48
+ export interface Attribute {
49
+ name: string;
50
+ label?: string;
51
+ type?: keyof typeof AttributeTypeEnum;
52
+ content?: Content;
53
+ settings?: SettingsAttribute;
54
+ }
55
+ export type Content = string | string[];
56
+ export interface SettingsComponent {
57
+ required?: boolean;
58
+ }
59
+ export interface Component {
60
+ _id: string;
61
+ name: string;
62
+ label: string;
63
+ type: keyof typeof ComponentType;
64
+ attributes: Attribute[];
65
+ conditions: Condition[][];
66
+ settings?: SettingsComponent;
67
+ filter?: DataJSON;
68
+ createdAt?: DateTime;
69
+ updatedAt?: DateTime;
70
+ }
71
+ export interface ComponentResult {
72
+ total: number;
73
+ page: number;
74
+ lastPage: number;
75
+ data: Component[];
76
+ }
77
+ export interface Data {
78
+ _id: string;
79
+ patient_dmi: string;
80
+ from: string;
81
+ component_id: Component;
82
+ value: DataJSON;
83
+ extra: DataJSON;
84
+ createdAt: DateTime;
85
+ updatedAt: DateTime;
86
+ }
87
+ export interface DataResult {
88
+ total: number;
89
+ page: number;
90
+ lastPage: number;
91
+ data: Data[];
92
+ }
@@ -0,0 +1,39 @@
1
+ // Enums
2
+ export var ConditionEnum;
3
+ (function (ConditionEnum) {
4
+ ConditionEnum["EQ"] = "equal";
5
+ ConditionEnum["SUP"] = "superior";
6
+ ConditionEnum["INF"] = "inferior";
7
+ })(ConditionEnum || (ConditionEnum = {}));
8
+ export var AttributeTypeEnum;
9
+ (function (AttributeTypeEnum) {
10
+ AttributeTypeEnum["TEXT"] = "text";
11
+ AttributeTypeEnum["LIST"] = "list";
12
+ AttributeTypeEnum["NUMBER"] = "number";
13
+ AttributeTypeEnum["BOOLEAN"] = "boolean";
14
+ AttributeTypeEnum["DATE"] = "date";
15
+ AttributeTypeEnum["NOTE"] = "note";
16
+ AttributeTypeEnum["IMAGE"] = "image";
17
+ AttributeTypeEnum["LINK"] = "link";
18
+ AttributeTypeEnum["MULTIPLE"] = "multiple";
19
+ AttributeTypeEnum["AUTOGEN"] = "autogen";
20
+ AttributeTypeEnum["FILE"] = "file";
21
+ AttributeTypeEnum["TITRE"] = "titre";
22
+ AttributeTypeEnum["TEXT_OUTPUT"] = "text_output";
23
+ AttributeTypeEnum["IMAGE_OUTPUT"] = "image_output";
24
+ AttributeTypeEnum["VIDEO_OUTPUT"] = "video_output";
25
+ })(AttributeTypeEnum || (AttributeTypeEnum = {}));
26
+ export var ComponentType;
27
+ (function (ComponentType) {
28
+ ComponentType["GENERAL"] = "general";
29
+ ComponentType["ACCESS"] = "access";
30
+ ComponentType["PEC"] = "PEC";
31
+ ComponentType["DOSSIER"] = "dossier";
32
+ ComponentType["PATIENT"] = "patient";
33
+ ComponentType["PRESCRIPTION"] = "prescription";
34
+ ComponentType["WORKFLOW"] = "workflow";
35
+ ComponentType["FOLDER"] = "folder";
36
+ ComponentType["CONSULTATION"] = "consultation";
37
+ ComponentType["TREE"] = "tree";
38
+ })(ComponentType || (ComponentType = {}));
39
+ //# sourceMappingURL=type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type.js","sourceRoot":"","sources":["../../src/util/type.ts"],"names":[],"mappings":"AAIA,QAAQ;AACR,MAAM,CAAN,IAAY,aAIX;AAJD,WAAY,aAAa;IACvB,6BAAY,CAAA;IACZ,iCAAgB,CAAA;IAChB,iCAAgB,CAAA;AAClB,CAAC,EAJW,aAAa,KAAb,aAAa,QAIxB;AAED,MAAM,CAAN,IAAY,iBAgBX;AAhBD,WAAY,iBAAiB;IAC3B,kCAAa,CAAA;IACb,kCAAa,CAAA;IACb,sCAAiB,CAAA;IACjB,wCAAmB,CAAA;IACnB,kCAAa,CAAA;IACb,kCAAa,CAAA;IACb,oCAAe,CAAA;IACf,kCAAa,CAAA;IACb,0CAAqB,CAAA;IACrB,wCAAmB,CAAA;IACnB,kCAAa,CAAA;IACb,oCAAe,CAAA;IACf,gDAA2B,CAAA;IAC3B,kDAA6B,CAAA;IAC7B,kDAA6B,CAAA;AAC/B,CAAC,EAhBW,iBAAiB,KAAjB,iBAAiB,QAgB5B;AAED,MAAM,CAAN,IAAY,aAWX;AAXD,WAAY,aAAa;IACvB,oCAAmB,CAAA;IACnB,kCAAiB,CAAA;IACjB,4BAAW,CAAA;IACX,oCAAmB,CAAA;IACnB,oCAAmB,CAAA;IACnB,8CAA6B,CAAA;IAC7B,sCAAqB,CAAA;IACrB,kCAAiB,CAAA;IACjB,8CAA6B,CAAA;IAC7B,8BAAa,CAAA;AACf,CAAC,EAXW,aAAa,KAAb,aAAa,QAWxB"}
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "module-package-comp",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "npx tailwindcss -i ./src/index.css -o ./dist/index.css && tsc",
9
+ "dev": "concurrently \"npx tailwindcss -i ./src/index.css -o ./dist/index.css --watch\" \"tsc -watch\""
10
+ },
11
+ "keywords": [],
12
+ "author": "",
13
+ "license": "ISC",
14
+ "devDependencies": {
15
+ "@types/react": "^18.3.3",
16
+ "@types/react-dom": "^18.3.0",
17
+ "concurrently": "^8.2.2",
18
+ "react": "^18.3.1",
19
+ "react-dom": "^18.3.1",
20
+ "tailwindcss": "^3.4.4",
21
+ "typescript": "^5.5.3"
22
+ }
23
+ }
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ }
@@ -0,0 +1,107 @@
1
+ import { useState } from "react";
2
+ import {
3
+ Attribute,
4
+ Component,
5
+ Condition,
6
+ ConditionEnum,
7
+ DataJSON,
8
+ } from "../util/type";
9
+ import { ShowAttr } from "./ShowAttr";
10
+ import PrimaryBtn from "./PrimaryBtn";
11
+
12
+ type ComponentDisplayerProps = {
13
+ components: Component[];
14
+ data: DataJSON;
15
+ setData: (data: DataJSON) => void;
16
+ onSubmit: () => void;
17
+ };
18
+
19
+ export function ComponentDisplayer({
20
+ components,
21
+ data,
22
+ setData,
23
+ onSubmit,
24
+ }: ComponentDisplayerProps) {
25
+ const [page, setPage] = useState(0);
26
+ return (
27
+ <form onSubmit={onSubmit} className="min-w-72">
28
+ <h1 className="font-bold text-center text-blue-800 text-4xl mb-8">
29
+ {components[page].label}
30
+ </h1>
31
+ <div className="space-y-3">
32
+ {components[page].attributes.map((attr) => {
33
+ if (canShow(attr, data, components[page].conditions || [])) {
34
+ return (
35
+ <ShowAttr
36
+ key={attr.name}
37
+ attribute={attr}
38
+ value={data[attr.name] ? String(data[attr.name]) : ""}
39
+ setValue={(s) => {
40
+ const temp = { ...data };
41
+ temp[attr.name] = s;
42
+ setData(temp);
43
+ }}
44
+ />
45
+ );
46
+ } else {
47
+ if (data[attr.name]) {
48
+ const temp = { ...data };
49
+ temp[attr.name] = "";
50
+ setData(temp);
51
+ }
52
+ }
53
+ })}
54
+ </div>
55
+ <div className="flex justify-center my-8">
56
+ <PrimaryBtn clickFn={onSubmit} text="Submit" />
57
+ </div>
58
+ </form>
59
+ );
60
+ }
61
+
62
+ function canShow(
63
+ attr: Attribute,
64
+ data: DataJSON,
65
+ conds: Condition[][],
66
+ ): boolean {
67
+ const conditions = conds.filter(
68
+ (c) => c[c.length - 1].delimiter === attr.name,
69
+ );
70
+
71
+ if (!conditions || conditions.length === 0) {
72
+ return true;
73
+ }
74
+
75
+ return conditions.some((conditionSet) => {
76
+ let lastDelimiter = "ou"; // Default to OR
77
+ return conditionSet.every((condition, index) => {
78
+ const result = evaluateCondition(condition, data);
79
+
80
+ if (index < conditionSet.length - 1) {
81
+ lastDelimiter = condition.delimiter;
82
+ }
83
+
84
+ if (lastDelimiter === "et") {
85
+ return result;
86
+ } else {
87
+ return result || index === conditionSet.length - 1;
88
+ }
89
+ });
90
+ });
91
+ }
92
+
93
+ function evaluateCondition(condition: Condition, data: DataJSON): boolean {
94
+ const { attribute, cond, value } = condition;
95
+ const dataValue = data[attribute];
96
+
97
+ switch (cond) {
98
+ case "EQ":
99
+ return value === dataValue;
100
+ case "INF":
101
+ return value < dataValue;
102
+ case "SUP":
103
+ return value > dataValue;
104
+ default:
105
+ return false;
106
+ }
107
+ }
@@ -0,0 +1,30 @@
1
+ import { useRef } from "react";
2
+
3
+ type props = {
4
+ text: string;
5
+ clickFn: () => void;
6
+ className?: string;
7
+ disabled?: boolean;
8
+ };
9
+
10
+ export default function PrimaryBtn({
11
+ text,
12
+ clickFn,
13
+ className,
14
+ disabled,
15
+ }: props) {
16
+ const btn = useRef<HTMLButtonElement>(null);
17
+ return (
18
+ <button
19
+ type="submit"
20
+ ref={btn}
21
+ onClick={clickFn}
22
+ className={
23
+ "rounded-2xl bg-secondary-blue bg-opacity-85 px-8 py-2 font-bold text-white-shade hover:bg-opacity-100"
24
+ }
25
+ disabled={disabled}
26
+ >
27
+ {text}
28
+ </button>
29
+ );
30
+ }
@@ -0,0 +1,120 @@
1
+ import { Attribute, AttributeTypeEnum, Content } from "../util/type";
2
+ import LabeledSelect from "./inputs/LableledSelect";
3
+ import LabeledInput from "./inputs/LableledInput";
4
+ import MultiSelect from "./inputs/MultiSelect";
5
+ import LabeledMutliSelect from "./inputs/LabeledMutliSelect";
6
+ import { useMemo } from "react";
7
+
8
+ type ShowAttrProps = {
9
+ attribute: Attribute;
10
+ value: string;
11
+ setValue: (s: string) => void;
12
+ showLabel?: boolean;
13
+ };
14
+
15
+ export function ShowAttr({
16
+ attribute,
17
+ value,
18
+ setValue,
19
+ showLabel = true,
20
+ }: ShowAttrProps) {
21
+ const label = useMemo(
22
+ () => (showLabel ? attribute.label || "" : ""),
23
+ [attribute.label],
24
+ );
25
+ const type = useMemo(() => {
26
+ if (attribute.type) return AttributeTypeEnum[attribute.type];
27
+ }, [attribute.type]);
28
+ switch (type) {
29
+ case AttributeTypeEnum.NUMBER:
30
+ return (
31
+ <LabeledInput
32
+ label={label}
33
+ value={value}
34
+ setValue={setValue}
35
+ type="number"
36
+ />
37
+ );
38
+ case AttributeTypeEnum.TEXT:
39
+ return (
40
+ <LabeledInput
41
+ label={label}
42
+ value={value}
43
+ setValue={setValue}
44
+ type="text"
45
+ />
46
+ );
47
+ case AttributeTypeEnum.LIST:
48
+ return (
49
+ <LabeledSelect
50
+ label={label}
51
+ selected={value}
52
+ setSelected={setValue}
53
+ options={(attribute.content as string[]) || []}
54
+ />
55
+ );
56
+ case AttributeTypeEnum.BOOLEAN:
57
+ return (
58
+ <LabeledInput
59
+ type="checkbox"
60
+ label={label}
61
+ checked={value === "true"}
62
+ setValue={(s) => setValue(s)}
63
+ />
64
+ );
65
+ case AttributeTypeEnum.DATE:
66
+ return (
67
+ <LabeledInput
68
+ type="date"
69
+ label={label}
70
+ value={value}
71
+ setValue={(date) => setValue(date)}
72
+ />
73
+ );
74
+ case AttributeTypeEnum.LINK:
75
+ return (
76
+ <div className="w-full">
77
+ <label className="block text-sm font-bold mb-1">{label}</label>
78
+ <a href={attribute.content as string}>{attribute.content}</a>
79
+ </div>
80
+ );
81
+ case AttributeTypeEnum.MULTIPLE:
82
+ return (
83
+ <LabeledMutliSelect
84
+ label={label}
85
+ selectedOptions={value !== "" ? value.split(",") : []}
86
+ onChange={(selected) => setValue(selected.join(","))}
87
+ options={(attribute.content as string[]) || []}
88
+ />
89
+ );
90
+ case AttributeTypeEnum.FILE:
91
+ return (
92
+ <LabeledInput
93
+ label={label}
94
+ type="file"
95
+ value={value}
96
+ setValue={setValue}
97
+ />
98
+ );
99
+ case AttributeTypeEnum.IMAGE_OUTPUT:
100
+ return (
101
+ <div className="w-full">
102
+ <label className="block text-sm font-bold mb-1">{label}</label>
103
+ <img className="w-64" src={attribute.content as string} />
104
+ </div>
105
+ );
106
+ case AttributeTypeEnum.TEXT_OUTPUT:
107
+ return (
108
+ <div className="w-full">
109
+ <label className="block text-sm font-bold mb-1">{label}</label>
110
+ <p>{attribute.content as string}</p>
111
+ </div>
112
+ );
113
+ case AttributeTypeEnum.TITRE:
114
+ return (
115
+ <div className="w-full">
116
+ <h2 className="text-3xl font-semibold">{attribute.label}</h2>
117
+ </div>
118
+ );
119
+ }
120
+ }
@@ -0,0 +1,43 @@
1
+ import { HTMLInputTypeAttribute } from "react";
2
+
3
+ export type InputProps = {
4
+ value?: string;
5
+ setValue: (s: string) => void;
6
+ type: HTMLInputTypeAttribute;
7
+ className?: string;
8
+ onBlur?: () => void;
9
+ maxLength?: number;
10
+ placeholder?: string;
11
+ disabled?: boolean;
12
+ checked?: boolean;
13
+ };
14
+
15
+ export default function Input({
16
+ value,
17
+ setValue,
18
+ className,
19
+ type,
20
+ onBlur,
21
+ maxLength,
22
+ placeholder,
23
+ disabled,
24
+ checked,
25
+ }: InputProps) {
26
+ return (
27
+ <input
28
+ type={type}
29
+ className="py-2 px-3 block w-full border-2 border-gray-200 rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none"
30
+ value={value}
31
+ onChange={(e) =>
32
+ type === "checkbox"
33
+ ? setValue(String(e.target.checked))
34
+ : setValue(e.target.value)
35
+ }
36
+ disabled={disabled}
37
+ onBlur={onBlur}
38
+ maxLength={maxLength}
39
+ placeholder={placeholder}
40
+ checked={checked}
41
+ />
42
+ );
43
+ }
@@ -0,0 +1,15 @@
1
+ import Input, { InputProps } from "./Input";
2
+ import MultiSelect, { MultiSelectProps } from "./MultiSelect";
3
+
4
+ type Props = {
5
+ label: string;
6
+ } & MultiSelectProps;
7
+
8
+ export default function LabeledMutliSelect({ label, ...inputprops }: Props) {
9
+ return (
10
+ <div className="w-full">
11
+ <label className="block text-sm font-bold mb-1">{label}</label>
12
+ <MultiSelect {...inputprops} />
13
+ </div>
14
+ );
15
+ }
@@ -0,0 +1,14 @@
1
+ import Input, { InputProps } from "./Input";
2
+
3
+ type Props = {
4
+ label: string;
5
+ } & InputProps;
6
+
7
+ export default function LabeledInput({ label, ...inputProps }: Props) {
8
+ return (
9
+ <div className="w-full">
10
+ <label className="block text-sm font-bold mb-1">{label}</label>
11
+ <Input {...inputProps} />
12
+ </div>
13
+ );
14
+ }
@@ -0,0 +1,14 @@
1
+ import Select, { SelectProps } from "./Select";
2
+
3
+ type Props = {
4
+ label: string;
5
+ } & SelectProps;
6
+
7
+ export default function LabeledSelect({ label, ...inputProps }: Props) {
8
+ return (
9
+ <div className="w-full">
10
+ <label className="block text-sm font-bold mb-1">{label}</label>
11
+ <Select {...inputProps} />
12
+ </div>
13
+ );
14
+ }
@@ -0,0 +1,86 @@
1
+ import React, { useState } from "react";
2
+
3
+ export type MultiSelectProps = {
4
+ options: string[];
5
+ selectedOptions: string[];
6
+ onChange: (selected: string[]) => void;
7
+ };
8
+
9
+ const MultiSelect: React.FC<MultiSelectProps> = ({
10
+ options,
11
+ selectedOptions,
12
+ onChange,
13
+ }) => {
14
+ const [isOpen, setIsOpen] = useState(false);
15
+ const [filter, setFilter] = useState("");
16
+
17
+ const toggleDropdown = () => setIsOpen(!isOpen);
18
+
19
+ const handleOptionClick = (option: string) => {
20
+ if (selectedOptions.includes(option)) {
21
+ onChange(selectedOptions.filter((selected) => selected !== option));
22
+ } else {
23
+ onChange([...selectedOptions, option]);
24
+ }
25
+ };
26
+ console.log(selectedOptions);
27
+ const handleFilterChange = (e: React.ChangeEvent<HTMLInputElement>) => {
28
+ setFilter(e.target.value);
29
+ };
30
+
31
+ const filteredOptions = options.filter((option) =>
32
+ option.toLowerCase().includes(filter.toLowerCase()),
33
+ );
34
+
35
+ return (
36
+ <div className="relative inline-block w-full">
37
+ <div
38
+ className="py-2 px-3 bg-white block w-full border-2 border-gray-200 rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none"
39
+ onClick={toggleDropdown}
40
+ >
41
+ {Array.isArray(selectedOptions) && selectedOptions.length > 0 ? (
42
+ selectedOptions.map((option) => (
43
+ <span
44
+ key={option}
45
+ className="mr-1 inline-block rounded-full bg-secondary-blue px-2 py-1 text-sm text-white-shade"
46
+ >
47
+ {option}
48
+ </span>
49
+ ))
50
+ ) : (
51
+ <span className="text-placeholder-gray">Select options</span>
52
+ )}
53
+ </div>
54
+ {isOpen && (
55
+ <div className="absolute left-0 right-0 z-10 mt-2 rounded-md border border-secondary-gray bg-white-shade">
56
+ <input
57
+ type="text"
58
+ className="w-full border-b border-secondary-gray p-2"
59
+ placeholder="Filter options..."
60
+ value={filter}
61
+ onChange={handleFilterChange}
62
+ />
63
+ <div className="max-h-60 overflow-y-auto">
64
+ {filteredOptions.map((option) => (
65
+ <div
66
+ key={option}
67
+ className="cursor-pointer p-2 hover:bg-primary-gray"
68
+ onClick={() => handleOptionClick(option)}
69
+ >
70
+ <input
71
+ type="checkbox"
72
+ checked={selectedOptions.includes(option)}
73
+ readOnly
74
+ className="mr-2"
75
+ />
76
+ <span className="ml-2">{option}</span>
77
+ </div>
78
+ ))}
79
+ </div>
80
+ </div>
81
+ )}
82
+ </div>
83
+ );
84
+ };
85
+
86
+ export default MultiSelect;
@@ -0,0 +1,28 @@
1
+ export type SelectProps = {
2
+ options: string[];
3
+ selected: string;
4
+ setSelected: (s: string) => void;
5
+ canDefault?: boolean;
6
+ };
7
+
8
+ export default function Select({
9
+ options,
10
+ selected,
11
+ setSelected,
12
+ canDefault,
13
+ }: SelectProps) {
14
+ return (
15
+ <select
16
+ value={selected || ""}
17
+ onChange={(e) => setSelected(e.target.value)}
18
+ className="py-2 px-3 bg-white block w-full border-2 border-gray-200 rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none"
19
+ >
20
+ <option value="" disabled={!canDefault} hidden={!canDefault}>
21
+ selection
22
+ </option>
23
+ {options.map((opt) => (
24
+ <option value={opt}>{opt}</option>
25
+ ))}
26
+ </select>
27
+ );
28
+ }
package/src/index.css ADDED
@@ -0,0 +1,3 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
package/src/index.tsx ADDED
@@ -0,0 +1,6 @@
1
+ import "./index.css";
2
+
3
+ export * from "./component/ComponentDisplayer";
4
+ export * from "./component/ShowAttr";
5
+
6
+ export * from "./util/type";