dx4 v2
This commit is contained in:
+179
-27
@@ -1,8 +1,15 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { BiCheck, BiLeftArrowAlt, BiRightArrowAlt, BiX } from "react-icons/bi";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
BiCheck,
|
||||
BiHide,
|
||||
BiLeftArrowAlt,
|
||||
BiRightArrowAlt,
|
||||
BiShow,
|
||||
BiX,
|
||||
} from "react-icons/bi";
|
||||
import { formatDate, getDateTags } from "./dengi.js";
|
||||
import { AnimatePresence, motion, LayoutGroup } from "motion/react";
|
||||
|
||||
import trashcss from "./trash.module.css";
|
||||
export function Switch({
|
||||
checked,
|
||||
defaultChecked = false,
|
||||
@@ -67,7 +74,7 @@ export function Switch({
|
||||
);
|
||||
}
|
||||
|
||||
export function LoadingSpinner(props) {
|
||||
export function LoadingSpinner({ size }) {
|
||||
return (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@@ -75,8 +82,8 @@ export function LoadingSpinner(props) {
|
||||
viewBox="0 0 100 101"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={props.size}
|
||||
height={props.size}
|
||||
width={size}
|
||||
height={size}
|
||||
>
|
||||
<path
|
||||
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
||||
@@ -90,10 +97,14 @@ export function LoadingSpinner(props) {
|
||||
);
|
||||
}
|
||||
|
||||
function DatePickerButton({ next = false, date, setDate }) {
|
||||
function DatePickerButton({ next = false, date, setDate, pink = false }) {
|
||||
return (
|
||||
<span
|
||||
className="cursor-pointer lg:hover:*:fill-black lg:hover:bg-white transition *:transition-colors rounded-full"
|
||||
className={`cursor-pointer ${
|
||||
!pink
|
||||
? "lg:hover:*:fill-black lg:hover:bg-white"
|
||||
: "lg:hover:*:fill-white lg:hover:bg-pink-500"
|
||||
} transition *:transition-colors rounded-full`}
|
||||
onClick={() => {
|
||||
const d = new Date(date);
|
||||
d.setDate(d.getDate() + (next ? 1 : -1));
|
||||
@@ -105,17 +116,29 @@ function DatePickerButton({ next = false, date, setDate }) {
|
||||
);
|
||||
}
|
||||
|
||||
export const DatePicker = React.memo((props) => {
|
||||
export const DatePicker = (props) => {
|
||||
const dateTag = getDateTags(props.date);
|
||||
|
||||
return (
|
||||
<LayoutGroup>
|
||||
<div className="flex items-center justify-between px-2 py-1 w-full bg-grain border-zinc-800/40 border rounded-2xl relative">
|
||||
<DatePickerButton date={props.date} setDate={props.setDate} />
|
||||
<div
|
||||
className={`flex items-center justify-between px-2 py-1 w-full bg-grain ${
|
||||
!props.pink ? "border-zinc-800/40" : "border-pink-500/20"
|
||||
} border rounded-2xl relative`}
|
||||
>
|
||||
<DatePickerButton
|
||||
date={props.date}
|
||||
setDate={props.setDate}
|
||||
pink={props.pink}
|
||||
/>
|
||||
<AnimatePresence mode="popLayout">
|
||||
<motion.span
|
||||
key={formatDate(props.date)}
|
||||
className="cursor-pointer flex items-center gap-1 justify-center w-[80%] text-zinc-300 lg:hover:text-black *:transition-colors transition-colors p-1 rounded-3xl lg:hover:bg-white px-3 overflow-hidden lg:hover:*:text-black"
|
||||
className={`cursor-pointer flex items-center gap-1 justify-center w-[80%] lg:hover:text-black *:transition-colors transition-colors p-1 rounded-3xl text-zinc-300 ${
|
||||
!props.pink
|
||||
? "lg:hover:bg-white lg:hover:*:text-black"
|
||||
: "lg:hover:bg-pink-500 lg:hover:*:text-white"
|
||||
} px-3 overflow-hidden `}
|
||||
variants={{
|
||||
a: { opacity: 0, filter: "blur(1px)" },
|
||||
b: { opacity: 1, filter: "blur(0px)" },
|
||||
@@ -131,7 +154,9 @@ export const DatePicker = React.memo((props) => {
|
||||
className={
|
||||
"block min-w-2 min-h-2 rounded-full mx-1 transition-colors" +
|
||||
(props.indicator
|
||||
? " bg-zinc-300 animate-pulse"
|
||||
? props.pink
|
||||
? " bg-pink-500 animate-pulse"
|
||||
: " bg-zinc-300 animate-pulse"
|
||||
: " bg-transparent")
|
||||
}
|
||||
layoutId="datepicker_indicator"
|
||||
@@ -151,7 +176,11 @@ export const DatePicker = React.memo((props) => {
|
||||
layoutId="datepicker_date_div"
|
||||
layout="position"
|
||||
>
|
||||
<span className="h-[1px] -rotate-50 bg-zinc-800 w-[20px] " />
|
||||
<span
|
||||
className={`h-[1px] -rotate-50 ${
|
||||
props.pink ? "bg-pink-400/50" : "bg-zinc-800"
|
||||
} w-[20px] `}
|
||||
/>
|
||||
</motion.div>
|
||||
<motion.span
|
||||
initial={{ x: 10, opacity: 0 }}
|
||||
@@ -165,11 +194,16 @@ export const DatePicker = React.memo((props) => {
|
||||
)}
|
||||
</motion.span>
|
||||
</AnimatePresence>
|
||||
<DatePickerButton next date={props.date} setDate={props.setDate} />
|
||||
<DatePickerButton
|
||||
next
|
||||
date={props.date}
|
||||
setDate={props.setDate}
|
||||
pink={props.pink}
|
||||
/>
|
||||
</div>
|
||||
</LayoutGroup>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export function DengiButton({
|
||||
onClick,
|
||||
@@ -235,23 +269,24 @@ export function DengiButton({
|
||||
// );
|
||||
// }
|
||||
|
||||
const defaultDialogContent = {
|
||||
message: "?",
|
||||
yesBtn: "da",
|
||||
noBtn: "net",
|
||||
bgDissmis: true,
|
||||
yesBlocked: false,
|
||||
yesIcon: <BiCheck />,
|
||||
noIcon: <BiX />,
|
||||
noBtnStyle: "",
|
||||
yesBtnStyle: "",
|
||||
};
|
||||
export function DengiYesOrNoDialog({
|
||||
content = {
|
||||
message: "?",
|
||||
yesBtn: "da",
|
||||
noBtn: "net",
|
||||
bgDissmis: true,
|
||||
yesIcon: <BiCheck />,
|
||||
noIcon: <BiX />,
|
||||
noBtnStyle: "",
|
||||
yesBtnStyle: "",
|
||||
},
|
||||
content = defaultDialogContent,
|
||||
show,
|
||||
onAnswer,
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
{/* Background */}
|
||||
<AnimatePresence>
|
||||
{show && (
|
||||
<motion.div
|
||||
@@ -280,6 +315,7 @@ export function DengiYesOrNoDialog({
|
||||
<DengiButton
|
||||
className={content.yesBtnStyle + " w-full"}
|
||||
onClick={() => onAnswer(true)}
|
||||
disabled={content.yesBlocked}
|
||||
>
|
||||
{content.yesIcon}
|
||||
{content.yesBtn}
|
||||
@@ -299,3 +335,119 @@ export function DengiYesOrNoDialog({
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function DengiEditable({
|
||||
value,
|
||||
password = false,
|
||||
disWHidden = true,
|
||||
disabled = false,
|
||||
showLoading = true,
|
||||
minLenght = 1,
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
onSubmit = ({ value, setIdle }) => {},
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
onInput = ({ value, setIdle }) => {},
|
||||
pink = false,
|
||||
}) {
|
||||
const [val, setVal] = useState(value);
|
||||
const [hid, setHid] = useState(true);
|
||||
const [idle, setIdle] = useState(false);
|
||||
useEffect(() => {
|
||||
setVal(value);
|
||||
}, [value]);
|
||||
return (
|
||||
<form
|
||||
action="#"
|
||||
className="flex rounded-xl overflow-hidden max-w-50 h-8 relative"
|
||||
onSubmit={(e) => {
|
||||
if (idle) return;
|
||||
e.preventDefault();
|
||||
onSubmit({ value: val, setIdle });
|
||||
}}
|
||||
>
|
||||
{value !== false || !showLoading ? (
|
||||
<input
|
||||
type={hid && password ? "password" : "text"}
|
||||
value={val}
|
||||
minLength={minLenght}
|
||||
disabled={(disWHidden && password && hid) || disabled || idle}
|
||||
className={`${pink ? "bg-pink-700/20" : "bg-zinc-800"} ${
|
||||
val === value && !password ? "rounded-r-xl" : ""
|
||||
} pl-2 rounded-l-xl w-full py-1 transition`}
|
||||
onInput={(e) => {
|
||||
setVal(e.target.value);
|
||||
onInput({ value: e.target.value, setIdle });
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<span
|
||||
className={`w-full ${
|
||||
pink ? "bg-pink-700/20" : "bg-zinc-800"
|
||||
} py-1 w-full flex items-center justify-center`}
|
||||
>
|
||||
<LoadingSpinner size={25} />
|
||||
</span>
|
||||
)}
|
||||
|
||||
{password && (
|
||||
<button
|
||||
className={`${
|
||||
pink
|
||||
? "bg-pink-400 hover:bg-pink-600"
|
||||
: "bg-zinc-600 hover:bg-zinc-700 "
|
||||
} cursor-pointer transition-colors ${
|
||||
val === value ? "rounded-r-xl" : ""
|
||||
} p-1 text-2xl `}
|
||||
onContextMenu={(e) => {
|
||||
e.preventDefault();
|
||||
if ((disWHidden && password && hid) || disabled || idle) return;
|
||||
setVal(value);
|
||||
}}
|
||||
onClick={() => {
|
||||
setHid(!hid);
|
||||
}}
|
||||
>
|
||||
{hid ? <BiShow /> : <BiHide />}
|
||||
</button>
|
||||
)}
|
||||
|
||||
<AnimatePresence mode="popLayout">
|
||||
{val !== value && (
|
||||
<motion.button
|
||||
initial={{ opacity: 0, translateX: 10 }}
|
||||
animate={{ opacity: 1, translateX: 0 }}
|
||||
exit={{ opacity: 0 }}
|
||||
disabled={idle}
|
||||
className={`${
|
||||
pink
|
||||
? "bg-pink-400 disabled:bg-pink-900/50 hover:bg-pink-600"
|
||||
: "bg-zinc-600 disabled:bg-zinc-900 hover:bg-zinc-700"
|
||||
} p-1 text-2xl rounded-r-xl transition-colors not-disabled:cursor-pointer`}
|
||||
onContextMenu={(e) => {
|
||||
e.preventDefault();
|
||||
setVal(value);
|
||||
}}
|
||||
>
|
||||
{idle ? <LoadingSpinner size={24} /> : <BiCheck />}
|
||||
</motion.button>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export function TextPreloader({ width = "5rem" }) {
|
||||
return (
|
||||
<span
|
||||
className={"h-4 block bg-zinc-900 overflow-hidden rounded-full w-(--w)"}
|
||||
style={{ "--w": width }}
|
||||
>
|
||||
<span
|
||||
className={
|
||||
"block h-10 -translate-y-3 w-3 bg-zinc-300 relative blur-[7px] " +
|
||||
trashcss.anim_preloader
|
||||
}
|
||||
></span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user