@solidpb/ui-kit 0.4.1 → 0.5.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.
@@ -25,6 +25,21 @@ const checkbox = tv({
25
25
  size: "sm",
26
26
  },
27
27
  });
28
+ const label = tv({
29
+ base: "label text-xs",
30
+ variants: {
31
+ size: {
32
+ xs: "text-xs",
33
+ sm: "text-xs",
34
+ md: "text-xs",
35
+ lg: "text-sm",
36
+ xl: "text-sm",
37
+ },
38
+ },
39
+ defaultVariants: {
40
+ size: "sm",
41
+ },
42
+ });
28
43
  export const Checkbox = (props) => {
29
44
  const [local, inputProps] = splitProps(props, ["label", "size", "appearance", "class", "onChange"]);
30
45
  return (<label class="flex items-center gap-3 w-fit">
@@ -36,7 +51,7 @@ export const Checkbox = (props) => {
36
51
  local.onChange?.(e.currentTarget.checked);
37
52
  }}/>
38
53
 
39
- {local.label && <span class="select-none">{local.label}</span>}
54
+ {local.label && <span class={label({ size: local.size })}>{local.label}</span>}
40
55
  </label>);
41
56
  };
42
57
  export default Checkbox;
@@ -20,6 +20,7 @@ export function createForm() {
20
20
  const Form = (props) => {
21
21
  const [values, setValues] = createStore({ ...props.data });
22
22
  const setValue = (key, value) => {
23
+ console.log("Setting value", key, value);
23
24
  setValues(key, value);
24
25
  };
25
26
  const getValue = (key) => {
@@ -1,4 +1,4 @@
1
- import { JSXElement } from "solid-js";
1
+ import { JSX, JSXElement } from "solid-js";
2
2
  export interface RelationPickerProps<T> {
3
3
  value: T | T[] | null;
4
4
  options: T[];
@@ -18,6 +18,8 @@ export interface RelationPickerProps<T> {
18
18
  onTextInputChange?: (text: string) => void;
19
19
  defaultFilter?: (option: T[] | Exclude<NonNullable<T>, null>, filter: string) => boolean;
20
20
  onLinkClick?: (value: T) => void;
21
+ href?: string;
22
+ onCreateInline?: (text: string) => Promise<T | undefined>;
21
23
  }
22
- export declare const RelationPicker: <T>(props: RelationPickerProps<T>) => import("solid-js").JSX.Element;
24
+ export declare const RelationPicker: <T extends object>(props: RelationPickerProps<T>) => JSX.Element;
23
25
  export default RelationPicker;
@@ -72,6 +72,21 @@ export const RelationPicker = (props) => {
72
72
  ];
73
73
  return props.options;
74
74
  };
75
+ const handleKeyDown = async (e) => {
76
+ if (e.key === "Enter" && props.onCreateInline) {
77
+ e.preventDefault();
78
+ e.stopPropagation();
79
+ const newValue = await props.onCreateInline(inputRef.value);
80
+ if (newValue) {
81
+ if (props.multi) {
82
+ props.onChange([...(Array.isArray(props.value) ? props.value : []), newValue]);
83
+ }
84
+ else {
85
+ props.onChange(newValue);
86
+ }
87
+ }
88
+ }
89
+ };
75
90
  return (<div class="floating-label">
76
91
  {props.label && <span>{props.label}</span>}
77
92
  <Combobox disabled={props.disabled} multiple={props.multi} value={values()} onChange={props.onChange} options={options()}
@@ -103,9 +118,9 @@ export const RelationPicker = (props) => {
103
118
  tags: props.multi && Array.isArray(props.value) && props.value.length > 0,
104
119
  })}>
105
120
  <Show when={props.multi} fallback={<>
106
- {!props.multi && values() && (<Button variant="ghost" appearance="primary" size="xs" modifier="square" onClick={() => props.onLinkClick?.(props.value)}>
121
+ {!props.multi && values() && (props.href || props.onLinkClick) && (<a class="btn btn-ghost btn-primary btn-xs btn-square" href={props.href} onClick={() => props.onLinkClick?.(props.value)}>
107
122
  <Link class="w-[1em] h-[1em]"/>
108
- </Button>)}
123
+ </a>)}
109
124
  <Combobox.Input onBlur={(e) => {
110
125
  if (!props.value) {
111
126
  e.currentTarget.value = "";
@@ -113,15 +128,15 @@ export const RelationPicker = (props) => {
113
128
  else {
114
129
  e.currentTarget.value = String(state.selectedOptions()[0][props.labelKey]);
115
130
  }
116
- }} ref={inputRef} onInput={(e) => props.onTextInputChange?.(e.currentTarget.value)}/>
131
+ }} ref={inputRef} onInput={(e) => props.onTextInputChange?.(e.currentTarget.value)} onKeyDown={handleKeyDown}/>
117
132
  </>}>
118
133
  <div class="flex flex-wrap gap-1 w-full">
119
134
  <For each={state.selectedOptions()}>
120
135
  {(option) => (<span onPointerDown={(e) => e.stopPropagation()}>
121
- <Tag appearance="neutral" variant="soft" title={String(option[props.labelKey])} onDelete={() => state.remove(option)}/>
136
+ <Tag appearance="neutral" variant="soft" title={String(option[props.labelKey])} onDelete={() => state.remove(option)} colorHex={"colorHex" in option ? option.colorHex : undefined}/>
122
137
  </span>)}
123
138
  </For>
124
- <Combobox.Input class="w-[unset]" onBlur={(e) => (e.currentTarget.value = "")} ref={inputRef} onInput={(e) => props.onTextInputChange?.(e.currentTarget.value)}/>
139
+ <Combobox.Input class="w-[unset]" onBlur={(e) => (e.currentTarget.value = "")} ref={inputRef} onInput={(e) => props.onTextInputChange?.(e.currentTarget.value)} onKeyDown={handleKeyDown}/>
125
140
  </div>
126
141
  </Show>
127
142
  </div>
@@ -25,6 +25,21 @@ const toggle = tv({
25
25
  size: "sm",
26
26
  },
27
27
  });
28
+ const label = tv({
29
+ base: "label text-xs",
30
+ variants: {
31
+ size: {
32
+ xs: "text-xs",
33
+ sm: "text-xs",
34
+ md: "text-xs",
35
+ lg: "text-sm",
36
+ xl: "text-sm",
37
+ },
38
+ },
39
+ defaultVariants: {
40
+ size: "sm",
41
+ },
42
+ });
28
43
  export const Switch = (props) => {
29
44
  const [local, inputProps] = splitProps(props, ["label", "size", "appearance", "class", "onChange"]);
30
45
  return (<label class="flex items-center gap-3 w-fit">
@@ -33,9 +48,9 @@ export const Switch = (props) => {
33
48
  appearance: local.appearance,
34
49
  class: local.class,
35
50
  })} onChange={(e) => {
36
- local.onChange?.(Boolean(e.currentTarget.value));
51
+ local.onChange?.(e.currentTarget.checked);
37
52
  }}/>
38
- {local.label && <span class="select-none">{local.label}</span>}
53
+ {local.label && <span class={label({ size: local.size })}>{local.label}</span>}
39
54
  </label>);
40
55
  };
41
56
  export default Switch;
package/dist/index.d.ts CHANGED
@@ -28,7 +28,6 @@ export * from "./components/Switch";
28
28
  export * from "./components/Table";
29
29
  export * from "./components/Tabs";
30
30
  export * from "./components/Tag";
31
- export * from "./components/TagArea";
32
31
  export * from "./components/TextArea";
33
32
  export * from "./components/ThemeSwitch";
34
33
  export * from "./components/Toast";
package/dist/index.js CHANGED
@@ -28,7 +28,6 @@ export * from "./components/Switch";
28
28
  export * from "./components/Table";
29
29
  export * from "./components/Tabs";
30
30
  export * from "./components/Tag";
31
- export * from "./components/TagArea";
32
31
  export * from "./components/TextArea";
33
32
  export * from "./components/ThemeSwitch";
34
33
  export * from "./components/Toast";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidpb/ui-kit",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -1,15 +0,0 @@
1
- import { JSX, JSXElement } from "solid-js";
2
- interface TagAreaProps<T extends Tag = Tag> {
3
- tags: T[];
4
- setTags: (tags: T[]) => void;
5
- onCreateTag: (name: string) => Promise<T | undefined>;
6
- onDeleteTag: (tag: T) => Promise<void>;
7
- suggestions?: T[];
8
- noSuggestionsPlaceholder?: string;
9
- dropDownAction?: JSXElement;
10
- placeholder?: string;
11
- editable?: boolean;
12
- size?: "xs" | "sm" | "md" | "lg" | "xl";
13
- }
14
- export declare const TagArea: <T extends Tag = Tag>(props: TagAreaProps<T>) => JSX.Element;
15
- export default TagArea;
@@ -1,116 +0,0 @@
1
- import { createSignal, For, Show, createMemo } from "solid-js";
2
- import { TextField } from "@kobalte/core/text-field";
3
- import Tag from "../Tag/Tag";
4
- import { tv } from "tailwind-variants";
5
- const tagArea = tv({
6
- base: "textarea outline-offset-0 p-2 min-h-2",
7
- variants: {
8
- editing: {
9
- true: "",
10
- false: "cursor-pointer",
11
- },
12
- },
13
- });
14
- const menu = tv({
15
- base: "dropdown-content bg-base-200 min-w-30 shadow-sm rounded-box menu absolute border border-base-200 gap-1",
16
- variants: {
17
- size: {
18
- xs: "menu-xs",
19
- sm: "menu-sm",
20
- md: "menu-base",
21
- lg: "menu-lg",
22
- xl: "menu-xl",
23
- },
24
- },
25
- defaultVariants: {
26
- size: "sm",
27
- },
28
- });
29
- export const TagArea = (props) => {
30
- const [tagInput, setTagInput] = createSignal("");
31
- const [showSuggestions, setShowSuggestions] = createSignal(false);
32
- const [editing, setEditing] = createSignal(false);
33
- let inputRef;
34
- const filteredSuggestions = createMemo(() => (props.suggestions || []).filter((s) => s.name.toLowerCase().includes(tagInput().toLowerCase()) && !props.tags.some((t) => t.id === s.id)));
35
- const handleTagInput = async (e) => {
36
- setShowSuggestions(true);
37
- if (tagInput().trim()) {
38
- if (e.key === "Enter") {
39
- e.preventDefault();
40
- e.stopPropagation();
41
- const newTagName = tagInput().trim();
42
- if (!props.tags.map((t) => t.name).includes(newTagName)) {
43
- let newTag = undefined;
44
- newTag = await props.onCreateTag(newTagName);
45
- if (newTag)
46
- props.setTags([...props.tags, newTag]);
47
- }
48
- setTagInput("");
49
- setShowSuggestions(false);
50
- }
51
- else {
52
- setShowSuggestions(true);
53
- }
54
- }
55
- else {
56
- if (e.key === "Delete" || e.key === "Backspace") {
57
- if (props.tags.length > 0) {
58
- const lastTag = props.tags[props.tags.length - 1];
59
- await props.onDeleteTag(lastTag);
60
- props.setTags(props.tags.slice(0, -1));
61
- }
62
- }
63
- }
64
- };
65
- const handleSuggestionClick = (tag) => {
66
- props.setTags([...props.tags, tag]);
67
- setTagInput("");
68
- setShowSuggestions(false);
69
- };
70
- const deleteTag = async (t) => {
71
- await props.onDeleteTag(t);
72
- props.setTags((props.tags || []).filter((tag) => tag.id !== t.id));
73
- };
74
- return (<div class={tagArea({ editing: editing() })} onMouseDown={(e) => {
75
- if (props.editable === false)
76
- return;
77
- e.preventDefault();
78
- setEditing(true);
79
- inputRef?.focus();
80
- }}>
81
- <div class="flex flex-wrap gap-1">
82
- <For each={props.tags || []}>
83
- {(t) => (<Tag title={t.name || ""} colorHex={t.colorHex || "#6b7280"} onDelete={editing()
84
- ? () => {
85
- setShowSuggestions(false);
86
- deleteTag(t);
87
- }
88
- : undefined} size={props.size}/>)}
89
- </For>
90
- {editing() && props.editable !== false && (<div class="relative flex-1 min-w-30">
91
- <TextField value={tagInput()} onChange={setTagInput}>
92
- <TextField.Input ref={inputRef} onKeyDown={handleTagInput} placeholder={props.placeholder || ""} onBlur={() => {
93
- setEditing(false);
94
- setShowSuggestions(false);
95
- setTagInput("");
96
- }} class="w-full focus:outline-none"/>
97
- </TextField>
98
- <Show when={showSuggestions()}>
99
- <div class={menu({ size: props.size })}>
100
- <Show when={filteredSuggestions().length > 0} fallback={<p class="italic text-xs">{props.noSuggestionsPlaceholder ?? "No matches found"}</p>}>
101
- <ul>
102
- <For each={filteredSuggestions()}>
103
- {(s) => (<li class="cursor-pointer rounded" onMouseDown={() => handleSuggestionClick(s)}>
104
- <a>{s.name}</a>
105
- </li>)}
106
- </For>
107
- </ul>
108
- </Show>
109
- <Show when={props.dropDownAction}>{props.dropDownAction}</Show>
110
- </div>
111
- </Show>
112
- </div>)}
113
- </div>
114
- </div>);
115
- };
116
- export default TagArea;
@@ -1 +0,0 @@
1
- export * from "./TagArea";
@@ -1 +0,0 @@
1
- export * from "./TagArea";