chatbot-editor 1.0.0 → 1.0.2
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.
- package/dist/{App.d.ts → ChatbotEditor.d.ts} +1 -1
- package/dist/{App.js → ChatbotEditor.js} +3 -3
- package/dist/ChatbotEditor.js.map +1 -0
- package/dist/Components/SideBar.js +1 -1
- package/dist/Components/SideBar.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/main.js +2 -2
- package/dist/main.js.map +1 -1
- package/index.css +2 -0
- package/package.json +5 -1
- package/.github/workflows/npm-publish.yml +0 -21
- package/dist/App.js.map +0 -1
- package/eslint.config.js +0 -26
- package/index.html +0 -12
- package/src/App.tsx +0 -43
- package/src/Canva.tsx +0 -23
- package/src/Components/Answers.tsx +0 -27
- package/src/Components/Arrow.tsx +0 -60
- package/src/Components/Bubble.tsx +0 -13
- package/src/Components/Button.tsx +0 -14
- package/src/Components/Input.tsx +0 -25
- package/src/Components/Question.tsx +0 -104
- package/src/Components/Select.tsx +0 -45
- package/src/Components/SideBar.tsx +0 -31
- package/src/CurrentQuestionHook.tsx +0 -22
- package/src/TQuestion.ts +0 -23
- package/src/UI.tsx +0 -138
- package/src/index.ts +0 -7
- package/src/main.tsx +0 -114
- package/src/utils.ts +0 -3
- package/tsconfig.app.json +0 -28
- package/tsconfig.json +0 -16
- package/tsconfig.node.json +0 -26
- package/vite.config.ts +0 -8
|
@@ -3,11 +3,11 @@ import Canva from "./Canva";
|
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { CurrentQuestionProvider } from "./CurrentQuestionHook";
|
|
5
5
|
import React from "react";
|
|
6
|
-
export default function
|
|
6
|
+
export default function ChatbotEditor(props) {
|
|
7
7
|
const [questions, setQuestions] = useState([]);
|
|
8
8
|
const [reloadArrow, setReloadArrow] = useState(false);
|
|
9
9
|
return (React.createElement(CurrentQuestionProvider, null,
|
|
10
|
-
React.createElement("div", { className: "relative h-
|
|
10
|
+
React.createElement("div", { className: "relative h-full w-full overflow-hidden" },
|
|
11
11
|
React.createElement(Canva, { questions: questions, setQuestions: setQuestions, reloadArrow: reloadArrow, setReloadArrow: setReloadArrow }),
|
|
12
12
|
React.createElement(UI, { setReloadArrow: setReloadArrow, questions: questions, addQuestion: (initPos) => {
|
|
13
13
|
let idQuestion = "question";
|
|
@@ -19,4 +19,4 @@ export default function App(props) {
|
|
|
19
19
|
setQuestions(prev => [...prev, { id: idQuestion, text: "Text", position: initPos }]);
|
|
20
20
|
}, editQuestion: (id, data) => setQuestions(prev => prev.map(v => v.id === id ? data : v)), deleteQuestion: id => setQuestions(prev => prev.filter(v => v.id !== id)), setQuestions: setQuestions, save: props.save, load: props.load, export: props.export, import: props.import }))));
|
|
21
21
|
}
|
|
22
|
-
//# sourceMappingURL=
|
|
22
|
+
//# sourceMappingURL=ChatbotEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatbotEditor.js","sourceRoot":"","sources":["../src/ChatbotEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,MAAM,CAAC;AACtB,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,OAAO,EAAC,QAAQ,EAAC,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAC,uBAAuB,EAAC,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,MAAM,OAAO,CAAC;AAS1B,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,KAAe;IACpD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAc,EAAE,CAAC,CAAC;IAC5D,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACtD,OAAO,CACN,oBAAC,uBAAuB;QACvB,6BAAK,SAAS,EAAE,wCAAwC;YACvD,oBAAC,KAAK,IAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,cAAc,GAAG;YACpH,oBAAC,EAAE,IAAC,cAAc,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,OAA+B,EAAE,EAAE;oBAC1G,IAAI,UAAU,GAAG,UAAU,CAAC;oBAC5B,IAAI,CAAC,GAAG,CAAC,CAAC;oBACV,OAAM,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,UAAU,CAAC,EAAE,CAAC;wBAChD,CAAC,EAAE,CAAC;wBACJ,UAAU,GAAG,WAAW,CAAC,EAAE,CAAC;oBAC7B,CAAC;oBACD,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAC,CAAC,CAAC,CAAC;gBACpF,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAC7B,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAE3D,cAAc,EAAE,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,EACzE,YAAY,EAAE,YAAY,EAC1B,IAAI,EAAE,KAAK,CAAC,IAAI,EAChB,IAAI,EAAE,KAAK,CAAC,IAAI,EAChB,MAAM,EAAE,KAAK,CAAC,MAAM,EACpB,MAAM,EAAE,KAAK,CAAC,MAAM,GACnB,CACG,CACmB,CAC1B,CAAA;AACF,CAAC"}
|
|
@@ -2,7 +2,7 @@ import { clsx } from "clsx";
|
|
|
2
2
|
import { BsCheck, BsX } from "react-icons/bs";
|
|
3
3
|
import React from "react";
|
|
4
4
|
export default function SideBar(props) {
|
|
5
|
-
return (React.createElement("div", { className: clsx("fixed transition w-1/3 h-
|
|
5
|
+
return (React.createElement("div", { className: clsx("fixed transition w-1/3 h-full top-0 left-0 bg-stone-900 z-50 flex flex-col", props.display && "translate-x-0" || "-translate-x-full") },
|
|
6
6
|
React.createElement("div", { className: "flex justify-end" },
|
|
7
7
|
React.createElement("button", { className: "mx-2 my-1 p-1 rounded cursor-pointer border border-stone-200/30 aspect-square w-8", onClick: () => {
|
|
8
8
|
props.setDisplay(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SideBar.js","sourceRoot":"","sources":["../../src/Components/SideBar.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAC,IAAI,EAAC,MAAM,MAAM,CAAC;AAC1B,OAAO,EAAC,OAAO,EAAE,GAAG,EAAC,MAAM,gBAAgB,CAAC;AAC5C,OAAO,KAAK,MAAM,OAAO,CAAC;AAU1B,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,KAAmB;IAClD,OAAO,CACN,6BAAK,SAAS,EAAE,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"SideBar.js","sourceRoot":"","sources":["../../src/Components/SideBar.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAC,IAAI,EAAC,MAAM,MAAM,CAAC;AAC1B,OAAO,EAAC,OAAO,EAAE,GAAG,EAAC,MAAM,gBAAgB,CAAC;AAC5C,OAAO,KAAK,MAAM,OAAO,CAAC;AAU1B,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,KAAmB;IAClD,OAAO,CACN,6BAAK,SAAS,EAAE,IAAI,CAAC,4EAA4E,EAAE,KAAK,CAAC,OAAO,IAAI,eAAe,IAAI,mBAAmB,CAAC;QAC1J,6BAAK,SAAS,EAAE,kBAAkB;YACjC,gCAAQ,SAAS,EAAE,mFAAmF,EAAE,OAAO,EAAE,GAAG,EAAE;oBACrH,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;oBACxB,IAAG,KAAK,CAAC,OAAO,EAAE,CAAC;wBAClB,KAAK,CAAC,OAAO,EAAE,CAAC;oBAClB,CAAC;gBAAA,CAAC;gBACD,oBAAC,GAAG,IAAC,SAAS,EAAE,SAAS,GAAG,CACpB;YACR,KAAK,CAAC,UAAU,IAAI,gCAAQ,SAAS,EAAE,mFAAmF,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,UAAU,EAAE;gBAAE,oBAAC,OAAO,IAAC,SAAS,EAAE,SAAS,GAAG,CAAS,CACxN;QACN,6BAAK,SAAS,EAAE,0BAA0B,IAAG,KAAK,CAAC,QAAQ,CAAO,CAC7D,CACN,CAAA;AACF,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ChatbotEditor from "./ChatbotEditor";
|
|
2
2
|
import type TQuestion from "./TQuestion.ts";
|
|
3
|
-
export {
|
|
3
|
+
export { ChatbotEditor, type TQuestion };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
export {
|
|
1
|
+
import ChatbotEditor from "./ChatbotEditor";
|
|
2
|
+
export { ChatbotEditor };
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAG5C,OAAO,EACN,aAAa,EAEb,CAAA"}
|
package/dist/main.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { StrictMode } from 'react';
|
|
2
2
|
import { createRoot } from 'react-dom/client';
|
|
3
3
|
import './index.css';
|
|
4
|
-
import
|
|
4
|
+
import ChatbotEditor from "./ChatbotEditor";
|
|
5
5
|
import React from "react";
|
|
6
6
|
const save = (questions) => {
|
|
7
7
|
const save = questions.map(q => {
|
|
@@ -104,5 +104,5 @@ const importFile = async () => {
|
|
|
104
104
|
return p;
|
|
105
105
|
};
|
|
106
106
|
createRoot(document.getElementById('root')).render(React.createElement(StrictMode, null,
|
|
107
|
-
React.createElement(
|
|
107
|
+
React.createElement(ChatbotEditor, { save: save, load: load, export: download, import: importFile })));
|
|
108
108
|
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,OAAO,CAAA;AAChC,OAAO,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAA;AAC3C,OAAO,aAAa,CAAA;AACpB,OAAO,
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.tsx"],"names":[],"mappings":"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"}
|
package/index.css
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-orange-100:oklch(95.4% .038 75.164);--color-orange-500:oklch(70.5% .213 47.604);--color-green-800:oklch(44.8% .119 151.328);--color-violet-900:oklch(38% .189 293.745);--color-neutral-400:oklch(70.8% 0 0);--color-neutral-800:oklch(26.9% 0 0);--color-neutral-900:oklch(20.5% 0 0);--color-neutral-950:oklch(14.5% 0 0);--color-stone-100:oklch(97% .001 106.424);--color-stone-200:oklch(92.3% .003 48.717);--color-stone-400:oklch(70.9% .01 56.259);--color-stone-500:oklch(55.3% .013 58.071);--color-stone-600:oklch(44.4% .011 73.639);--color-stone-700:oklch(37.4% .01 67.558);--color-stone-800:oklch(26.8% .007 34.298);--color-stone-900:oklch(21.6% .006 56.043);--color-stone-950:oklch(14.7% .004 49.25);--color-white:#fff;--spacing:.25rem;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--font-weight-bold:700;--radius-lg:.5rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.top-0{top:calc(var(--spacing)*0)}.top-1{top:calc(var(--spacing)*1)}.top-2{top:calc(var(--spacing)*2)}.top-3{top:calc(var(--spacing)*3)}.top-full{top:100%}.-right-3{right:calc(var(--spacing)*-3)}.-right-5{right:calc(var(--spacing)*-5)}.-right-7{right:calc(var(--spacing)*-7)}.-right-15{right:calc(var(--spacing)*-15)}.right-8{right:calc(var(--spacing)*8)}.-left-4{left:calc(var(--spacing)*-4)}.left-0{left:calc(var(--spacing)*0)}.-z-20{z-index:calc(20*-1)}.z-0{z-index:0}.z-10{z-index:10}.z-50{z-index:50}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.m-1{margin:calc(var(--spacing)*1)}.mx-1{margin-inline:calc(var(--spacing)*1)}.mx-2{margin-inline:calc(var(--spacing)*2)}.my-1{margin-block:calc(var(--spacing)*1)}.-mt-2{margin-top:calc(var(--spacing)*-2)}.-mb-2{margin-bottom:calc(var(--spacing)*-2)}.flex{display:flex}.hidden{display:none}.aspect-square{aspect-ratio:1}.h-0{height:calc(var(--spacing)*0)}.h-4{height:calc(var(--spacing)*4)}.h-6{height:calc(var(--spacing)*6)}.h-8{height:calc(var(--spacing)*8)}.h-64{height:calc(var(--spacing)*64)}.h-full{height:100%}.max-h-32{max-height:calc(var(--spacing)*32)}.min-h-8{min-height:calc(var(--spacing)*8)}.min-h-full{min-height:100%}.w-0{width:calc(var(--spacing)*0)}.w-1\/3{width:33.3333%}.w-4{width:calc(var(--spacing)*4)}.w-6{width:calc(var(--spacing)*6)}.w-8{width:calc(var(--spacing)*8)}.w-16{width:calc(var(--spacing)*16)}.w-50{width:calc(var(--spacing)*50)}.w-full{width:100%}.max-w-full{max-width:100%}.min-w-full{min-width:100%}.grow{flex-grow:1}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-0{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-t-lg{border-top-left-radius:var(--radius-lg);border-top-right-radius:var(--radius-lg)}.rounded-b-lg{border-bottom-right-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-green-800{border-color:var(--color-green-800)}.border-neutral-400\/20{border-color:#a1a1a133}@supports (color:color-mix(in lab, red, red)){.border-neutral-400\/20{border-color:color-mix(in oklab,var(--color-neutral-400)20%,transparent)}}.border-stone-200\/30{border-color:#e7e5e44d}@supports (color:color-mix(in lab, red, red)){.border-stone-200\/30{border-color:color-mix(in oklab,var(--color-stone-200)30%,transparent)}}.border-violet-900{border-color:var(--color-violet-900)}.bg-green-800{background-color:var(--color-green-800)}.bg-neutral-800{background-color:var(--color-neutral-800)}.bg-neutral-900{background-color:var(--color-neutral-900)}.bg-neutral-950{background-color:var(--color-neutral-950)}.bg-stone-700{background-color:var(--color-stone-700)}.bg-stone-800{background-color:var(--color-stone-800)}.bg-stone-900{background-color:var(--color-stone-900)}.bg-stone-950{background-color:var(--color-stone-950)}.bg-violet-900{background-color:var(--color-violet-900)}.stroke-stone-600{stroke:var(--color-stone-600)}.p-1{padding:calc(var(--spacing)*1)}.p-2{padding:calc(var(--spacing)*2)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-4{padding-inline:calc(var(--spacing)*4)}.py-1{padding-block:calc(var(--spacing)*1)}.py-2{padding-block:calc(var(--spacing)*2)}.pt-1{padding-top:calc(var(--spacing)*1)}.pt-2{padding-top:calc(var(--spacing)*2)}.pb-1{padding-bottom:calc(var(--spacing)*1)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-8{padding-bottom:calc(var(--spacing)*8)}.text-center{text-align:center}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.text-nowrap{text-wrap:nowrap}.text-ellipsis{text-overflow:ellipsis}.text-orange-100{color:var(--color-orange-100)}.text-orange-500{color:var(--color-orange-500)}.text-stone-100{color:var(--color-stone-100)}.text-stone-200{color:var(--color-stone-200)}.text-stone-500{color:var(--color-stone-500)}.text-white{color:var(--color-white)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media (hover:hover){.hover\:scale-105:hover{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x)var(--tw-scale-y)}.hover\:bg-stone-900:hover{background-color:var(--color-stone-900)}}.disabled\:cursor-default:disabled{cursor:default}.disabled\:bg-neutral-800:disabled{background-color:var(--color-neutral-800)}.disabled\:bg-neutral-900:disabled{background-color:var(--color-neutral-900)}.disabled\:text-stone-400:disabled{color:var(--color-stone-400)}@media (hover:hover){.disabled\:hover\:scale-100:disabled:hover{--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x)var(--tw-scale-y)}}}.workspace{position:relative}.workspace:before{background-image:radial-gradient(var(--color-neutral-800)10%,transparent 10%);content:"";pointer-events:none;background-size:20px 20px;width:100%;height:100%;display:block;position:fixed}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}
|
package/package.json
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
"repository": "MacaronFR/chatbot-editor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"/dist",
|
|
9
|
+
"/index.css"
|
|
10
|
+
],
|
|
7
11
|
"types": "dist/index.d.ts",
|
|
8
12
|
"scripts": {
|
|
9
13
|
"dev": "vite",
|
|
@@ -34,5 +38,5 @@
|
|
|
34
38
|
"typescript-eslint": "^8.46.4",
|
|
35
39
|
"vite": "^7.2.4"
|
|
36
40
|
},
|
|
37
|
-
"version": "1.0.
|
|
41
|
+
"version": "1.0.2"
|
|
38
42
|
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
name: publish on npm
|
|
2
|
-
on:
|
|
3
|
-
release:
|
|
4
|
-
types:
|
|
5
|
-
- published
|
|
6
|
-
|
|
7
|
-
jobs:
|
|
8
|
-
publish-npm:
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
steps:
|
|
11
|
-
- uses: actions/checkout@v4
|
|
12
|
-
- uses: actions/setup-node@v4
|
|
13
|
-
with:
|
|
14
|
-
node version: 20
|
|
15
|
-
registry-url: https://registry.npmjs.org/
|
|
16
|
-
- run: npm install
|
|
17
|
-
- run: npm version ${{ github.event.release.name }} --git-tag-version=false
|
|
18
|
-
- run: npm run build
|
|
19
|
-
- run: npm publish
|
|
20
|
-
env:
|
|
21
|
-
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
package/dist/App.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"App.js","sourceRoot":"","sources":["../src/App.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,MAAM,CAAC;AACtB,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,OAAO,EAAC,QAAQ,EAAC,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAC,uBAAuB,EAAC,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,MAAM,OAAO,CAAC;AAS1B,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,KAAe;IAC1C,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAc,EAAE,CAAC,CAAC;IAC5D,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACtD,OAAO,CACN,oBAAC,uBAAuB;QACvB,6BAAK,SAAS,EAAE,4CAA4C;YAC3D,oBAAC,KAAK,IAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,cAAc,GAAG;YACpH,oBAAC,EAAE,IAAC,cAAc,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,OAA+B,EAAE,EAAE;oBAC1G,IAAI,UAAU,GAAG,UAAU,CAAC;oBAC5B,IAAI,CAAC,GAAG,CAAC,CAAC;oBACV,OAAM,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,UAAU,CAAC,EAAE,CAAC;wBAChD,CAAC,EAAE,CAAC;wBACJ,UAAU,GAAG,WAAW,CAAC,EAAE,CAAC;oBAC7B,CAAC;oBACD,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAC,CAAC,CAAC,CAAC;gBACpF,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAC7B,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAE3D,cAAc,EAAE,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,EACzE,YAAY,EAAE,YAAY,EAC1B,IAAI,EAAE,KAAK,CAAC,IAAI,EAChB,IAAI,EAAE,KAAK,CAAC,IAAI,EAChB,MAAM,EAAE,KAAK,CAAC,MAAM,EACpB,MAAM,EAAE,KAAK,CAAC,MAAM,GACnB,CACG,CACmB,CAC1B,CAAA;AACF,CAAC"}
|
package/eslint.config.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import js from '@eslint/js'
|
|
2
|
-
import globals from 'globals'
|
|
3
|
-
import reactHooks, {rules} from 'eslint-plugin-react-hooks'
|
|
4
|
-
import reactRefresh from 'eslint-plugin-react-refresh'
|
|
5
|
-
import tseslint from 'typescript-eslint'
|
|
6
|
-
import { defineConfig, globalIgnores } from 'eslint/config'
|
|
7
|
-
|
|
8
|
-
export default defineConfig([
|
|
9
|
-
globalIgnores(['dist']),
|
|
10
|
-
{
|
|
11
|
-
files: ['**/*.{ts,tsx}'],
|
|
12
|
-
extends: [
|
|
13
|
-
js.configs.recommended,
|
|
14
|
-
tseslint.configs.recommended,
|
|
15
|
-
reactHooks.configs.flat.recommended,
|
|
16
|
-
reactRefresh.configs.vite,
|
|
17
|
-
],
|
|
18
|
-
languageOptions: {
|
|
19
|
-
ecmaVersion: 2020,
|
|
20
|
-
globals: globals.browser,
|
|
21
|
-
},
|
|
22
|
-
rules: {
|
|
23
|
-
"react-hooks/set-state-in-effect": "off"
|
|
24
|
-
},
|
|
25
|
-
}
|
|
26
|
-
]);
|
package/index.html
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>chatbot-editor</title>
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<div id="root"></div>
|
|
10
|
-
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
-
</body>
|
|
12
|
-
</html>
|
package/src/App.tsx
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import UI from "./UI";
|
|
2
|
-
import Canva from "./Canva";
|
|
3
|
-
import {useState} from "react";
|
|
4
|
-
import type TQuestion from "./TQuestion.ts";
|
|
5
|
-
import {CurrentQuestionProvider} from "./CurrentQuestionHook";
|
|
6
|
-
import React from "react";
|
|
7
|
-
|
|
8
|
-
interface AppProps {
|
|
9
|
-
save?: (questions: TQuestion[]) => void;
|
|
10
|
-
load?: () => Promise<TQuestion[]>;
|
|
11
|
-
export?: (questions: TQuestion[]) => void;
|
|
12
|
-
import?: () => Promise<TQuestion[]>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default function App(props: AppProps) {
|
|
16
|
-
const [questions, setQuestions] = useState<TQuestion[]>([]);
|
|
17
|
-
const [reloadArrow, setReloadArrow] = useState(false);
|
|
18
|
-
return (
|
|
19
|
-
<CurrentQuestionProvider>
|
|
20
|
-
<div className={"relative h-screen w-screen overflow-hidden"}>
|
|
21
|
-
<Canva questions={questions} setQuestions={setQuestions} reloadArrow={reloadArrow} setReloadArrow={setReloadArrow}/>
|
|
22
|
-
<UI setReloadArrow={setReloadArrow} questions={questions} addQuestion={(initPos: {x: number, y: number}) => {
|
|
23
|
-
let idQuestion = "question";
|
|
24
|
-
let i = 0;
|
|
25
|
-
while(questions.find(v => v.id === idQuestion)) {
|
|
26
|
-
i++;
|
|
27
|
-
idQuestion = `question${i}`;
|
|
28
|
-
}
|
|
29
|
-
setQuestions(prev => [...prev, {id: idQuestion, text: "Text", position: initPos}]);
|
|
30
|
-
}} editQuestion={(id, data) =>
|
|
31
|
-
setQuestions(prev => prev.map(v => v.id === id ? data : v))
|
|
32
|
-
}
|
|
33
|
-
deleteQuestion={id => setQuestions(prev => prev.filter(v => v.id !== id))}
|
|
34
|
-
setQuestions={setQuestions}
|
|
35
|
-
save={props.save}
|
|
36
|
-
load={props.load}
|
|
37
|
-
export={props.export}
|
|
38
|
-
import={props.import}
|
|
39
|
-
/>
|
|
40
|
-
</div>
|
|
41
|
-
</CurrentQuestionProvider>
|
|
42
|
-
)
|
|
43
|
-
}
|
package/src/Canva.tsx
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import Question from "./Components/Question";
|
|
3
|
-
import type TQuestion from "./TQuestion.ts";
|
|
4
|
-
import type {SetState} from "./utils.ts";
|
|
5
|
-
import {useEffect} from "react";
|
|
6
|
-
|
|
7
|
-
interface CanvaProps {
|
|
8
|
-
questions: TQuestion[];
|
|
9
|
-
setQuestions: SetState<TQuestion[]>;
|
|
10
|
-
reloadArrow: boolean;
|
|
11
|
-
setReloadArrow: SetState<boolean>;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export default function Canva(props: CanvaProps) {
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
props.setReloadArrow(prev => !prev);
|
|
17
|
-
}, [props.questions, props.setReloadArrow]);
|
|
18
|
-
return (
|
|
19
|
-
<div className={"min-w-full min-h-full bg-neutral-950 workspace overflow-auto"}>
|
|
20
|
-
{props.questions.map((v, i) => <Question {...v} key={i} reloadArrow={props.reloadArrow} setReloadArrow={props.setReloadArrow}/>)}
|
|
21
|
-
</div>
|
|
22
|
-
)
|
|
23
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import Arrow from "./Arrow";
|
|
3
|
-
import {useEffect, useMemo, useRef, useState} from "react";
|
|
4
|
-
import {CiWarning} from "react-icons/ci";
|
|
5
|
-
|
|
6
|
-
interface AnswersProps {
|
|
7
|
-
text: string;
|
|
8
|
-
next: string;
|
|
9
|
-
reloadArrows: boolean;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export default function Answers(props: AnswersProps) {
|
|
13
|
-
const container = useRef<HTMLDivElement>(null);
|
|
14
|
-
const [pos, setPos] = useState({x: 0, y: 0});
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
setPos(container.current!.getBoundingClientRect());
|
|
17
|
-
}, [props.reloadArrows]);
|
|
18
|
-
const destPos = useMemo(() => {
|
|
19
|
-
return document.getElementById(props.next)?.getBoundingClientRect();
|
|
20
|
-
}, [props.next, props.reloadArrows]);
|
|
21
|
-
return (
|
|
22
|
-
<div className={"rounded bg-stone-700 w-16 px-2 py-1 text-nowrap text-ellipsis relative"} ref={container}>
|
|
23
|
-
<p className={"text-ellipsis text-nowrap overflow-hidden max-w-full"} title={props.text}>{props.text}</p>
|
|
24
|
-
{destPos && <Arrow from={{x: 64, y: 16}} to={{x: destPos.x - pos.x - 1, y: destPos.y - pos.y + 20}}/> || <CiWarning className={"text-orange-500 absolute -right-5 top-2"}/>}
|
|
25
|
-
</div>
|
|
26
|
-
)
|
|
27
|
-
}
|
package/src/Components/Arrow.tsx
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import {useMemo, useRef} from "react";
|
|
3
|
-
|
|
4
|
-
interface ArrowProps {
|
|
5
|
-
from: {x: number, y: number},
|
|
6
|
-
to: {x: number, y: number}
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export default function Arrow(props: ArrowProps) {
|
|
10
|
-
const ref = useRef<SVGSVGElement>(null);
|
|
11
|
-
const [width, height] = useMemo(() => [
|
|
12
|
-
Math.abs(props.from.x - props.to.x) + (props.from.x + 20 <= props.to.x - 20 ? 0 : 40),
|
|
13
|
-
Math.abs(props.from.y - props.to.y)
|
|
14
|
-
], [props.from, props.to]);
|
|
15
|
-
const [start] = useMemo(() => [
|
|
16
|
-
{x: props.from.x + 20 < props.to.x - 20 ? 0 : -20, y: 0}
|
|
17
|
-
], [props.from, props.to]);
|
|
18
|
-
const [left, top] = useMemo(() =>
|
|
19
|
-
[
|
|
20
|
-
props.from.x + 20 <= props.to.x - 20 ? props.from.x : props.to.x - 20,
|
|
21
|
-
props.from.y < props.to.y ? props.from.y : props.to.y
|
|
22
|
-
],
|
|
23
|
-
[props.from, props.to]
|
|
24
|
-
);
|
|
25
|
-
const [from, to] = useMemo(() => [
|
|
26
|
-
{x: props.from.x + 20 < props.to.x - 20 ? 0 : (props.from.x < props.to.x ? (props.from.x === props.to.x ? 0 : -20) : width - 40), y: props.from.y < props.to.y ? 0 : height},
|
|
27
|
-
{x: props.from.x + 20 < props.to.x - 20 ? width : (props.from.x + 20 === props.to.x - 20 ? 20 : 0), y: props.from.y < props.to.y ? height : 0},
|
|
28
|
-
], [props.from, props.to, width, height]);
|
|
29
|
-
const path = useMemo(() => {
|
|
30
|
-
let p = `M${from.x} ${from.y} L${from.x + 16} ${from.y}`;
|
|
31
|
-
if(from.x + 20 < to.x - 20) {
|
|
32
|
-
if(from.y < to.y) {
|
|
33
|
-
p += ` L${(to.x - from.x) / 2 - 4} ${from.y} Q${(to.x - from.x) / 2 - 4} ${from.y} ${(to.x - from.x) / 2} ${from.y + 4} L${(to.x - from.x) / 2} ${to.y - 4} Q${(to.x - from.x) / 2} ${to.y - 4} ${(to.x - from.x) / 2 + 4} ${to.y}`;
|
|
34
|
-
} else if(from.y > to.y) {
|
|
35
|
-
p += ` L${(to.x - from.x) / 2 - 4} ${from.y} Q${(to.x - from.x) / 2 - 4} ${from.y} ${(to.x - from.x) / 2} ${from.y - 4} L${(to.x - from.x) / 2} ${to.y + 4} Q${(to.x - from.x) / 2} ${to.y + 4} ${(to.x - from.x) / 2 + 4} ${to.y}`;
|
|
36
|
-
}
|
|
37
|
-
} else if(from.x + 20 > to.x - 20) {
|
|
38
|
-
if(from.y < to.y) {
|
|
39
|
-
p += ` Q${from.x + 16} ${from.y} ${from.x + 20} ${from.y + 4} L${from.x + 20} ${to.y - 44} Q${from.x + 20} ${to.y - 44} ${from.x + 16} ${to.y - 40} L${to.x-16} ${to.y - 40} Q${to.x-16} ${to.y - 40} ${to.x-20} ${to.y - 36}`;
|
|
40
|
-
p += ` L${to.x - 20} ${to.y - 4} Q${to.x - 20} ${to.y - 4} ${to.x - 16} ${to.y}`;
|
|
41
|
-
} else if(from.y > to.y) {
|
|
42
|
-
p += ` Q${from.x + 16} ${from.y} ${from.x + 20} ${from.y - 4} L${from.x + 20} ${from.y - 36} Q${from.x + 20} ${from.y - 36} ${from.x + 16} ${from.y -40} L${to.x-16} ${from.y - 40} Q${to.x-16} ${from.y - 40} ${to.x-20} ${from.y - 44}`;
|
|
43
|
-
p += ` L${to.x - 20} ${to.y + 4} Q${to.x - 20} ${to.y + 4} ${to.x - 16} ${to.y}`;
|
|
44
|
-
}
|
|
45
|
-
} else {
|
|
46
|
-
if(from.y < to.y) {
|
|
47
|
-
p += ` Q${from.x + 16} ${from.y} ${from.x + 20} ${from.y + 4} L${from.x + 20} ${to.y - 4} Q${from.x + 20} ${to.y - 4} ${from.x + 24} ${to.y}`;
|
|
48
|
-
} else if(from.y > to.y) {
|
|
49
|
-
p += ` Q${from.x + 16} ${from.y} ${from.x + 20} ${from.y - 4} L${from.x + 20} ${to.y + 4} Q${from.x + 20} ${to.y + 4} ${from.x + 24} ${to.y}`;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
p += ` L${to.x} ${to.y}`
|
|
53
|
-
return p;
|
|
54
|
-
}, [from, to]);
|
|
55
|
-
return (
|
|
56
|
-
<svg ref={ref} viewBox={`${start.x} ${start.y} ${width} ${height}`} width={width} height={height} className={"absolute -z-20 stroke-stone-600 pointer-events-none"} style={{left: left, top: top}}>
|
|
57
|
-
<path d={path} fill={"none"} strokeWidth={"2"}/>
|
|
58
|
-
</svg>
|
|
59
|
-
)
|
|
60
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import {type ReactNode} from "react";
|
|
2
|
-
import React from "react";
|
|
3
|
-
|
|
4
|
-
interface ButtonProps {
|
|
5
|
-
children: ReactNode;
|
|
6
|
-
disabled?: boolean;
|
|
7
|
-
onClick?: (e: React.MouseEvent) => void;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export default function Button(props: ButtonProps) {
|
|
11
|
-
return (
|
|
12
|
-
<button className={"bg-stone-800 rounded-lg py-1 px-2 text-lg cursor-pointer transition hover:scale-105 disabled:hover:scale-100 disabled:cursor-default disabled:text-stone-400 disabled:bg-neutral-800"} onClick={props.onClick} disabled={props.disabled}>{props.children}</button>
|
|
13
|
-
)
|
|
14
|
-
}
|
package/src/Components/Input.tsx
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import {useMemo} from "react";
|
|
3
|
-
import {clsx} from "clsx";
|
|
4
|
-
|
|
5
|
-
interface InputProps {
|
|
6
|
-
value: string;
|
|
7
|
-
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
8
|
-
type?: React.HTMLInputTypeAttribute;
|
|
9
|
-
label?: React.ReactNode;
|
|
10
|
-
disabled?: boolean;
|
|
11
|
-
id?: string;
|
|
12
|
-
className?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default function Input(props: InputProps) {
|
|
16
|
-
const id = useMemo(() => {
|
|
17
|
-
return props.id ?? (Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15));
|
|
18
|
-
}, [props.id]);
|
|
19
|
-
return (
|
|
20
|
-
<div className={clsx("flex flex-col", props.className)}>
|
|
21
|
-
{props.label && <label className={clsx("px-2 pt-1 rounded-t-lg pb-2 -mb-2", props.disabled === true && "text-stone-500 bg-neutral-950" || "bg-stone-950")} htmlFor={id}>{props.label}</label>}
|
|
22
|
-
<input type={props.type} value={props.value} onChange={props.onChange} id={id} className={"bg-stone-800 rounded-lg py-1 px-2 text-lg disabled:bg-neutral-900"} disabled={props.disabled}/>
|
|
23
|
-
</div>
|
|
24
|
-
)
|
|
25
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import {useCallback, useEffect, useMemo, useRef, useState} from "react";
|
|
2
|
-
import Bubble from "./Bubble";
|
|
3
|
-
import Answers from "./Answers";
|
|
4
|
-
import {useCurrentQuestion} from "../CurrentQuestionHook";
|
|
5
|
-
import * as React from "react";
|
|
6
|
-
import Arrow from "./Arrow";
|
|
7
|
-
import type {SetState} from "../utils.ts";
|
|
8
|
-
import type {TAction, TAnswers} from "../TQuestion.ts";
|
|
9
|
-
import {CiWarning} from "react-icons/ci";
|
|
10
|
-
|
|
11
|
-
interface QuestionProps {
|
|
12
|
-
id: string;
|
|
13
|
-
text: string | string[];
|
|
14
|
-
goto?: string;
|
|
15
|
-
answers?: TAnswers[];
|
|
16
|
-
action?: TAction;
|
|
17
|
-
reloadArrow: boolean;
|
|
18
|
-
setReloadArrow: SetState<boolean>;
|
|
19
|
-
position: {x: number, y: number};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export default function Question(props: QuestionProps) {
|
|
23
|
-
const container = useRef<HTMLDivElement>(null);
|
|
24
|
-
const [position, setPosition] = useState(props.position);
|
|
25
|
-
const [clicked, setClicked] = useState(false);
|
|
26
|
-
const [, setOpen] = useState(false);
|
|
27
|
-
const [, setQuestion] = useCurrentQuestion();
|
|
28
|
-
const onMouseDown = useCallback((e: React.MouseEvent) => {
|
|
29
|
-
if(e.button !== 0) return;
|
|
30
|
-
setClicked(true);
|
|
31
|
-
setOpen(true);
|
|
32
|
-
}, []);
|
|
33
|
-
const onMouseUp = useCallback((e: MouseEvent) => {
|
|
34
|
-
if(e.button !== 0) return;
|
|
35
|
-
if(e.target !== container.current && (container.current && !container.current.contains(e.target as Node))) return;
|
|
36
|
-
setClicked(false);
|
|
37
|
-
setOpen(prev => {
|
|
38
|
-
if(prev) {
|
|
39
|
-
setQuestion(props);
|
|
40
|
-
}
|
|
41
|
-
return true;
|
|
42
|
-
})
|
|
43
|
-
}, [props, setQuestion]);
|
|
44
|
-
const onMouseMove = useCallback((e: MouseEvent) => {
|
|
45
|
-
setOpen(false);
|
|
46
|
-
setPosition(prev => {
|
|
47
|
-
return {
|
|
48
|
-
x: prev.x + e.movementX,
|
|
49
|
-
y: prev.y + e.movementY
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
props.setReloadArrow(prev => !prev);
|
|
53
|
-
}, [setPosition, props]);
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
if(clicked) {
|
|
56
|
-
document.addEventListener("mousemove", onMouseMove);
|
|
57
|
-
}
|
|
58
|
-
return () => {
|
|
59
|
-
document.removeEventListener("mousemove", onMouseMove)
|
|
60
|
-
}
|
|
61
|
-
}, [clicked, onMouseMove]);
|
|
62
|
-
useEffect(() => {
|
|
63
|
-
document.addEventListener("mouseup", onMouseUp);
|
|
64
|
-
return () => document.removeEventListener("mouseup", onMouseUp);
|
|
65
|
-
}, [onMouseUp]);
|
|
66
|
-
const gotoPosition = useMemo(() => {
|
|
67
|
-
let pos: {x: number, y: number} | undefined = undefined;
|
|
68
|
-
if(props.goto || props.action?.goto) {
|
|
69
|
-
const toElement = document.getElementById(props.goto ?? props.action?.goto ?? "");
|
|
70
|
-
if(toElement) {
|
|
71
|
-
pos = {x: toElement.offsetLeft, y: toElement.offsetTop};
|
|
72
|
-
pos.x -= (position.x - position.x % 20);
|
|
73
|
-
pos.y -= (position.y - position.y % 20);
|
|
74
|
-
pos.y += 18
|
|
75
|
-
} else {
|
|
76
|
-
pos = undefined;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return pos;
|
|
80
|
-
}, [props.goto, props.action?.goto, position, props.reloadArrow]);
|
|
81
|
-
return (
|
|
82
|
-
<div id={props.id} className={"w-50 h-64 bg-stone-900 border border-neutral-400/20 rounded z-10 absolute flex flex-col gap-1 pb-1"} style={{top: position.y - (position.y % 20), left: position.x - (position.x % 20)}} onMouseDown={onMouseDown} ref={container}>
|
|
83
|
-
<h2 title={props.id} className={"text-stone-100 m-1 min-h-8 bg-neutral-900 px-2 py-1 font-mono overflow-hidden text-ellipsis"}>{props.id}</h2>
|
|
84
|
-
<div className={"p-1 flex flex-col gap-1 max-h-32 overflow-x-hidden overflow-y-auto"}>
|
|
85
|
-
{ typeof props.text === "string" ? <Bubble text={props.text}/> : props.text.map((v, i) => <Bubble text={v} key={i}/>)}
|
|
86
|
-
</div>
|
|
87
|
-
<div className={"absolute -right-15 top-1 flex flex-col gap-1.5"}>
|
|
88
|
-
{props.answers?.map((v, i) => <Answers text={v.text} key={i} next={v.next} reloadArrows={props.reloadArrow}/>)}
|
|
89
|
-
</div>
|
|
90
|
-
{props.id === "start" && <div className={"absolute top-2 -left-4 w-6 h-6 rounded-full border-2 border-green-800 text-center flex items-center justify-center"}><div className={"bg-green-800 w-4 h-4 rounded-full"}/></div>}
|
|
91
|
-
{(props.goto || props.action?.goto) && <div>
|
|
92
|
-
<div className={"absolute top-2 -right-3 w-6 h-6 rounded-full border-2 border-violet-900 text-center flex justify-center items-center"}><div className={"bg-violet-900 w-4 h-4 rounded-full"}/></div>
|
|
93
|
-
{gotoPosition && <Arrow from={{x: 200, y: 20}} to={gotoPosition}/> || <CiWarning className={"text-orange-500 absolute top-3 -right-7"}/>}
|
|
94
|
-
</div>}
|
|
95
|
-
<div className={"grow"}/>
|
|
96
|
-
{ props.action && <div className={"font-mono text-stone-200 px-4 mx-1 rounded bg-neutral-800 overflow-hidden"}>
|
|
97
|
-
<p className={"font-sans"}>Action</p>
|
|
98
|
-
{props.action.action === "set" && <p><span>{props.action.property}</span> == {props.action.value}</p> }
|
|
99
|
-
{props.action.action === "ask" && <p><span>{props.action.property}</span> == user input</p> }
|
|
100
|
-
{props.action.action === "end" && <p>Chatbot end</p> }
|
|
101
|
-
</div>}
|
|
102
|
-
</div>
|
|
103
|
-
)
|
|
104
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import {useMemo, useRef, useState} from "react";
|
|
3
|
-
import {clsx} from "clsx";
|
|
4
|
-
import {BsChevronDown} from "react-icons/bs";
|
|
5
|
-
|
|
6
|
-
export interface Option {
|
|
7
|
-
value: string;
|
|
8
|
-
label: React.ReactNode;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface SelectProps {
|
|
12
|
-
options: Option[];
|
|
13
|
-
id?: string;
|
|
14
|
-
value: string;
|
|
15
|
-
onChange: (v: string) => void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export default function Select(props: SelectProps) {
|
|
19
|
-
const select = useRef<HTMLSelectElement | null>(null);
|
|
20
|
-
const id = useMemo(() => {
|
|
21
|
-
return props.id ?? (Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15));
|
|
22
|
-
}, [props.id]);
|
|
23
|
-
const [visible, setVisible] = useState(false);
|
|
24
|
-
return (
|
|
25
|
-
<label htmlFor={id}>
|
|
26
|
-
<div className={"relative bg-stone-800 rounded-lg h-8 flex justify-between items-center px-2"} onClick={() => setVisible(prev => !prev)}>
|
|
27
|
-
<p>{props.options.find(o => o.value == props.value)?.label ?? props.value}</p>
|
|
28
|
-
<button><BsChevronDown/></button>
|
|
29
|
-
<div className={clsx("absolute bg-stone-800 pt-2 -mt-2 z-0 w-full rounded-b-lg top-full left-0", visible || "hidden")}>
|
|
30
|
-
{props.options.map((v, i) =>
|
|
31
|
-
<p key={i} className={"p-1 hover:bg-stone-900"} onClick={() => {
|
|
32
|
-
if(select.current) {
|
|
33
|
-
select.current.value = v.value;
|
|
34
|
-
props.onChange(v.value);
|
|
35
|
-
}
|
|
36
|
-
}}>{v.label}</p>
|
|
37
|
-
)}
|
|
38
|
-
</div>
|
|
39
|
-
</div>
|
|
40
|
-
<select className={"hidden"} id={id} onChange={e => props.onChange(e.target.value)} value={props.value} ref={select}>
|
|
41
|
-
{props.options.map((v, i) => <option value={v.value} key={i}>{v.label}</option>)}
|
|
42
|
-
</select>
|
|
43
|
-
</label>
|
|
44
|
-
)
|
|
45
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import {type ReactNode} from "react";
|
|
2
|
-
import type {SetState} from "../utils.ts";
|
|
3
|
-
import {clsx} from "clsx";
|
|
4
|
-
import {BsCheck, BsX} from "react-icons/bs";
|
|
5
|
-
import React from "react";
|
|
6
|
-
|
|
7
|
-
interface SideBarProps {
|
|
8
|
-
display: boolean;
|
|
9
|
-
setDisplay: SetState<boolean>;
|
|
10
|
-
children: ReactNode;
|
|
11
|
-
onClose?: () => void;
|
|
12
|
-
onValidate?: () => void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default function SideBar(props: SideBarProps) {
|
|
16
|
-
return (
|
|
17
|
-
<div className={clsx("fixed transition w-1/3 h-screen top-0 left-0 bg-stone-900 z-50 flex flex-col", props.display && "translate-x-0" || "-translate-x-full")}>
|
|
18
|
-
<div className={"flex justify-end"}>
|
|
19
|
-
<button className={"mx-2 my-1 p-1 rounded cursor-pointer border border-stone-200/30 aspect-square w-8"} onClick={() => {
|
|
20
|
-
props.setDisplay(false);
|
|
21
|
-
if(props.onClose) {
|
|
22
|
-
props.onClose();
|
|
23
|
-
}}}>
|
|
24
|
-
<BsX className={"w-6 h-6"}/>
|
|
25
|
-
</button>
|
|
26
|
-
{props.onValidate && <button className={"mx-2 my-1 p-1 rounded cursor-pointer border border-stone-200/30 aspect-square w-8"} onClick={() => props.onValidate && props.onValidate()}><BsCheck className={"w-6 h-6"}/></button>}
|
|
27
|
-
</div>
|
|
28
|
-
<div className={"p-2 grow overflow-hidden"}>{props.children}</div>
|
|
29
|
-
</div>
|
|
30
|
-
)
|
|
31
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import {createContext, type ReactNode, useContext, useState} from "react";
|
|
3
|
-
import type TQuestion from "./TQuestion.ts";
|
|
4
|
-
import type {SetState} from "./utils.ts";
|
|
5
|
-
|
|
6
|
-
const CurrentQuestionContext = createContext<[TQuestion | null, SetState<TQuestion | null>]>([null, () => {}]);
|
|
7
|
-
|
|
8
|
-
interface CurrentQuestionProviderProps {
|
|
9
|
-
children: ReactNode;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function CurrentQuestionProvider(props: CurrentQuestionProviderProps) {
|
|
13
|
-
const [question, setQuestion] = useState<TQuestion | null>(null);
|
|
14
|
-
return <CurrentQuestionContext.Provider value={[question, setQuestion]}>
|
|
15
|
-
{props.children}
|
|
16
|
-
</CurrentQuestionContext.Provider>
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// eslint-disable-next-line react-refresh/only-export-components
|
|
20
|
-
export function useCurrentQuestion() {
|
|
21
|
-
return useContext(CurrentQuestionContext);
|
|
22
|
-
}
|
package/src/TQuestion.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export default interface TQuestion {
|
|
2
|
-
id: string;
|
|
3
|
-
text: string | string[];
|
|
4
|
-
answers?: TAnswers[];
|
|
5
|
-
goto?: string;
|
|
6
|
-
action?: TAction;
|
|
7
|
-
position: {x: number, y: number};
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface TAnswers {
|
|
11
|
-
text: string;
|
|
12
|
-
next: string;
|
|
13
|
-
action?: TAction
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface TAction {
|
|
17
|
-
action: TActionValue
|
|
18
|
-
property?: string;
|
|
19
|
-
value?: any;
|
|
20
|
-
goto?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type TActionValue = "set" | "ask" | "end";
|
package/src/UI.tsx
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import SideBar from "./Components/SideBar";
|
|
2
|
-
import {useEffect, useState} from "react";
|
|
3
|
-
import {useCurrentQuestion} from "./CurrentQuestionHook";
|
|
4
|
-
import type TQuestion from "./TQuestion.ts";
|
|
5
|
-
import type { TAnswers, TAction } from "./TQuestion.ts";
|
|
6
|
-
import type {SetState} from "./utils.ts";
|
|
7
|
-
import Button from "./Components/Button";
|
|
8
|
-
import Input from "./Components/Input";
|
|
9
|
-
import {clsx} from "clsx";
|
|
10
|
-
import Select from "./Components/Select";
|
|
11
|
-
import {BsPlus} from "react-icons/bs";
|
|
12
|
-
import {LuSave} from "react-icons/lu";
|
|
13
|
-
import {MdOutlineFileOpen} from "react-icons/md";
|
|
14
|
-
import {BiDownload, BiUpload} from "react-icons/bi";
|
|
15
|
-
import React from "react";
|
|
16
|
-
|
|
17
|
-
interface UIProps {
|
|
18
|
-
questions: TQuestion[];
|
|
19
|
-
setQuestions: SetState<TQuestion[]>;
|
|
20
|
-
addQuestion: (initPos: {x: number, y: number}) => void;
|
|
21
|
-
editQuestion: (id: string, data: TQuestion) => void;
|
|
22
|
-
deleteQuestion: (id: string) => void;
|
|
23
|
-
setReloadArrow: SetState<boolean>;
|
|
24
|
-
save?: (questions: TQuestion[]) => void;
|
|
25
|
-
load?: () => Promise<TQuestion[]>;
|
|
26
|
-
export?: (questions: TQuestion[]) => void;
|
|
27
|
-
import?: () => Promise<TQuestion[]>;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export default function UI(props: UIProps) {
|
|
31
|
-
const [question, setQuestion] = useCurrentQuestion();
|
|
32
|
-
const [id, setId] = useState("");
|
|
33
|
-
const [goto, setGoto] = useState<string>();
|
|
34
|
-
const [text, setText] = useState<string[]>([""]);
|
|
35
|
-
const [answers, setAnswers] = useState<TAnswers[]>([]);
|
|
36
|
-
const [action, setAction] = useState<TAction | undefined>(undefined);
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
if(question) {
|
|
39
|
-
setId(question.id);
|
|
40
|
-
if(typeof question.text === "string") {
|
|
41
|
-
setText([question.text]);
|
|
42
|
-
} else {
|
|
43
|
-
setText(question.text);
|
|
44
|
-
}
|
|
45
|
-
setGoto(question.goto);
|
|
46
|
-
setAnswers(question.answers ?? []);
|
|
47
|
-
setAction(question.action);
|
|
48
|
-
}
|
|
49
|
-
}, [question]);
|
|
50
|
-
return (
|
|
51
|
-
<div className={"fixed top-0 left-0 w-0 h-0 overflow-visible text-white z-10"}>
|
|
52
|
-
<SideBar display={question !== null} setDisplay={(value) => { if(typeof value === "function") value = value(question !== null); if(!value) setQuestion(null) }} onClose={() => setQuestion(null)} onValidate={() => {
|
|
53
|
-
const t = text.length === 1 ? text[0] : text;
|
|
54
|
-
props.editQuestion(question?.id ?? "", {id: id, text: t, goto: goto, answers: answers, position: {x: 0, y: 0}, action: action});
|
|
55
|
-
setQuestion(null);
|
|
56
|
-
}}>
|
|
57
|
-
<div className={"flex flex-col gap-2 h-full pb-8 overflow-y-auto overflow-x-hidden"}>
|
|
58
|
-
<Input type={"text"} value={id} onChange={(e) => setId(e.target.value)} label={"ID"}/>
|
|
59
|
-
<div className={"bg-stone-950 p-2 rounded-lg flex flex-col gap-1"}>
|
|
60
|
-
<p className={"text-lg font-bold "}>Text</p>
|
|
61
|
-
{text.map((value, index) => <div className={"flex gap-1"} key={index}>
|
|
62
|
-
<Input type={"text"} value={value} onChange={(e) => setText(prev => prev.map((v, i) => i === index ? e.target.value : v))}/>
|
|
63
|
-
<Button onClick={() => setText(prev => prev.filter((_, i) => i !== index))}>-</Button>
|
|
64
|
-
</div>)}
|
|
65
|
-
<Button onClick={() => setText(prev => [...prev, ""])}>+</Button>
|
|
66
|
-
</div>
|
|
67
|
-
<div className={"bg-stone-950 p-2 rounded-lg flex flex-col gap-1"}>
|
|
68
|
-
<p className={"text-lg font-bold"}>Answers</p>
|
|
69
|
-
{answers.map((value, index) => <div className={"flex ga flex-col p-1"} key={index}>
|
|
70
|
-
<div className={"flex gap-1 items-end"}>
|
|
71
|
-
<Input type={"text"} value={value.text} onChange={(e) => setAnswers(prev => prev.map((v, i) => i === index ? {...v, text: e.target.value} : v))} label={"Text"}/>
|
|
72
|
-
<Input type={"text"} value={value.next} onChange={(e) => setAnswers(prev => prev.map((v, i) => i === index ? {...v, next: e.target.value} : v))} label={"Next"}/>
|
|
73
|
-
<Button onClick={() => setAnswers(prev => prev.filter((_, i) => i !== index))}>-</Button>
|
|
74
|
-
</div>
|
|
75
|
-
<div className={"flex flex-col gap-1"}>
|
|
76
|
-
<p>Action</p>
|
|
77
|
-
<Select options={[{value: "none", label: "None"}, {value: "set", label: "Set"}]} value={value.action?.action ?? "none"} onChange={ (v) => v === "none" ?
|
|
78
|
-
setAnswers(prev => prev.map((v, i) => i === index ? {...v, action: undefined} : v)) :
|
|
79
|
-
setAnswers(prev => prev.map((v, i) => i === index ? {...v, action: {action: "set"}} : v))
|
|
80
|
-
}/>
|
|
81
|
-
<Input className={clsx(value.action === undefined && "hidden")} label={"Property"} value={value.action?.property ?? ""} onChange={e => setAnswers(prev => prev.map((v, i) => i === index ? {...v, action: {action: value.action!.action, property: e.target.value, value: value.action?.value}} : v))}/>
|
|
82
|
-
<Input className={clsx(value.action === undefined && "hidden")} label={"Value"} value={value.action?.value ?? ""} onChange={e => setAnswers(prev => prev.map((v, i) => i === index ? {...v, action: {action: value.action!.action, value: e.target.value, property: value.action?.property}} : v))}/>
|
|
83
|
-
</div>
|
|
84
|
-
</div>)}
|
|
85
|
-
<Button disabled={goto !== undefined} onClick={() => setAnswers(prev => [...prev, {text: "", next: ""}])}>+</Button>
|
|
86
|
-
</div>
|
|
87
|
-
<div className={"bg-stone-950 p-2 rounded-lg flex flex-col gap-1"}>
|
|
88
|
-
<p className={"text-lg font-bold"}>Action</p>
|
|
89
|
-
<Select options={[{value: "ask", label: "Ask"}, {value: "set", label: "Set"}, {value: "end", label: "End"}, {value: "none", label: "None"}]} value={action?.action ?? "none"} onChange={(v) => {
|
|
90
|
-
switch (v) {
|
|
91
|
-
case "ask":
|
|
92
|
-
setAction(prev => ({action: "ask", property: prev?.property, value: undefined}));
|
|
93
|
-
break;
|
|
94
|
-
case "end":
|
|
95
|
-
setAction({action: "end", property: undefined, value: undefined});
|
|
96
|
-
break;
|
|
97
|
-
case "set":
|
|
98
|
-
setAction(prev => ({action: "set", property: prev?.property, value: prev?.value}));
|
|
99
|
-
break;
|
|
100
|
-
case "none":
|
|
101
|
-
setAction(undefined);
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
}}/>
|
|
105
|
-
<Input className={clsx((action === undefined || action.action === "end") && "hidden")} type={"text"} label={"Property"} value={action?.property ?? ""} onChange={(e) => setAction(prev => prev ? ({...prev, property: e.target.value === "" ? undefined : e.target.value}) : undefined)}/>
|
|
106
|
-
<Input className={clsx((action === undefined || action.action !== "set") && "hidden")} type={"text"} label={"Value"} value={action?.value ?? ""} onChange={(e) => setAction(prev => prev ? ({...prev, value: e.target.value === "" ? undefined : e.target.value}) : undefined)}/>
|
|
107
|
-
<Input className={clsx((action === undefined || action.action !== "ask") && "hidden")} type={"text"} label={"Goto"} value={action?.goto ?? ""} onChange={(e) => setAction(prev => prev ? ({...prev, goto: e.target.value === "" ? undefined : e.target.value}) : undefined)}/>
|
|
108
|
-
</div>
|
|
109
|
-
<Input disabled={answers.length !== 0} type={"text"} label={"Goto"} value={goto ?? ""} onChange={(e) => setGoto(e.target.value === "" ? undefined : e.target.value)}/>
|
|
110
|
-
<div className={"grow"}/>
|
|
111
|
-
<Button onClick={() => {
|
|
112
|
-
props.deleteQuestion(question?.id ?? "")
|
|
113
|
-
setQuestion(null);
|
|
114
|
-
}}>Delete</Button>
|
|
115
|
-
</div>
|
|
116
|
-
</SideBar>
|
|
117
|
-
<div className={"p-2 fixed right-8 top-0 flex gap-2"}>
|
|
118
|
-
<Button onClick={() => props.addQuestion({x: 20, y: 20})}><BsPlus className={"w-6 h-6"}/></Button>
|
|
119
|
-
{props.save && <Button onClick={() => props.save && props.save(props.questions)}><LuSave className={"w-6 h-6"}/></Button>}
|
|
120
|
-
{props.load && <Button onClick={async () => {
|
|
121
|
-
if(props.load) {
|
|
122
|
-
const questions = await props.load();
|
|
123
|
-
props.setQuestions(questions);
|
|
124
|
-
props.setReloadArrow(prev => !prev);
|
|
125
|
-
}
|
|
126
|
-
}}><MdOutlineFileOpen className={"w-6 h-6"}/></Button>}
|
|
127
|
-
{props.export && <Button onClick={() => props.export && props.export(props.questions)}><BiDownload className={"w-6 h-6"}/></Button>}
|
|
128
|
-
{ props.import &&<Button onClick={async () => {
|
|
129
|
-
if(props.import) {
|
|
130
|
-
const questions = await props.import();
|
|
131
|
-
props.setQuestions(questions);
|
|
132
|
-
props.setReloadArrow(prev => !prev);
|
|
133
|
-
}
|
|
134
|
-
}}><BiUpload className={"w-6 h-6"}/></Button>}
|
|
135
|
-
</div>
|
|
136
|
-
</div>
|
|
137
|
-
)
|
|
138
|
-
}
|
package/src/index.ts
DELETED
package/src/main.tsx
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import {StrictMode} from 'react'
|
|
2
|
-
import {createRoot} from 'react-dom/client'
|
|
3
|
-
import './index.css'
|
|
4
|
-
import App from "./App";
|
|
5
|
-
import type TQuestion from "./TQuestion.ts";
|
|
6
|
-
import type { TAction, TAnswers } from "./TQuestion.ts";
|
|
7
|
-
import React from "react";
|
|
8
|
-
|
|
9
|
-
const save = (questions: TQuestion[]) => {
|
|
10
|
-
const save = questions.map(q => {
|
|
11
|
-
const pos = document.getElementById(q.id)!.getBoundingClientRect();
|
|
12
|
-
q.position.x = pos.x;
|
|
13
|
-
q.position.y = pos.y;
|
|
14
|
-
return q;
|
|
15
|
-
});
|
|
16
|
-
const file = URL.createObjectURL(new Blob([JSON.stringify(save)], {type: "application/json"}));
|
|
17
|
-
const a = document.createElement("a");
|
|
18
|
-
a.href = file;
|
|
19
|
-
a.download = "questions.json";
|
|
20
|
-
a.click();
|
|
21
|
-
URL.revokeObjectURL(file);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const download = (questions: TQuestion[]) => {
|
|
25
|
-
const result = {} as Record<string, {question: string | string[], goto?: string, answers: TAnswers[] | undefined, action?: TAction}>;
|
|
26
|
-
questions.forEach(q => result[q.id] = {
|
|
27
|
-
question: q.text,
|
|
28
|
-
goto: q.goto,
|
|
29
|
-
action: q.action,
|
|
30
|
-
answers: q.answers
|
|
31
|
-
});
|
|
32
|
-
const file = URL.createObjectURL(new Blob([JSON.stringify(result)], {type: "application/json"}));
|
|
33
|
-
const a = document.createElement("a");
|
|
34
|
-
a.href = file;
|
|
35
|
-
a.download = "questions.json";
|
|
36
|
-
a.click();
|
|
37
|
-
URL.revokeObjectURL(file);
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const load = async () => {
|
|
41
|
-
const fileLoader = document.createElement("input");
|
|
42
|
-
fileLoader.type = "file";
|
|
43
|
-
fileLoader.accept = ".json";
|
|
44
|
-
const p = new Promise<TQuestion[]>((resolve, reject) => {
|
|
45
|
-
fileLoader.addEventListener("change", () => {
|
|
46
|
-
if(fileLoader.files?.length === 1) {
|
|
47
|
-
fileLoader.files[0].text().then(v => {
|
|
48
|
-
const questions = JSON.parse(v);
|
|
49
|
-
if(typeof questions === "object" && questions instanceof Array) {
|
|
50
|
-
const validate = questions.filter(q => {
|
|
51
|
-
return typeof q["id"] === "string" && typeof q["text"] === "string" && typeof q["position"] === "object" && typeof q["position"]["x"] === "number" && typeof q["position"]["y"] === "number"
|
|
52
|
-
});
|
|
53
|
-
if(validate.length !== questions.length) {
|
|
54
|
-
console.error("Invalid file format");
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
resolve(questions as TQuestion[]);
|
|
58
|
-
} else {
|
|
59
|
-
reject("Invalid file format");
|
|
60
|
-
}
|
|
61
|
-
}).catch(reject);
|
|
62
|
-
} else {
|
|
63
|
-
reject("You must select only one file");
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
fileLoader.click();
|
|
68
|
-
return p;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const importFile = async () => {
|
|
72
|
-
const fileLoader = document.createElement("input");
|
|
73
|
-
fileLoader.type = "file";
|
|
74
|
-
fileLoader.accept = ".json";
|
|
75
|
-
const p = new Promise<TQuestion[]>((resolve, reject) => {
|
|
76
|
-
fileLoader.addEventListener("change", () => {
|
|
77
|
-
if (fileLoader.files?.length === 1) {
|
|
78
|
-
fileLoader.files[0].text().then(v => {
|
|
79
|
-
const questions = JSON.parse(v);
|
|
80
|
-
if (typeof questions === "object") {
|
|
81
|
-
const validate = Object.values<any>(questions).filter(q => {
|
|
82
|
-
return typeof q["question"] === "string" || (typeof q["question"] === "object" && q["question"] instanceof Array)
|
|
83
|
-
});
|
|
84
|
-
if (validate.length !== Object.values(questions).length) {
|
|
85
|
-
reject("Invalid file format");
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
const tmp = Object.keys(questions).map((id, index) => {
|
|
89
|
-
return {
|
|
90
|
-
id: id,
|
|
91
|
-
text: questions[id]["question"],
|
|
92
|
-
goto: questions[id]["goto"] ?? undefined,
|
|
93
|
-
answers: questions[id]["answers"] ?? [],
|
|
94
|
-
position: {x: index % 5 * 260 + 20, y: Math.floor(index / 5) * 300 + 20},
|
|
95
|
-
action: questions[id]["action"] ?? undefined
|
|
96
|
-
} as TQuestion;
|
|
97
|
-
});
|
|
98
|
-
resolve(tmp);
|
|
99
|
-
} else {
|
|
100
|
-
reject("Invalid file format");
|
|
101
|
-
}
|
|
102
|
-
}).catch(reject);
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
fileLoader.click();
|
|
107
|
-
return p;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
createRoot(document.getElementById('root')!).render(
|
|
111
|
-
<StrictMode>
|
|
112
|
-
<App save={save} load={load} export={download} import={importFile}/>
|
|
113
|
-
</StrictMode>,
|
|
114
|
-
)
|
package/src/utils.ts
DELETED
package/tsconfig.app.json
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
4
|
-
"target": "ES2022",
|
|
5
|
-
"useDefineForClassFields": true,
|
|
6
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
-
"module": "ESNext",
|
|
8
|
-
"types": ["vite/client"],
|
|
9
|
-
"skipLibCheck": true,
|
|
10
|
-
|
|
11
|
-
/* Bundler mode */
|
|
12
|
-
"moduleResolution": "bundler",
|
|
13
|
-
"allowImportingTsExtensions": true,
|
|
14
|
-
"verbatimModuleSyntax": true,
|
|
15
|
-
"moduleDetection": "force",
|
|
16
|
-
"noEmit": true,
|
|
17
|
-
"jsx": "react-jsx",
|
|
18
|
-
|
|
19
|
-
/* Linting */
|
|
20
|
-
"strict": true,
|
|
21
|
-
"noUnusedLocals": true,
|
|
22
|
-
"noUnusedParameters": true,
|
|
23
|
-
"erasableSyntaxOnly": true,
|
|
24
|
-
"noFallthroughCasesInSwitch": true,
|
|
25
|
-
"noUncheckedSideEffectImports": true
|
|
26
|
-
},
|
|
27
|
-
"include": ["src"]
|
|
28
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"include": ["src"],
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"target": "ES2022",
|
|
5
|
-
"module": "ESNext",
|
|
6
|
-
"declaration": true,
|
|
7
|
-
"sourceMap": true,
|
|
8
|
-
"outDir": "dist",
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"forceConsistentCasingInFileNames": true,
|
|
11
|
-
"strict": true,
|
|
12
|
-
"skipLibCheck": true,
|
|
13
|
-
"jsx": "react",
|
|
14
|
-
"moduleResolution": "bundler"
|
|
15
|
-
}
|
|
16
|
-
}
|
package/tsconfig.node.json
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
|
-
"target": "ES2023",
|
|
5
|
-
"lib": ["ES2023"],
|
|
6
|
-
"module": "ESNext",
|
|
7
|
-
"types": ["node"],
|
|
8
|
-
"skipLibCheck": true,
|
|
9
|
-
|
|
10
|
-
/* Bundler mode */
|
|
11
|
-
"moduleResolution": "bundler",
|
|
12
|
-
"allowImportingTsExtensions": true,
|
|
13
|
-
"verbatimModuleSyntax": true,
|
|
14
|
-
"moduleDetection": "force",
|
|
15
|
-
"noEmit": true,
|
|
16
|
-
|
|
17
|
-
/* Linting */
|
|
18
|
-
"strict": true,
|
|
19
|
-
"noUnusedLocals": true,
|
|
20
|
-
"noUnusedParameters": true,
|
|
21
|
-
"erasableSyntaxOnly": true,
|
|
22
|
-
"noFallthroughCasesInSwitch": true,
|
|
23
|
-
"noUncheckedSideEffectImports": true
|
|
24
|
-
},
|
|
25
|
-
"include": ["vite.config.ts"]
|
|
26
|
-
}
|