text-editor-studio-ts
Version:
A powerful mobile-responsive rich text editor built with Lexical and React
202 lines (201 loc) • 7.48 kB
JavaScript
import { jsx, jsxs } from "react/jsx-runtime";
import { useMemo, useState, useRef, useCallback, useEffect } from "react";
import { l } from "./LexicalCollaborationContext.prod-_XIRbBSl.js";
import { o, u, q as $isPollNode, B as Button, s as createPollOption, I as Input } from "./index-BwW17RmP.js";
import { mergeRegister } from "@lexical/utils";
import { $getSelection, $isNodeSelection, CLICK_COMMAND, COMMAND_PRIORITY_LOW, KEY_DELETE_COMMAND, KEY_BACKSPACE_COMMAND, $getNodeByKey } from "lexical";
function getTotalVotes(options) {
return options.reduce((totalVotes, next) => {
return totalVotes + next.votes.length;
}, 0);
}
function PollOptionComponent({
option,
index,
options,
totalVotes,
withPollNode
}) {
const { clientID } = l();
const checkboxRef = useRef(null);
const votesArray = option.votes;
const checkedIndex = votesArray.indexOf(clientID);
const checked = checkedIndex !== -1;
const votes = votesArray.length;
const text = option.text;
return /* @__PURE__ */ jsxs("div", { className: "mb-2.5 flex flex-row items-center", children: [
/* @__PURE__ */ jsx(
"div",
{
className: `relative mr-2.5 flex h-[22px] w-[22px] rounded-md border border-border-system-global-secondary ${checked ? 'border-border-presentation-action-primary bg-background-presentation-action-primary after:pointer-events-none after:absolute after:left-2 after:top-1 after:m-0 after:block after:h-[9px] after:w-[5px] after:rotate-45 after:cursor-pointer after:border-b-2 after:border-r-2 after:border-solid after:border-white after:content-[""]' : ""}`,
children: /* @__PURE__ */ jsx(
"input",
{
ref: checkboxRef,
className: "absolute block h-full w-full cursor-pointer border-0 opacity-0",
type: "checkbox",
onChange: () => {
withPollNode((node) => {
node.toggleVote(option, clientID);
});
},
checked
}
)
}
),
/* @__PURE__ */ jsxs("div", { className: "relative flex flex-[10px] cursor-pointer overflow-hidden rounded-md border border-border-presentation-action-primary", children: [
/* @__PURE__ */ jsx(
"div",
{
className: "transition-width absolute left-0 top-0 z-0 h-full bg-background-presentation-state-success-primary duration-1000 ease-in-out",
style: { width: `${votes === 0 ? 0 : votes / totalVotes * 100}%` }
}
),
/* @__PURE__ */ jsx("span", { className: "absolute right-4 top-1.5 text-xs text-content-presentation-action-primary", children: votes > 0 && (votes === 1 ? "1 vote" : `${votes} votes`) }),
/* @__PURE__ */ jsx(
Input,
{
type: "text",
value: text,
onChange: (e) => {
const target = e.target;
const value = target.value;
const selectionStart = target.selectionStart;
const selectionEnd = target.selectionEnd;
withPollNode(
(node) => {
node.setOptionText(option, value);
},
() => {
target.selectionStart = selectionStart;
target.selectionEnd = selectionEnd;
}
);
},
placeholder: `Option ${index + 1}`
}
)
] }),
/* @__PURE__ */ jsx(
"button",
{
disabled: options.length < 3,
className: `relative z-0 ml-1.5 flex h-7 w-7 cursor-pointer rounded-md border-0 bg-transparent bg-[position:6px_6px] bg-no-repeat opacity-30 before:absolute before:left-[13px] before:top-1.5 before:block before:h-[15px] before:w-0.5 before:-rotate-45 before:bg-border-system-global-secondary before:content-[''] after:absolute after:left-[13px] after:top-1.5 after:block after:h-[15px] after:w-0.5 after:rotate-45 after:bg-border-system-global-secondary after:content-[''] hover:bg-background-system-body-primary-presentation-action-hover hover:opacity-100 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:opacity-30`,
"aria-label": "Remove",
onClick: () => {
withPollNode((node) => {
node.deleteOption(option);
});
}
}
)
] });
}
function PollComponent({
question,
options,
nodeKey
}) {
const [editor] = o();
const totalVotes = useMemo(() => getTotalVotes(options), [options]);
const [isSelected, setSelected, clearSelection] = u(nodeKey);
const [selection, setSelection] = useState(null);
const ref = useRef(null);
const $onDelete = useCallback(
(payload) => {
const deleteSelection = $getSelection();
if (isSelected && $isNodeSelection(deleteSelection)) {
const event = payload;
event.preventDefault();
editor.update(() => {
deleteSelection.getNodes().forEach((node) => {
if ($isPollNode(node)) {
node.remove();
}
});
});
}
return false;
},
[editor, isSelected]
);
useEffect(() => {
return mergeRegister(
editor.registerUpdateListener(({ editorState }) => {
setSelection(editorState.read(() => $getSelection()));
}),
editor.registerCommand(
CLICK_COMMAND,
(payload) => {
const event = payload;
if (event.target === ref.current) {
if (!event.shiftKey) {
clearSelection();
}
setSelected(!isSelected);
return true;
}
return false;
},
COMMAND_PRIORITY_LOW
),
editor.registerCommand(
KEY_DELETE_COMMAND,
$onDelete,
COMMAND_PRIORITY_LOW
),
editor.registerCommand(
KEY_BACKSPACE_COMMAND,
$onDelete,
COMMAND_PRIORITY_LOW
)
);
}, [clearSelection, editor, isSelected, nodeKey, $onDelete, setSelected]);
const withPollNode = (cb, onUpdate) => {
editor.update(
() => {
const node = $getNodeByKey(nodeKey);
if ($isPollNode(node)) {
cb(node);
}
},
{ onUpdate }
);
};
const addOption = () => {
withPollNode((node) => {
node.addOption(createPollOption());
});
};
const isFocused = $isNodeSelection(selection) && isSelected;
return /* @__PURE__ */ jsx(
"div",
{
className: `min-w-[400px] max-w-[600px] cursor-pointer select-none rounded-lg border border-border-system-global-secondary bg-background-system-body-primary ${isFocused ? " outline-2 outline-primary" : ""}`,
ref,
children: /* @__PURE__ */ jsxs("div", { className: "m-4 cursor-default", children: [
/* @__PURE__ */ jsx("h2", { className: "m-0 mb-4 text-center text-lg text-content-system-global-secondary", children: question }),
options.map((option, index) => {
const key = option.uid;
return /* @__PURE__ */ jsx(
PollOptionComponent,
{
withPollNode,
option,
index,
options,
totalVotes
},
key
);
}),
/* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(Button, { onClick: addOption, size: "M", children: "Add Option" }) })
] })
}
);
}
export {
PollComponent as default
};
//# sourceMappingURL=poll-component-DwrVM5FN.js.map