adding key value type
This commit is contained in:
parent
c3fdb700b2
commit
0342d40082
|
|
@ -309,7 +309,7 @@ export const ImgThumb = ({
|
||||||
"img-thumb",
|
"img-thumb",
|
||||||
className,
|
className,
|
||||||
css`
|
css`
|
||||||
width: ${w}px;
|
width: ${w}px;
|
||||||
height: ${h}px;
|
height: ${h}px;
|
||||||
background-image: linear-gradient(45deg, #ccc 25%, transparent 25%),
|
background-image: linear-gradient(45deg, #ccc 25%, transparent 25%),
|
||||||
linear-gradient(135deg, #ccc 25%, transparent 25%),
|
linear-gradient(135deg, #ccc 25%, transparent 25%),
|
||||||
|
|
@ -320,7 +320,7 @@ export const ImgThumb = ({
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{!local.error && (
|
{!local.error && url && (
|
||||||
<img
|
<img
|
||||||
onError={() => {
|
onError={() => {
|
||||||
local.error = true;
|
local.error = true;
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,204 @@
|
||||||
export const KeyValue = () => {
|
import { update } from "autosize";
|
||||||
|
import { useLocal } from "lib/utils/use-local";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
export const KeyValue = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
value: any;
|
||||||
|
onChange: (val: any) => void;
|
||||||
|
}) => {
|
||||||
|
const local = useLocal({
|
||||||
|
entries: Object.entries(value),
|
||||||
|
new: { idx: -1, key: "", value: "" },
|
||||||
|
});
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (local.entries.length > 0) {
|
||||||
|
Object.entries(value).forEach(([k, v], idx) => {
|
||||||
|
const found = local.entries.find((e) => {
|
||||||
|
if (e[0] === k) return true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (found) {
|
||||||
|
found[0] = k;
|
||||||
|
found[1] = v;
|
||||||
|
} else {
|
||||||
|
local.entries.push([k, v]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
local.entries = Object.entries(value);
|
||||||
|
}
|
||||||
|
local.render();
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
if (typeof value !== "object") return null;
|
||||||
|
|
||||||
|
const reverseEntries = (input: [string, unknown][]) => {
|
||||||
|
return input
|
||||||
|
.filter(([k, v]) => {
|
||||||
|
return true; // some irrelevant conditions here
|
||||||
|
})
|
||||||
|
.reduce((accum: any, [k, v]) => {
|
||||||
|
accum[k] = v;
|
||||||
|
return accum;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="c-flex c-relative c-flex-1">
|
<div className="c-flex c-relative c-flex-1" ref={ref}>
|
||||||
<table className="c-flex-1">
|
<table
|
||||||
|
className={cx(
|
||||||
|
"c-flex-1",
|
||||||
|
css`
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
outline: none;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
&:focus {
|
||||||
|
border: 1px solid #1c4ed8;
|
||||||
|
outline: 1px solid #1c4ed8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
{local.entries.map((item, idx) => (
|
||||||
<td>
|
<KVRow
|
||||||
<input type="text"></input>
|
key={idx}
|
||||||
</td>
|
item={item}
|
||||||
<td>
|
idx={idx}
|
||||||
<input type="text"></input>
|
update={(idx, k, v) => {
|
||||||
</td>
|
local.entries[idx] = [k, v];
|
||||||
</tr>
|
|
||||||
<tr>
|
if (k === "" && v === "") {
|
||||||
<td>
|
local.entries.splice(idx, 1);
|
||||||
<input type="text"></input>
|
}
|
||||||
</td>
|
|
||||||
<td>
|
local.render();
|
||||||
<input type="text"></input>
|
}}
|
||||||
</td>
|
onBlur={() => {
|
||||||
</tr>
|
onChange(reverseEntries(local.entries));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<KVRow
|
||||||
|
item={[local.new.key, local.new.value]}
|
||||||
|
idx={local.entries.length}
|
||||||
|
update={(idx, k, v) => {
|
||||||
|
local.new.key = k;
|
||||||
|
local.new.value = v;
|
||||||
|
local.render();
|
||||||
|
}}
|
||||||
|
onBlur={(field, val) => {
|
||||||
|
if (field === "key" && val) {
|
||||||
|
const idx = local.entries.length;
|
||||||
|
local.entries[idx] = [local.new.key, local.new.value];
|
||||||
|
local.new.key = "";
|
||||||
|
local.new.value = "";
|
||||||
|
local.render();
|
||||||
|
onChange(reverseEntries(local.entries));
|
||||||
|
setTimeout(() => {
|
||||||
|
(
|
||||||
|
ref?.current?.querySelector(
|
||||||
|
`.kv-row-${idx} .kv-value input`
|
||||||
|
) as HTMLInputElement
|
||||||
|
)?.focus();
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const KVRow = ({
|
||||||
|
item,
|
||||||
|
idx,
|
||||||
|
update,
|
||||||
|
onBlur,
|
||||||
|
}: {
|
||||||
|
item: [string, unknown];
|
||||||
|
idx: number;
|
||||||
|
update: (idx: number, key: string, value: string) => void;
|
||||||
|
onBlur?: (field: "key" | "value", val: string) => void;
|
||||||
|
}) => {
|
||||||
|
const [k, v] = item as any;
|
||||||
|
const keyref = useRef<HTMLInputElement>(null);
|
||||||
|
const valref = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tr className={`kv-row-${idx}`}>
|
||||||
|
<td
|
||||||
|
className={cx(
|
||||||
|
"kv-key",
|
||||||
|
css`
|
||||||
|
border-right: 1px solid #ececeb;
|
||||||
|
width: 30%;
|
||||||
|
`,
|
||||||
|
idx > 0 &&
|
||||||
|
css`
|
||||||
|
border-top: 1px solid #ececeb;
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
tabIndex={0}
|
||||||
|
type="text"
|
||||||
|
spellCheck={false}
|
||||||
|
value={k}
|
||||||
|
onChange={(e) => {
|
||||||
|
update(idx, e.currentTarget.value, v || "");
|
||||||
|
}}
|
||||||
|
onBlur={
|
||||||
|
onBlur
|
||||||
|
? (e) => {
|
||||||
|
onBlur("key", e.currentTarget.value);
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
ref={keyref}
|
||||||
|
></input>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
className={cx(
|
||||||
|
"kv-value",
|
||||||
|
idx > 0 &&
|
||||||
|
css`
|
||||||
|
border-top: 1px solid #ececeb;
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
tabIndex={0}
|
||||||
|
type="text"
|
||||||
|
spellCheck={false}
|
||||||
|
value={v || ""}
|
||||||
|
onChange={(e) => {
|
||||||
|
update(idx, k, e.currentTarget.value || "");
|
||||||
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Backspace" && !e.currentTarget.value) {
|
||||||
|
keyref.current?.focus();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onBlur={
|
||||||
|
onBlur
|
||||||
|
? (e) => {
|
||||||
|
onBlur("value", e.currentTarget.value);
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
ref={valref}
|
||||||
|
></input>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,19 @@ export const FieldTypeInput: FC<{
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case "key-value":
|
case "key-value":
|
||||||
return <KeyValue />;
|
return (
|
||||||
|
<KeyValue
|
||||||
|
value={
|
||||||
|
Object.keys(fm.data[field.name] || {}).length === 0
|
||||||
|
? field.prop.kv?.default
|
||||||
|
: fm.data[field.name] || {}
|
||||||
|
}
|
||||||
|
onChange={(val) => {
|
||||||
|
fm.data[field.name] = val;
|
||||||
|
fm.render();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
case "monthly": {
|
case "monthly": {
|
||||||
return (
|
return (
|
||||||
<Datepicker
|
<Datepicker
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ export type FieldProp = {
|
||||||
label: string;
|
label: string;
|
||||||
desc?: string;
|
desc?: string;
|
||||||
props?: any;
|
props?: any;
|
||||||
|
kv?: { default: any };
|
||||||
upload?: {
|
upload?: {
|
||||||
mode: "single-file" | "multi-file";
|
mode: "single-file" | "multi-file";
|
||||||
accept: string;
|
accept: string;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue