This commit is contained in:
rizky 2024-08-12 01:30:01 -07:00
parent 74e2762abd
commit 362cfdff27
12 changed files with 186 additions and 71 deletions

View File

@ -134,8 +134,12 @@ export const FieldInput: FC<{
<>
{prefix && prefix !== "" ? (
<div
className="
c-px-2 c-bg-gray-200 c-flex c-flex-row c-items-center"
className={cx(
"c-px-2 c-flex c-flex-row c-items-center",
css`
color: gray;
`
)}
>
{prefix}
</div>
@ -194,8 +198,12 @@ export const FieldInput: FC<{
</div>
{suffix && suffix !== "" ? (
<div
className="
c-px-2 c-bg-gray-200 c-flex c-flex-row c-items-center"
className={cx(
"c-px-2 c-flex c-flex-row c-items-center",
css`
color: gray;
`
)}
>
{suffix}
</div>

View File

@ -1,3 +1,4 @@
import { useLocal } from "lib/utils/use-local";
import { ExternalLink } from "lucide-react";
import { ReactElement } from "react";
@ -8,6 +9,25 @@ export const ThumbPreview = ({
url: string;
options: ReactElement;
}) => {
const local = useLocal({ size: "", is_doc: true }, async () => {
if (url.startsWith("_file/")) {
let _url = siteurl(`/_finfo/${url.substring("_file/".length)}`);
if (
location.hostname === "prasi.avolut.com" ||
location.host === "localhost:4550"
) {
const newurl = new URL(location.href);
newurl.pathname = `/_proxy/${_url}`;
_url = newurl.toString();
}
const info = await fetch(_url);
local.size = (await info.json())?.size;
local.render();
}
});
const file = getFileName(url);
if (typeof file === "string") return;
@ -34,14 +54,23 @@ export const ThumbPreview = ({
outline: 1px solid #1c4ed8;
}
`,
"c-flex c-justify-center c-items-center"
"c-flex c-justify-center c-items-center c-flex-col"
)}
onClick={() => {
let _url = siteurl(url || "");
window.open(_url, "_blank");
}}
>
{file.extension}
<div>{file.extension}</div>
<div
className={css`
font-size: 9px;
color: gray;
margin-top: -3px;
`}
>
{local.size}
</div>
</div>
);
@ -49,6 +78,7 @@ export const ThumbPreview = ({
if (url.startsWith("_file/")) {
if ([".png", ".jpeg", ".jpg", ".webp"].find((e) => url.endsWith(e))) {
is_image = true;
local.is_doc = false;
content = (
<img
onClick={() => {

View File

@ -3,7 +3,6 @@ import { FieldLoading } from "lib/comps/ui/field-loading";
import { Typeahead } from "lib/comps/ui/typeahead";
import { FC, useEffect } from "react";
import { FMLocal, FieldLocal, FieldProp } from "../../typings";
import { call_prasi_events } from "lib/exports";
export const TypeDropdown: FC<{
field: FieldLocal;
@ -35,23 +34,27 @@ export const TypeDropdown: FC<{
data: e.data,
};
});
let v = typeof arg.opt_get_value === "function"
? arg.opt_get_value({
fm,
name: field.name,
options: local.options,
type: field.type,
})
: fm.data[field.name];
let f = list.find((ex) => ex.value === v);
if(!f){
arg.opt_set_value({
fm,
name: field.name,
type: field.type,
options: local.options,
selected: [],
});
let v =
typeof arg.opt_get_value === "function"
? arg.opt_get_value({
fm,
name: field.name,
options: local.options,
type: field.type,
})
: fm.data[field.name];
if (field.type === "single-option") {
let f = list.find((ex: any) => ex.value === v);
if (!f) {
arg.opt_set_value({
fm,
name: field.name,
type: field.type,
options: local.options,
selected: [],
});
}
}
local.options = list;
} else {
@ -140,8 +143,8 @@ export const TypeDropdown: FC<{
popupClassName = cx(
css`
.opt-item {
padding-top: 0px;
padding-bottom: 0px;
padding-top: 4px;
padding-bottom: 4px;
line-height: 15px;
font-size: 12px;
border: 0px;

View File

@ -11,38 +11,30 @@ export const FieldRichText: FC<{
prop: PropTypeInput;
}> = ({ field, fm, prop }) => {
const local = useLocal({
ref: null as any,
ref: null as null | HTMLDivElement,
q: null as null | Quill,
});
useEffect(() => {
if (local.ref) {
const q = new Quill(local.ref, {
local.ref.innerHTML = fm.data[field.name] || "";
local.q = new Quill(local.ref, {
theme: "snow",
modules: {
toolbar: [
["bold", "italic", "underline", "strike"], // toggled buttons
["blockquote", "code-block"],
["link", "image", "video", "formula"],
[{ header: 1 }, { header: 2 }], // custom button values
[{ list: "ordered" }, { list: "bullet" }, { list: "check" }],
[{ script: "sub" }, { script: "super" }], // superscript/subscript
[{ indent: "-1" }, { indent: "+1" }], // outdent/indent
[{ direction: "rtl" }], // text direction
[{ size: ["small", false, "large", "huge"] }], // custom dropdown
[{ header: [1, 2, 3, 4, 5, 6, false] }],
[{ color: [] }, { background: [] }], // dropdown with defaults from theme
[{ font: [] }],
[{ align: [] }],
["clean"], // remove formatting button
],
},
});
local.q.on("text-change", (delta, oldDelta, source) => {
fm.data[field.name] = local.q?.getSemanticHTML();
fm.render();
});
}
}, []);
let value: any = fm.data[field.name];
return (
<div
className={cx(
@ -55,13 +47,22 @@ export const FieldRichText: FC<{
.ql-container {
border-top: 1px solid #cecece !important;
}
.ql-editor {
resize: vertical;
overflow-y: scroll;
min-height: 5rem !important;
}
`
)}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<div
ref={(e) => (local.ref = e)}
className={cx(css`
height: 20rem !important;
min-height: 5rem !important;
`)}
></div>
</div>

View File

@ -16,9 +16,8 @@ export const FieldUploadMulti: FC<{
arg: FieldProp;
on_change: (e: any) => void | Promise<void>;
}> = ({ field, fm, prop, on_change, arg }) => {
let value: string = (fm.data[field.name] || "").trim();
const input = useLocal({
value: 0 as any,
display: false as any,
@ -136,6 +135,9 @@ export const FieldUploadMulti: FC<{
.upload-star {
border: 1px solid gray;
}
.btn-del {
border: 1px solid red;
}
}
`,
fm.data[cover.field] === value &&
@ -188,9 +190,8 @@ export const FieldUploadMulti: FC<{
}
}}
className={cx(
"c-flex c-flex-row c-items-center c-px-1 c-rounded c-bg-white c-cursor-pointer hover:c-bg-red-100 transition-all",
"c-flex c-flex-row c-items-center c-px-1 c-rounded c-bg-white c-cursor-pointer hover:c-bg-red-100 transition-all btn-del",
css`
border: 1px solid red;
width: 25px;
height: 25px;
`
@ -258,11 +259,21 @@ export const FieldUploadMulti: FC<{
</div>
)}
</div>
<div className="c-flex">
<div className={cx("c-flex c-border c-rounded ")}>
<div className="c-flex c-pt-1">
<div
className={cx(
"c-flex c-border c-rounded c-cursor-pointer hover:c-bg-blue-50",
css`
&:hover {
border: 1px solid #1c4ed8;
outline: 1px solid #1c4ed8;
}
`
)}
>
<div
className={cx(
"c-flex c-flex-row c-relative c-flex-grow c-pr-2 c-items-center c-cursor-pointer hover:c-bg-blue-50",
"c-flex c-flex-row c-relative c-flex-grow c-pr-2 c-items-center ",
css`
padding-top: 3px;
padding-bottom: 2px;
@ -289,7 +300,11 @@ export const FieldUploadMulti: FC<{
)}
/>
)}
<div className="c-items-center c-flex c-text-base c-px-1 c-outline-none c-rounded c-cursor-pointer">
<div
className={cx(
"c-items-center c-flex c-text-base c-px-1 c-outline-none c-rounded c-cursor-pointer"
)}
>
<div className="c-flex c-flex-row c-items-center c-px-2">
<Upload className="c-h-4 c-w-4" />
</div>

View File

@ -150,14 +150,16 @@ ${
fm.data = form;
md.selected = form;
if (md.props.mode !== "full") md.master.reload({ toast: false });
md.render();
fm.render();
if (fm.props.back_on_save === "y") {
md.selected = null;
md.tab.active = "master";
md.params.apply();
md.render();
} else {
md.params.apply();
md.render();
fm.render();
}
}`
}

View File

@ -1,13 +1,12 @@
import { parseGenField } from "@/gen/utils";
import { MDLocal } from "lib/comps/md/utils/typings";
import { Button } from "lib/comps/ui/button";
import { toast } from "lib/comps/ui/toast";
import get from "lodash.get";
import { AlertTriangle, Check, ChevronLeft, Loader2, Plus } from "lucide-react";
import { FMLocal, FMProps } from "../typings";
import { editorFormData } from "./ed-data";
import { formError } from "./error";
import { toast } from "lib/comps/ui/toast";
import { Button } from "lib/comps/ui/button";
import { MDLocal } from "lib/comps/md/utils/typings";
import { masterDetailApplyParams } from "lib/comps/md/utils/md-hash";
export const formInit = (fm: FMLocal, props: FMProps) => {
for (const [k, v] of Object.entries(props)) {

View File

@ -131,7 +131,8 @@ export const MasterDetail: FC<MDProps> = (arg) => {
return (
<div
className={cx(
"master-detail c-flex-1 c-flex-col c-flex c-w-full c-h-full"
"master-detail c-flex-1 c-flex-col c-flex c-w-full c-h-full",
md.selected ? "mode-detail" : "mode-master"
)}
>
{md.props.show_head === "always" && <MDHeader md={md} mdr={mdr} />}

View File

@ -4,7 +4,7 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/utils"
const badgeVariants = cva(
"c-inline-flex c-items-center c-rounded-full c-border c-px-2.5 c-py-0.5 c-text-xs c-font-semibold c-transition-colors focus:c-outline-none focus:c-ring-2 focus:c-ring-ring focus:c-ring-offset-2",
"c-inline-flex c-items-center c-rounded-full c-border c-px-2.5 c-py-0.5 c-font-semibold c-transition-colors focus:c-outline-none focus:c-ring-2 focus:c-ring-ring focus:c-ring-offset-2",
{
variants: {
variant: {

View File

@ -82,7 +82,7 @@ export const TypeaheadOptions: FC<{
})}
{searching ? (
<div className="c-px-4 c-w-full c-text-xs c-text-slate-400">
<div className="c-px-4 c-w-full c-text-slate-400">
Loading...
</div>
) : (

View File

@ -337,18 +337,33 @@ export const Typeahead: FC<{
<div
className={cx(
local.mode === "single" ? "c-cursor-pointer" : "c-cursor-text",
"c-flex c-relative c-flex-wrap c-px-2 c-pb-0 c-items-center c-w-full c-h-full c-flex-1",
css`
padding-top: 0.35rem;
`,
className
"c-flex c-relative c-flex-wrap c-px-2 c-py-0 c-items-center c-w-full c-h-full c-flex-1",
className,
local.mode === "multi" && valueLabel.length > 0
? css`
input {
margin-top: 5px;
}
`
: css`
input {
margin-top: 5px;
}
`
)}
onClick={() => {
if (!disabled) input.current?.focus();
}}
>
{local.mode === "multi" ? (
<>
<div
className={cx(
css`
margin-top: 5px;
margin-bottom: -3px;
`
)}
>
{valueLabel.map((e, idx) => {
return (
<Badge
@ -356,7 +371,8 @@ export const Typeahead: FC<{
variant={"outline"}
className={cx(
"c-space-x-1 c-mr-2 c-mb-2 c-bg-white",
!disabled && " c-cursor-pointer hover:c-bg-red-100"
!disabled &&
" c-cursor-pointer hover:c-bg-red-100 hover:c-border-red-100"
)}
onClick={(ev) => {
if (!disabled) {
@ -377,7 +393,7 @@ export const Typeahead: FC<{
</Badge>
);
})}
</>
</div>
) : (
<></>
)}
@ -531,7 +547,7 @@ export const Typeahead: FC<{
disabled={disabled}
spellCheck={false}
className={cx(
"c-flex-1 c-mb-2 c-text-sm c-outline-none c-bg-transparent",
"c-flex-1 c-mb-2 c-outline-none c-bg-transparent",
local.mode === "single" ? "c-cursor-pointer" : ""
)}
onKeyDown={keydown}

View File

@ -130,6 +130,46 @@ export const FormatValue: FC<{
return <FilePreview url={value || ""} />;
}
if (name.startsWith("desc")) {
return (
<div className="c-flex c-space-x-2 c-items-center">
<div dangerouslySetInnerHTML={{ __html: value }} />
</div>
);
}
if (typeof value === "string" && value.startsWith("_file/")) {
return (
<img
onClick={() => {
let _url = siteurl(value || "");
window.open(_url, "_blank");
}}
className={cx(
"c-rounded-md",
css`
&:hover {
outline: 2px solid #1c4ed8;
}
`,
css`
width: 25px;
height: 25px;
background-image: linear-gradient(45deg, #ccc 25%, transparent 25%),
linear-gradient(135deg, #ccc 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #ccc 75%),
linear-gradient(135deg, transparent 75%, #ccc 75%);
background-size: 25px 25px; /* Must be a square */
background-position: 0 0, 12.5px 0, 12.5px -12.5px, 0px 12.5px; /* Must be half of one side of the square */
`
)}
src={siteurl(
`/_img/${value.substring("_file/".length)}?${"w=25&h=25&fit=cover"}`
)}
/>
);
}
return (
<div className="c-flex c-space-x-2 c-items-center">
<div>{value}</div>