mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-24 10:39:28 -04:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4bbafeae6 | ||
|
|
e39a7f0360 | ||
|
|
5ea90154b1 | ||
|
|
f627da6d8f | ||
|
|
682f11c79e | ||
|
|
0bdbb5214b | ||
|
|
94f2d2ec68 | ||
|
|
384b841528 | ||
|
|
62f97317ff | ||
|
|
8210a9f063 |
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2.0.0-rc",
|
||||
"productName": "ArtCraft 2D",
|
||||
"version": "0.1.0",
|
||||
"identifier": "ai.artcraft.app",
|
||||
"build": {
|
||||
"frontendDist": "../../../frontend/apps/editor2d/dist",
|
||||
"devUrl": "http://localhost:5173",
|
||||
"beforeDevCommand": "nx run editor2d:dev",
|
||||
"beforeBuildCommand": "nx run editor2d:build"
|
||||
},
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "ArtCraft 2D",
|
||||
"width": 2400,
|
||||
"height": 1300,
|
||||
"resizable": true,
|
||||
"fullscreen": false
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2.0.0-rc",
|
||||
"productName": "ArtCraft 3D",
|
||||
"version": "0.1.0",
|
||||
"identifier": "ai.artcraft.app",
|
||||
"build": {
|
||||
"frontendDist": "../../../frontend/apps/editor3d/dist",
|
||||
"devUrl": "http://localhost:5173",
|
||||
"beforeDevCommand": "nx run editor3d:dev",
|
||||
"beforeBuildCommand": "nx run editor3d:build"
|
||||
},
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "ArtCraft 3D",
|
||||
"width": 2400,
|
||||
"height": 1300,
|
||||
"resizable": true,
|
||||
"fullscreen": false
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# For some reason, we can't pass json files to the tauri dev command
|
||||
[build]
|
||||
frontendDist = "../../../frontend/apps/editor3d/dist"
|
||||
beforeDevCommand = "nx run editor3d:dev"
|
||||
devUrl = "http://localhost:5173"
|
||||
@@ -4,10 +4,10 @@
|
||||
"version": "0.1.0",
|
||||
"identifier": "ai.artcraft.app",
|
||||
"build": {
|
||||
"frontendDist": "../../../frontend/apps/editor3d/dist",
|
||||
"devUrl": "http://localhost:5173",
|
||||
"beforeDevCommand": "nx run editor3d:dev",
|
||||
"beforeBuildCommand": "nx run editor3d:build"
|
||||
"frontendDist": "../../../frontend/apps/editor2d/dist",
|
||||
"devUrl": "http://localhost:5741",
|
||||
"beforeDevCommand": "nx run editor2d:dev",
|
||||
"beforeBuildCommand": "nx run editor2d:build"
|
||||
},
|
||||
"app": {
|
||||
"windows": [
|
||||
|
||||
+15
-4
@@ -2,8 +2,7 @@
|
||||
|
||||
This is an `nx` monorepo that can contain multiple apps and shared libraries.
|
||||
|
||||
All commands to run these projects are performed from _this_ directory.
|
||||
|
||||
All commands to run these projects are performed from _this_ directory.
|
||||
|
||||
## Install dependencies
|
||||
|
||||
@@ -35,6 +34,10 @@ The names for @frontend and @storyteller come from the package.json file in the
|
||||
import { Login } from "@frontend/login";
|
||||
import { api } from "@storyteller/api";
|
||||
|
||||
For shared UI components, import from @storyteller/ui-[componentname].
|
||||
import { Button } from "@storyteller/ui-button";
|
||||
import { Modal } from "@storyteller/ui-modal";
|
||||
|
||||
building:
|
||||
npx nx build editor3d
|
||||
npx nx build editor2d
|
||||
@@ -42,6 +45,14 @@ npx nx build editor2d
|
||||
dev:
|
||||
npx nx dev editor3d
|
||||
npx nx dev editor2d
|
||||
|
||||
|
||||
|
||||
# component build
|
||||
1.
|
||||
npx nx g @nx/react:library libs/components/toaster --import-path=@storyteller/ui-toaster --bundler=vite
|
||||
npm install
|
||||
2.import it first in code
|
||||
3.nx sync
|
||||
4.nx build
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
"@fortawesome/pro-regular-svg-icons": "^6.7.2",
|
||||
"@fortawesome/pro-solid-svg-icons": "^6.7.2",
|
||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||
"@headlessui/react": "^2.1.3",
|
||||
"@preact/signals-react": "^2.2.0",
|
||||
"@tauri-apps/api": "^2.2.0",
|
||||
"@techstark/opencv-js": "^4.10.0-release.1",
|
||||
|
||||
@@ -62,6 +62,7 @@ export class EditEngine {
|
||||
this.editMode = EditModes.SELECT;
|
||||
|
||||
this.boardCanvasRef = boardCanvasRef;
|
||||
|
||||
this.stage = new Konva.Stage({
|
||||
container: this.boardCanvasRef,
|
||||
width: window.innerWidth,
|
||||
@@ -238,13 +239,19 @@ export class EditEngine {
|
||||
}
|
||||
|
||||
private onBoardCanvasResize() {
|
||||
this.realTimeDrawEngine.updateCaptureCanvas(undefined, undefined);
|
||||
this.realTimeDrawEngine.updateCaptureCanvas(0, 0);
|
||||
console.log("onBoardCanvasResize Event called");
|
||||
console.log("Board Canvas values below");
|
||||
console.log(this.boardCanvasRef.offsetWidth);
|
||||
console.log(this.boardCanvasRef.offsetHeight);
|
||||
|
||||
this.matteBox.updateSize({
|
||||
boardCanvasSize: {
|
||||
width: this.boardCanvasRef.offsetWidth,
|
||||
height: this.boardCanvasRef.offsetHeight,
|
||||
},
|
||||
});
|
||||
|
||||
this.uiLayer.draw();
|
||||
this.stage.width(this.boardCanvasRef.offsetWidth);
|
||||
this.stage.height(this.boardCanvasRef.offsetHeight);
|
||||
|
||||
@@ -510,7 +510,20 @@ export class Engine {
|
||||
}
|
||||
|
||||
private onBoardCanvasResize() {
|
||||
this.realTimeDrawEngine.updateCaptureCanvas(undefined, undefined);
|
||||
console.log("onBoardCanvasResize Event called");
|
||||
// Print window dimensions for debugging
|
||||
console.log("Window dimensions:", {
|
||||
innerWidth: window.innerWidth,
|
||||
innerHeight: window.innerHeight,
|
||||
outerWidth: window.outerWidth,
|
||||
outerHeight: window.outerHeight,
|
||||
});
|
||||
|
||||
this.realTimeDrawEngine.updateCaptureCanvas(
|
||||
window.innerWidth,
|
||||
window.innerHeight,
|
||||
);
|
||||
|
||||
this.matteBox.updateSize({
|
||||
boardCanvasSize: {
|
||||
width: this.boardCanvasRef.offsetWidth,
|
||||
|
||||
@@ -516,36 +516,74 @@ export class RealTimeDrawEngine {
|
||||
width: number | undefined,
|
||||
height: number | undefined,
|
||||
) {
|
||||
console.log("updateCaptureCanvas");
|
||||
if (!this.captureCanvas) {
|
||||
return;
|
||||
}
|
||||
if (width) {
|
||||
this.width = width;
|
||||
}
|
||||
if (height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
// Ensures that all the nodes stag in the same place should
|
||||
// there be a window resize.
|
||||
// recompute the position
|
||||
// to ensure that the position of this stays
|
||||
|
||||
const scaleFactor = 0.9;
|
||||
// Store old values for calculating delta
|
||||
const oldPositionX = this.positionX;
|
||||
const oldPositionY = this.positionY;
|
||||
|
||||
// recompute the position
|
||||
const oldWidth = this.width;
|
||||
const oldHeight = this.height;
|
||||
|
||||
// Original aspect ratio
|
||||
const originalAspectRatio = oldWidth / oldHeight;
|
||||
|
||||
// Calculate new dimensions based on window size
|
||||
let newWidth: number;
|
||||
let newHeight: number;
|
||||
|
||||
if (width === undefined) {
|
||||
newWidth = window.innerWidth * scaleFactor;
|
||||
} else {
|
||||
newWidth = width * scaleFactor;
|
||||
}
|
||||
|
||||
if (height === undefined) {
|
||||
newHeight = window.innerHeight * scaleFactor;
|
||||
} else {
|
||||
newHeight = height * scaleFactor;
|
||||
}
|
||||
|
||||
// Preserve aspect ratio
|
||||
const newAspectRatio = newWidth / newHeight;
|
||||
|
||||
if (newAspectRatio > originalAspectRatio) {
|
||||
// Width is constraining factor
|
||||
newWidth = newHeight * originalAspectRatio;
|
||||
} else {
|
||||
// Height is constraining factor
|
||||
newHeight = newWidth / originalAspectRatio;
|
||||
}
|
||||
|
||||
this.width = newWidth;
|
||||
this.height = newHeight;
|
||||
|
||||
console.log("New dimensions:", this.width, this.height);
|
||||
|
||||
// Calculate new position to center the canvas in the window
|
||||
this.positionX = (window.innerWidth - this.width) / 2;
|
||||
this.positionY = (window.innerHeight - this.height) / 2;
|
||||
|
||||
console.log("New centered position:", this.positionX, this.positionY);
|
||||
|
||||
// Update the canvas position and size
|
||||
this.captureCanvas.setPosition({
|
||||
x: this.positionX / 2,
|
||||
y: this.positionY / 2,
|
||||
x: this.positionX,
|
||||
y: this.positionY,
|
||||
});
|
||||
this.captureCanvas.size({ width: this.width, height: this.height });
|
||||
|
||||
// this is the change in positions
|
||||
const deltaX = this.positionX - oldPositionX;
|
||||
const deltaY = this.positionY - oldPositionY;
|
||||
// Calculate scale factors to maintain proportions
|
||||
const scaleX = this.width / oldWidth;
|
||||
const scaleY = this.height / oldHeight;
|
||||
|
||||
// Use a uniform scale factor to preserve aspect ratio
|
||||
const uniformScale = Math.min(scaleX, scaleY);
|
||||
|
||||
// Update all children except the capture canvas
|
||||
var children = this.mediaLayerRef.getChildren();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let node = children[i];
|
||||
@@ -554,11 +592,27 @@ export class RealTimeDrawEngine {
|
||||
if (node.name() === "CaptureCanvas") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const pos = node.getPosition();
|
||||
const oldScale = node.scale();
|
||||
|
||||
// Apply both position shift and scaling to maintain relative positioning
|
||||
node.setPosition({
|
||||
x: pos.x + deltaX,
|
||||
y: pos.y + deltaY,
|
||||
x: (pos.x - oldPositionX) * uniformScale + this.positionX,
|
||||
y: (pos.y - oldPositionY) * uniformScale + this.positionY,
|
||||
});
|
||||
|
||||
// Update scale proportionally if the node has a scale property
|
||||
if (
|
||||
oldScale &&
|
||||
typeof oldScale.x === "number" &&
|
||||
typeof oldScale.y === "number"
|
||||
) {
|
||||
node.scale({
|
||||
x: oldScale.x * uniformScale,
|
||||
y: oldScale.y * uniformScale,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.mediaLayerRef.batchDraw();
|
||||
|
||||
@@ -2,6 +2,38 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #363636 #171717;
|
||||
}
|
||||
|
||||
/* Works on Chrome, Edge, and Safari */
|
||||
*::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
background: #171717;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background-color: #363636;
|
||||
border-radius: 4px;
|
||||
border: 2px solid #171717;
|
||||
}
|
||||
|
||||
/* Chrome, Safari, Edge, Opera */
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
@apply select-none bg-ui-background font-sans text-white;
|
||||
@@ -27,28 +59,7 @@
|
||||
background-size: 36px 36px;
|
||||
@apply bg-center;
|
||||
}
|
||||
h1 {
|
||||
@apply text-3xl font-medium md:text-5xl;
|
||||
}
|
||||
h2 {
|
||||
@apply text-2xl font-medium md:text-4xl;
|
||||
}
|
||||
h3 {
|
||||
@apply text-xl font-medium md:text-3xl;
|
||||
}
|
||||
h4 {
|
||||
@apply text-lg font-medium;
|
||||
}
|
||||
h5,
|
||||
label {
|
||||
@apply text-base;
|
||||
}
|
||||
h6 {
|
||||
@apply text-sm;
|
||||
}
|
||||
p {
|
||||
@apply text-sm font-normal;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply font-medium text-primary-400 hover:text-primary-300;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import { withProtectionRoute } from "~/components/hoc";
|
||||
import { useRenderCounter } from "~/hooks/useRenderCounter";
|
||||
import { Resource, invoke } from '@tauri-apps/api/core';
|
||||
|
||||
// Components of the page
|
||||
import { ToolbarUserProfile } from "~/components/features";
|
||||
@@ -26,6 +27,11 @@ import {
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
export const isTauri = (): boolean => {
|
||||
console.log(window.__TAURI_INTERNALS__);
|
||||
return window.__TAURI_INTERNALS__;
|
||||
};
|
||||
|
||||
export const Main = withProtectionRoute(() => {
|
||||
// This is a hook that will log the number of times the component has rerendered
|
||||
// Let's make sure we only log once
|
||||
@@ -40,7 +46,7 @@ export const Main = withProtectionRoute(() => {
|
||||
const realtimeEngineRef = useRef<EngineType | null>(null);
|
||||
const generationEngineRef = useRef<GenerationEngine | null>(null);
|
||||
const [firstTimeDialogOpen, setFirstTimeDialogOpen] = useState(true);
|
||||
|
||||
const [signinDialogOpen, setSigninDialogOpen] = useState(false);
|
||||
const appModeValue = appMode.value;
|
||||
let childView;
|
||||
|
||||
@@ -97,7 +103,7 @@ export const Main = withProtectionRoute(() => {
|
||||
}}
|
||||
className="max-w-5xl"
|
||||
>
|
||||
<div className="flex flex-col items-center justify-center gap-6">
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<h1 className="text-3xl font-bold">
|
||||
<FontAwesomeIcon
|
||||
@@ -123,7 +129,22 @@ export const Main = withProtectionRoute(() => {
|
||||
</video>
|
||||
</div>
|
||||
<Button
|
||||
className="px-4 py-3 font-semibold"
|
||||
className="font-semibold"
|
||||
icon={faArrowRight}
|
||||
iconFlip={true}
|
||||
onClick={async () => {
|
||||
if (isTauri()) {
|
||||
await invoke('open_login_command');
|
||||
setFirstTimeDialogOpen(false);
|
||||
} else {
|
||||
console.error('Tauri is not available in this environment');
|
||||
}
|
||||
}}
|
||||
>
|
||||
Signin to OpenAI to get Started
|
||||
</Button>
|
||||
{/* <Button
|
||||
className="font-semibold"
|
||||
icon={faArrowRight}
|
||||
iconFlip={true}
|
||||
onClick={() => {
|
||||
@@ -131,7 +152,7 @@ export const Main = withProtectionRoute(() => {
|
||||
}}
|
||||
>
|
||||
Start creating now
|
||||
</Button>
|
||||
</Button> */}
|
||||
</div>
|
||||
</BaseDialog>
|
||||
</>
|
||||
|
||||
@@ -177,7 +177,100 @@ export const PromptBox = () => {
|
||||
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setPrompt(e.target.value);
|
||||
};
|
||||
const handleTauriEnqueue = async () => {
|
||||
const api = new Api();
|
||||
let image = getCanvasRenderBitmap();
|
||||
if (image === undefined) {
|
||||
return;
|
||||
}
|
||||
const base64Bitmap = await EncodeImageBitmapToBase64(image);
|
||||
|
||||
const byteString = atob(base64Bitmap);
|
||||
const mimeString = "image/png";
|
||||
|
||||
const ab = new ArrayBuffer(byteString.length);
|
||||
const ia = new Uint8Array(ab);
|
||||
|
||||
for (let i = 0; i < byteString.length; i++) {
|
||||
ia[i] = byteString.charCodeAt(i);
|
||||
}
|
||||
|
||||
const uuid = crypto.randomUUID(); // Generate a new UUID
|
||||
const file = new File([ab], `${uuid}.png`, { type: mimeString });
|
||||
|
||||
const snapshotMediaToken = await api.uploadSceneSnapshot({
|
||||
screenshot: file,
|
||||
});
|
||||
|
||||
if (snapshotMediaToken.data === undefined) {
|
||||
toast.error("Error: Unable to upload scene snapshot Please try again.");
|
||||
return;
|
||||
}
|
||||
console.log("useSystemPrompt", useSystemPrompt);
|
||||
console.log("Snapshot media token:", snapshotMediaToken.data);
|
||||
|
||||
const generateResponse = await invoke("sora_image_generation_command", {
|
||||
snapshot_media_token: snapshotMediaToken.data,
|
||||
disable_system_prompt: !useSystemPrompt,
|
||||
prompt: prompt,
|
||||
maybe_additional_images: referenceImages.map((image) => image.mediaToken),
|
||||
maybe_number_of_samples: 1,
|
||||
});
|
||||
toast.success("Please wait while we process your image.");
|
||||
};
|
||||
|
||||
const handleWebEnqueue = async () => {
|
||||
const api = new Api();
|
||||
let image = getCanvasRenderBitmap();
|
||||
if (image === undefined) {
|
||||
toast.error(
|
||||
"Error: Unable to generate image. Please check the input and try again.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
const base64Bitmap = await EncodeImageBitmapToBase64(image);
|
||||
|
||||
const byteString = atob(base64Bitmap);
|
||||
const mimeString = "image/png";
|
||||
|
||||
const ab = new ArrayBuffer(byteString.length);
|
||||
const ia = new Uint8Array(ab);
|
||||
|
||||
for (let i = 0; i < byteString.length; i++) {
|
||||
ia[i] = byteString.charCodeAt(i);
|
||||
}
|
||||
|
||||
const uuid = crypto.randomUUID(); // Generate a new UUID
|
||||
const file = new File([ab], `${uuid}.png`, { type: mimeString });
|
||||
|
||||
const snapshotMediaToken = await api.uploadSceneSnapshot({
|
||||
screenshot: file,
|
||||
});
|
||||
|
||||
if (snapshotMediaToken.data === undefined) {
|
||||
toast.error("Error: Unable to upload scene snapshot Please try again.");
|
||||
return;
|
||||
}
|
||||
console.log("useSystemPrompt", useSystemPrompt);
|
||||
console.log("Snapshot media token:", snapshotMediaToken.data);
|
||||
|
||||
const response = await api.enqueueImageGeneration({
|
||||
disableSystemPrompt: !useSystemPrompt,
|
||||
prompt: prompt,
|
||||
snapshotMediaToken: snapshotMediaToken.data,
|
||||
additionalImages: referenceImages.map((image) => image.mediaToken),
|
||||
});
|
||||
|
||||
if (response.success === true) {
|
||||
toast.success("Please wait while we process your image.");
|
||||
if (response.data) {
|
||||
addJobToken(response.data);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
toast.error("Failed to enqueue image generation. Please try again.");
|
||||
}
|
||||
};
|
||||
const handleEnqueue = async () => {
|
||||
if (!prompt.trim()) return;
|
||||
|
||||
@@ -191,69 +284,10 @@ export const PromptBox = () => {
|
||||
referenceImages,
|
||||
);
|
||||
|
||||
if (isDesktopApp) {
|
||||
let image = getCanvasRenderBitmap();
|
||||
if (image === undefined) {
|
||||
return;
|
||||
}
|
||||
const base64Bitmap = await EncodeImageBitmapToBase64(image);
|
||||
const generateResponse = await invoke("image_generation_command", {
|
||||
image: base64Bitmap,
|
||||
prompt: prompt,
|
||||
});
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
}
|
||||
|
||||
if (!isDesktopApp) {
|
||||
const api = new Api();
|
||||
let image = getCanvasRenderBitmap();
|
||||
if (image === undefined) {
|
||||
toast.error(
|
||||
"Error: Unable to generate image. Please check the input and try again.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
const base64Bitmap = await EncodeImageBitmapToBase64(image);
|
||||
const byteString = atob(base64Bitmap);
|
||||
const mimeString = "image/png";
|
||||
const ab = new ArrayBuffer(byteString.length);
|
||||
const ia = new Uint8Array(ab);
|
||||
|
||||
for (let i = 0; i < byteString.length; i++) {
|
||||
ia[i] = byteString.charCodeAt(i);
|
||||
}
|
||||
|
||||
const uuid = crypto.randomUUID(); // Generate a new UUID
|
||||
const file = new File([ab], `${uuid}.png`, { type: mimeString });
|
||||
|
||||
const snapshotMediaToken = await api.uploadSceneSnapshot({
|
||||
screenshot: file,
|
||||
});
|
||||
|
||||
if (snapshotMediaToken.data === undefined) {
|
||||
toast.error(
|
||||
"Error: Unable to upload scene snapshot Please try again.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
console.log("useSystemPrompt", useSystemPrompt);
|
||||
console.log("Snapshot media token:", snapshotMediaToken.data);
|
||||
const response = await api.enqueueImageGeneration({
|
||||
disableSystemPrompt: !useSystemPrompt,
|
||||
prompt: prompt,
|
||||
snapshotMediaToken: snapshotMediaToken.data,
|
||||
additionalImages: referenceImages.map((image) => image.mediaToken),
|
||||
});
|
||||
|
||||
if (response.success === true) {
|
||||
toast.success("Please wait while we process your image.");
|
||||
if (response.data) {
|
||||
addJobToken(response.data);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
toast.error("Failed to enqueue image generation. Please try again.");
|
||||
}
|
||||
if (isDesktopApp == true) {
|
||||
handleTauriEnqueue();
|
||||
} else {
|
||||
handleWebEnqueue();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error during image generation:", error);
|
||||
|
||||
@@ -1,16 +1,32 @@
|
||||
import { faGear } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { Login } from "~/app/pages/login";
|
||||
import { Activity } from "~/components/ui/Activity/Activity";
|
||||
// import { paperWrapperStyles } from "~/components/styles";
|
||||
// import { faPlus, faQuestion } from "@fortawesome/pro-solid-svg-icons";
|
||||
// import { ToolbarButton } from "~/components/features/ToolbarButton";
|
||||
import { Button } from "@storyteller/ui-button";
|
||||
import { SettingsModal } from "@storyteller/ui-settings-modal";
|
||||
import { useState } from "react";
|
||||
|
||||
export const ToolbarTopRight = () => {
|
||||
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
|
||||
return (
|
||||
<div
|
||||
className={twMerge("relative z-50 flex h-fit w-fit items-center gap-2")}
|
||||
>
|
||||
<Activity />
|
||||
</div>
|
||||
<>
|
||||
<div
|
||||
className={twMerge("relative z-50 flex h-fit w-fit items-center gap-2")}
|
||||
>
|
||||
<Button
|
||||
icon={faGear}
|
||||
className="h-[42px] w-[42px] bg-[#5F5F68]/60 backdrop-blur-lg hover:bg-[#5F5F68]/90"
|
||||
onClick={() => setIsSettingsModalOpen(true)}
|
||||
/>
|
||||
<Activity />
|
||||
</div>
|
||||
|
||||
<SettingsModal
|
||||
isOpen={isSettingsModalOpen}
|
||||
onClose={() => setIsSettingsModalOpen(false)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,7 +4,11 @@ import defaultTheme from "tailwindcss/defaultTheme";
|
||||
import { storytellerColors } from "./tailwind.stcolors";
|
||||
|
||||
export default {
|
||||
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
"../../libs/components/**/*.{js,jsx,ts,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
@@ -44,14 +48,42 @@ export default {
|
||||
info: colors.sky[500],
|
||||
|
||||
// brand colors
|
||||
primary: storytellerColors.azureRadiance,
|
||||
secondary: storytellerColors.gunpowder,
|
||||
primary: {
|
||||
DEFAULT: "#2d81ff",
|
||||
50: "#eef6ff",
|
||||
100: "#d9ebff",
|
||||
200: "#bcdcff",
|
||||
300: "#8ec7ff",
|
||||
400: "#59a7ff",
|
||||
500: "#2d81ff",
|
||||
600: "#1b63f5",
|
||||
700: "#144ee1",
|
||||
800: "#173fb6",
|
||||
900: "#19398f",
|
||||
950: "#142457",
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: "#3E3E41",
|
||||
50: "#f6f6f6",
|
||||
100: "#e7e7e7",
|
||||
200: "#d1d1d1",
|
||||
300: "#b0b0b0",
|
||||
400: "#888888",
|
||||
500: "#6d6d6d",
|
||||
600: "#5d5d5d",
|
||||
700: "#4f4f4f",
|
||||
800: "#454545",
|
||||
900: "#3d3d3d",
|
||||
950: "#3E3E41",
|
||||
},
|
||||
|
||||
tertiary: storytellerColors.aquamarineBlue,
|
||||
|
||||
ui: {
|
||||
background: "#101014",
|
||||
panel: "#3E3E41",
|
||||
border: "#FFFFFF0D",
|
||||
"panel-border": "#3F3F3F",
|
||||
controls: "#3E3E41",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -24,5 +24,15 @@
|
||||
"@ApiManager/*": ["./src/Classes/ApiManager/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"]
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../libs/components/settings-modal/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/button/tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
{
|
||||
"path": "../../libs/components/settings-modal"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/button"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export const Input = React.forwardRef(
|
||||
id={id ? id : label ? kebabCase(label) : undefined}
|
||||
className={twMerge(
|
||||
"h-10 w-full rounded-lg bg-[#242424] px-3 py-2.5 text-white placeholder-white/50 outline-none",
|
||||
"border border-[#363636] transition-all duration-150 ease-in-out hover:border-brand-primary/60 focus:border-brand-primary focus:!outline-none",
|
||||
"hover:border-primary/60 focus:border-primary border border-[#363636] transition-all duration-150 ease-in-out focus:!outline-none",
|
||||
icon && "pl-10",
|
||||
isError && "outline-red focus:outline-red",
|
||||
inputClassName,
|
||||
|
||||
@@ -60,6 +60,7 @@ export const LoadingBar = ({
|
||||
|
||||
return (
|
||||
<Transition
|
||||
as="div"
|
||||
className={wrapperClassName}
|
||||
show={show}
|
||||
enter="transition-opacity duration-150"
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
import { Transition } from '@headlessui/react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import { Transition } from "@headlessui/react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { H4 } from '.';
|
||||
import { H4 } from ".";
|
||||
|
||||
interface LoadingDotsProps {
|
||||
className?: string;
|
||||
isShowing?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface LoadingDotsInnerProps {
|
||||
className?: string;
|
||||
isShowing?: boolean;
|
||||
type?: 'typing'|'bricks';
|
||||
type?: "typing" | "bricks";
|
||||
message?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const LoadingDotsTyping = (props: LoadingDotsProps)=>{
|
||||
return (
|
||||
<LoadingDots {...props} />
|
||||
)
|
||||
}
|
||||
export const LoadingDotsBricks = (props: LoadingDotsProps)=>{
|
||||
return (
|
||||
<LoadingDots {...props} type="bricks"/>
|
||||
)
|
||||
}
|
||||
export const LoadingDotsTyping = (props: LoadingDotsProps) => {
|
||||
return <LoadingDots {...props} />;
|
||||
};
|
||||
export const LoadingDotsBricks = (props: LoadingDotsProps) => {
|
||||
return <LoadingDots {...props} type="bricks" />;
|
||||
};
|
||||
|
||||
export function LoadingDots({
|
||||
className,
|
||||
isShowing=true,
|
||||
type = 'typing',
|
||||
isShowing = true,
|
||||
type = "typing",
|
||||
message,
|
||||
}: LoadingDotsInnerProps){
|
||||
const classNames = twMerge("w-full h-full flex flex-col justify-center items-center bg-ui-background gap-6", className);
|
||||
}: LoadingDotsInnerProps) {
|
||||
const classNames = twMerge(
|
||||
"w-full h-full flex flex-col justify-center items-center bg-ui-background gap-6",
|
||||
className,
|
||||
);
|
||||
|
||||
return(
|
||||
return (
|
||||
<Transition
|
||||
as="div"
|
||||
className={classNames}
|
||||
show={isShowing}
|
||||
enter="transition-opacity duration-150"
|
||||
@@ -45,17 +45,9 @@ export function LoadingDots({
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
{ type==='typing' &&
|
||||
<div className="dot-typing"></div>
|
||||
}
|
||||
{ type==='bricks' &&
|
||||
<div className="dot-bricks"></div>
|
||||
}
|
||||
{
|
||||
message &&
|
||||
<H4>{message}</H4>
|
||||
}
|
||||
{type === "typing" && <div className="dot-typing"></div>}
|
||||
{type === "bricks" && <div className="dot-bricks"></div>}
|
||||
{message && <H4>{message}</H4>}
|
||||
</Transition>
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
faChevronLeft,
|
||||
faChevronRight,
|
||||
faGear,
|
||||
faImages,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { useLocation, useParams } from "react-router-dom";
|
||||
@@ -10,7 +12,7 @@ import { SceneTitleInput } from "./SceneTitleInput";
|
||||
import { getCurrentLocationWithoutParams } from "~/utilities";
|
||||
import { Activity } from "~/pages/PageEnigma/comps/GenerateModals/Activity";
|
||||
import { LibraryModal } from "~/pages/PageEnigma/comps/LibraryModal/LibraryModal";
|
||||
import { useState } from "react";
|
||||
import { SettingsModal } from "@storyteller/ui-settings-modal";
|
||||
|
||||
function isEditorPath(path: string) {
|
||||
if (path === "/") return true;
|
||||
@@ -28,6 +30,7 @@ export const TopBar = ({ pageName }: Props) => {
|
||||
);
|
||||
const [isLibraryModalOpen, setIsLibraryModalOpen] = useState(false);
|
||||
const [activeLibraryTab, setActiveLibraryTab] = useState("my-media");
|
||||
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
|
||||
|
||||
const handleClick = () => {
|
||||
window.location.href = "https://storyteller-2d.netlify.app/";
|
||||
@@ -72,6 +75,12 @@ export const TopBar = ({ pageName }: Props) => {
|
||||
|
||||
<div className="flex justify-end gap-3.5">
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon={faGear}
|
||||
className="h-[38px] w-[38px]"
|
||||
onClick={() => setIsSettingsModalOpen(true)}
|
||||
/>
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon={faImages}
|
||||
@@ -79,6 +88,7 @@ export const TopBar = ({ pageName }: Props) => {
|
||||
>
|
||||
My Gallery
|
||||
</Button>
|
||||
|
||||
<Activity />
|
||||
</div>
|
||||
<div className="flex justify-end gap-2">
|
||||
@@ -88,6 +98,11 @@ export const TopBar = ({ pageName }: Props) => {
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<SettingsModal
|
||||
isOpen={isSettingsModalOpen}
|
||||
onClose={() => setIsSettingsModalOpen(false)}
|
||||
/>
|
||||
|
||||
<LibraryModal
|
||||
isOpen={isLibraryModalOpen}
|
||||
onClose={() => setIsLibraryModalOpen(false)}
|
||||
|
||||
@@ -59,14 +59,7 @@ class DndAsset {
|
||||
this.overElement = null;
|
||||
overTimeline.value = false;
|
||||
this.notDropText = "";
|
||||
if (reopenAfterDragSignal.value) {
|
||||
assetModalVisibleDuringDrag.value = true;
|
||||
} else {
|
||||
assetModalVisibleDuringDrag.value = true;
|
||||
setTimeout(() => {
|
||||
assetModalVisibleDuringDrag.value = false;
|
||||
}, 0);
|
||||
}
|
||||
assetModalVisibleDuringDrag.value = reopenAfterDragSignal.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ export const PageEditor = () => {
|
||||
onClick={handleOverlayClick}
|
||||
>
|
||||
<div
|
||||
className="absolute bottom-0 mb-2 ml-2 flex origin-bottom-left flex-col gap-2"
|
||||
className="absolute bottom-0 mb-4 ml-4 flex origin-bottom-left flex-col gap-2"
|
||||
style={{ transform: `scale(${getScale()})` }}
|
||||
>
|
||||
<Outliner />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TransitionDialogue } from "~/components/reusable/TransitionDialogue";
|
||||
import { Modal } from "@storyteller/ui-modal";
|
||||
import { UploadModal3D } from "~/components/reusable/UploadModal3D";
|
||||
import {
|
||||
faPlus,
|
||||
@@ -77,19 +77,21 @@ export const AssetModal = () => {
|
||||
const [activeLibraryTab, setActiveLibraryTab] = useState("library");
|
||||
const [activeAssetTab, setActiveAssetTab] = useState("all");
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [reopenAfterAdd, setReopenAfterAdd] = useState(false);
|
||||
const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);
|
||||
const searchInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const handleReopenChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = e.target.checked;
|
||||
// Force the update to be asynchronous
|
||||
setTimeout(() => {
|
||||
reopenAfterDragSignal.value = newValue;
|
||||
}, 0);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
assetModalVisible.value = false;
|
||||
};
|
||||
|
||||
// Update the signal when the preference changes
|
||||
useEffect(() => {
|
||||
reopenAfterDragSignal.value = reopenAfterAdd;
|
||||
}, [reopenAfterAdd]);
|
||||
|
||||
useEffect(() => {
|
||||
if (assetModalVisible.value) {
|
||||
// Check for stored category and tab from upload
|
||||
@@ -391,7 +393,7 @@ export const AssetModal = () => {
|
||||
};
|
||||
|
||||
const handleUploadSuccess = () => {
|
||||
if (reopenAfterAdd) {
|
||||
if (reopenAfterDragSignal.value) {
|
||||
// Small delay to allow the modal to close and reopen
|
||||
setTimeout(() => {
|
||||
handleClose();
|
||||
@@ -405,7 +407,7 @@ export const AssetModal = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<TransitionDialogue
|
||||
<Modal
|
||||
isOpen={assetModalVisible.value && assetModalVisibleDuringDrag.value}
|
||||
onClose={handleClose}
|
||||
className="h-[640px] max-w-4xl"
|
||||
@@ -448,19 +450,34 @@ export const AssetModal = () => {
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-auto flex items-center gap-2 pt-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="reopen-after-add"
|
||||
checked={reopenAfterAdd}
|
||||
onChange={(e) => setReopenAfterAdd(e.target.checked)}
|
||||
className="h-4 w-4 rounded-lg border-gray-300 bg-gray-700 text-brand-primary focus:ring-brand-primary"
|
||||
/>
|
||||
<label
|
||||
htmlFor="reopen-after-add"
|
||||
className="text-sm text-white/70"
|
||||
<div
|
||||
className="flex cursor-pointer items-center"
|
||||
onClick={(e) => {
|
||||
// Prevent double-firing when clicking the checkbox itself
|
||||
if (e.target instanceof HTMLInputElement) return;
|
||||
const checkbox = document.getElementById(
|
||||
"reopen-after-add",
|
||||
) as HTMLInputElement;
|
||||
if (checkbox) {
|
||||
checkbox.checked = !checkbox.checked;
|
||||
reopenAfterDragSignal.value = checkbox.checked;
|
||||
}
|
||||
}}
|
||||
>
|
||||
Reopen after adding
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="reopen-after-add"
|
||||
checked={reopenAfterDragSignal.value}
|
||||
onChange={handleReopenChange}
|
||||
className="h-4 w-4 cursor-pointer rounded-lg border-gray-300 bg-gray-700 text-brand-primary focus:ring-brand-primary"
|
||||
/>
|
||||
<label
|
||||
htmlFor="reopen-after-add"
|
||||
className="ml-2 cursor-pointer select-none text-sm text-white/70"
|
||||
>
|
||||
Reopen after adding
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-9 p-3 pb-0 ps-0 pt-2">
|
||||
@@ -520,7 +537,7 @@ export const AssetModal = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TransitionDialogue>
|
||||
</Modal>
|
||||
<UploadModal3D
|
||||
onClose={() => setIsUploadModalOpen(false)}
|
||||
onSuccess={handleUploadSuccess}
|
||||
|
||||
+4
-2
@@ -155,7 +155,7 @@ export const ControlPanelSceneObject = () => {
|
||||
updatedScaleValues.z = (parseFloat(localScale.z) + scale).toString();
|
||||
|
||||
handleScaleChange(updatedScaleValues);
|
||||
}
|
||||
};
|
||||
|
||||
const handleScaleChange = (xyz: Record<string, string>) => {
|
||||
if (isInvalid(xyz)) {
|
||||
@@ -180,9 +180,10 @@ export const ControlPanelSceneObject = () => {
|
||||
|
||||
return (
|
||||
<Transition
|
||||
as="div"
|
||||
show={isShowing.value}
|
||||
className={twMerge(
|
||||
"glass absolute bottom-0 right-0 mb-2 mr-2 flex h-fit w-56 origin-bottom-right flex-col gap-2 rounded-lg border border-ui-panel-border p-3.5 text-white shadow-lg",
|
||||
"glass absolute bottom-0 right-0 mb-4 mr-4 flex h-fit w-56 origin-bottom-right flex-col gap-2 rounded-lg border border-ui-panel-border p-3.5 text-white shadow-lg",
|
||||
)}
|
||||
enter="transition-opacity duration-150"
|
||||
enterFrom="opacity-0"
|
||||
@@ -208,6 +209,7 @@ export const ControlPanelSceneObject = () => {
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
as="div"
|
||||
show={!isCollapsed}
|
||||
enter="transition-all duration-200 ease-in-out"
|
||||
enterFrom="opacity-0 max-h-0"
|
||||
|
||||
@@ -179,6 +179,7 @@ export const Outliner = () => {
|
||||
|
||||
return (
|
||||
<Transition
|
||||
as="div"
|
||||
show={outlinerIsShowing.value}
|
||||
className={twMerge(
|
||||
"glass flex max-h-[34vh] w-[240px] origin-bottom-left flex-col overflow-hidden rounded-lg shadow-lg",
|
||||
|
||||
@@ -2,11 +2,7 @@ import React from "react";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { poseMode, showPoseControls } from "../signals/selectedMode";
|
||||
import { Button, Tooltip } from "~/components";
|
||||
import {
|
||||
faCheck,
|
||||
faCheckDouble,
|
||||
faPersonRunning,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { faCheck, faPersonRunning } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { SettingsModal } from "./SettingsModal";
|
||||
+7
-10
@@ -5,11 +5,7 @@ import { Badge } from "~/components";
|
||||
import { AssetType } from "~/enums";
|
||||
import { useState } from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faHandPointer } from "@fortawesome/pro-regular-svg-icons";
|
||||
import {
|
||||
faArrowPointer,
|
||||
faUpDownLeftRight,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { faUpDownLeftRight } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
interface Props {
|
||||
debug?: string;
|
||||
@@ -44,10 +40,7 @@ export const ItemElement = ({ item }: Props) => {
|
||||
const [imageError, setImageError] = useState(false);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="group relative w-full cursor-pointer select-none overflow-hidden transition-all duration-200"
|
||||
onPointerDown={(event) => DndAsset.onPointerDown(event, item)}
|
||||
>
|
||||
<div className="group relative w-full select-none overflow-hidden transition-all duration-200">
|
||||
{item.media_type && (
|
||||
<Badge
|
||||
label={
|
||||
@@ -61,7 +54,11 @@ export const ItemElement = ({ item }: Props) => {
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="pointer-events-none relative aspect-[16/12] w-full select-none overflow-hidden rounded-xl border-[3px] border-white/5 bg-brand-secondary-600 object-cover object-center transition-all group-hover:border-brand-primary">
|
||||
<div
|
||||
className="pointer-events-none relative aspect-[16/12] w-full select-none overflow-hidden rounded-xl border-[3px] border-white/5 bg-brand-secondary-600 object-cover object-center transition-all group-hover:border-brand-primary"
|
||||
onPointerDown={(event) => DndAsset.onPointerDown(event, item)}
|
||||
style={{ cursor: "grab", pointerEvents: "auto" }}
|
||||
>
|
||||
{item.thumbnail && !imageError && (
|
||||
<img
|
||||
crossOrigin="anonymous"
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
"@fortawesome/pro-regular-svg-icons": "^6.5.1",
|
||||
"@fortawesome/pro-solid-svg-icons": "^6.5.1",
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@headlessui/react": "^1.7.18",
|
||||
"@mediapipe/tasks-vision": "^0.10.20",
|
||||
"@netlify/edge-functions": "^2.3.1",
|
||||
"@netlify/remix-edge-adapter": "^3.2.0",
|
||||
|
||||
@@ -6,6 +6,7 @@ export default {
|
||||
"./app/index.html",
|
||||
"./app/src/**/*.{js,jsx,ts,tsx}",
|
||||
"./app/src/*.{js,jsx,ts,tsx}",
|
||||
"../../libs/components/**/*.{js,jsx,ts,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
@@ -25,7 +26,37 @@ export default {
|
||||
colors: {
|
||||
transparent: "transparent",
|
||||
current: "currentColor",
|
||||
|
||||
white: colors.white,
|
||||
gray: colors.gray,
|
||||
black: colors.black,
|
||||
primary: {
|
||||
DEFAULT: "#2d81ff",
|
||||
"50": "#eef6ff",
|
||||
"100": "#d9ebff",
|
||||
"200": "#bcdcff",
|
||||
"300": "#8ec7ff",
|
||||
"400": "#59a7ff",
|
||||
"500": "#2d81ff",
|
||||
"600": "#1b63f5",
|
||||
"700": "#144ee1",
|
||||
"800": "#173fb6",
|
||||
"900": "#19398f",
|
||||
"950": "#142457",
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: "#3E3E41",
|
||||
"50": "#f6f6f6",
|
||||
"100": "#e7e7e7",
|
||||
"200": "#d1d1d1",
|
||||
"300": "#b0b0b0",
|
||||
"400": "#888888",
|
||||
"500": "#6d6d6d",
|
||||
"600": "#5d5d5d",
|
||||
"700": "#4f4f4f",
|
||||
"800": "#454545",
|
||||
"900": "#3d3d3d",
|
||||
"950": "#3E3E41",
|
||||
},
|
||||
"brand-primary": {
|
||||
//sunglo
|
||||
DEFAULT: "#2d81ff", //=500
|
||||
@@ -85,7 +116,6 @@ export default {
|
||||
"900": "#64373f",
|
||||
"950": "#361b1e",
|
||||
},
|
||||
|
||||
success: {
|
||||
//apple
|
||||
DEFAULT: "#40ad48", //=500
|
||||
@@ -101,7 +131,6 @@ export default {
|
||||
"900": "#1f4823",
|
||||
"950": "#0c270f",
|
||||
},
|
||||
|
||||
action: {
|
||||
DEFAULT: "#3E3E41",
|
||||
"50": "#f6f6f6",
|
||||
@@ -116,7 +145,6 @@ export default {
|
||||
"900": "#3d3d3d",
|
||||
"950": "#3E3E41",
|
||||
},
|
||||
|
||||
character: {
|
||||
unselected: "#46527C",
|
||||
selected: "#6384F4",
|
||||
@@ -169,7 +197,6 @@ export default {
|
||||
cannotDrop: "#904948",
|
||||
wrapper: "#39394d",
|
||||
},
|
||||
|
||||
ui: {
|
||||
background: "#242424",
|
||||
panel: "#1F1F1F",
|
||||
@@ -178,7 +205,6 @@ export default {
|
||||
"controls-button": "#3E3E41",
|
||||
divider: "#515168",
|
||||
},
|
||||
|
||||
media: {
|
||||
"audio-tts": "#6AAA88",
|
||||
"audio-v2v": "#6691B9",
|
||||
@@ -186,29 +212,22 @@ export default {
|
||||
"audio-demo": "#B96675",
|
||||
"is-new": "#FFDC26",
|
||||
},
|
||||
|
||||
inference: {
|
||||
pending: "#6b728b",
|
||||
generating: "#46527C",
|
||||
error: "#924C4B",
|
||||
},
|
||||
|
||||
"axis-x": "#D33242",
|
||||
"axis-y": "#308752",
|
||||
"axis-z": "#2E70FF",
|
||||
red: "#D33242",
|
||||
green: "#308752",
|
||||
blue: "#2E70FF",
|
||||
|
||||
facebook: "#4267b2",
|
||||
reddit: "#ff5700",
|
||||
whatsapp: "#25d366",
|
||||
x: "#000000",
|
||||
email: "#858585",
|
||||
|
||||
white: colors.white,
|
||||
gray: colors.gray,
|
||||
black: colors.black,
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
"references": [
|
||||
{
|
||||
"path": "../../libs/components/toaster"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/settings-modal"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# @storyteller/ui-button
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test @storyteller/ui-button` to execute the unit tests via [Vitest](https://vitest.dev/).
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@storyteller/ui-button",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"development": "./src/index.ts",
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
export * from './lib/button';
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Replace this with your own classes
|
||||
*
|
||||
* e.g.
|
||||
* .container {
|
||||
* }
|
||||
*/
|
||||
@@ -0,0 +1,12 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import Button from './button';
|
||||
|
||||
describe('Button', () => {
|
||||
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<Button />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,108 @@
|
||||
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { ButtonHTMLAttributes } from "react";
|
||||
import { faSpinnerThird } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
icon?: IconDefinition;
|
||||
iconClassName?: string;
|
||||
iconFlip?: boolean;
|
||||
htmlFor?: string;
|
||||
variant?: "primary" | "secondary" | "action";
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
export const Button = ({
|
||||
icon,
|
||||
iconClassName,
|
||||
children,
|
||||
className: propsClassName,
|
||||
htmlFor,
|
||||
variant: propsVariant = "primary",
|
||||
disabled,
|
||||
iconFlip = false,
|
||||
loading,
|
||||
...rest
|
||||
}: ButtonProps) => {
|
||||
function getVariantClassNames(variant: string) {
|
||||
switch (variant) {
|
||||
case "secondary": {
|
||||
return "bg-secondary hover:bg-secondary-700 text-white focus-visible:outline-secondary";
|
||||
}
|
||||
case "action": {
|
||||
return "bg-action hover:bg-action-900 text-white focus-visible:outline-action";
|
||||
}
|
||||
case "primary":
|
||||
default: {
|
||||
return "bg-primary hover:bg-primary-400 text-white focus-visible:outline-primary-600";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const disabledClass = twMerge(
|
||||
disabled || loading ? "opacity-50 pointer-events-none" : ""
|
||||
);
|
||||
|
||||
const className = twMerge(
|
||||
"text-sm font-medium rounded-lg px-3 py-2 border border-transparent shadow-sm focus-visible:outline focus-visible:outline-0 focus-visible:outline-offset-0 transition-all duration-150 flex gap-2 items-center justify-center",
|
||||
getVariantClassNames(propsVariant),
|
||||
propsClassName,
|
||||
disabledClass
|
||||
);
|
||||
|
||||
if (htmlFor) {
|
||||
return (
|
||||
<label className={className} htmlFor={htmlFor} style={rest.style}>
|
||||
{loading && !iconFlip ? (
|
||||
<FontAwesomeIcon icon={faSpinnerThird} className="animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
{icon && !iconFlip ? (
|
||||
<FontAwesomeIcon icon={icon} className={iconClassName} />
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
{children}
|
||||
{loading && iconFlip ? (
|
||||
<FontAwesomeIcon icon={faSpinnerThird} className="animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
{icon && iconFlip ? (
|
||||
<FontAwesomeIcon icon={icon} className={iconClassName} />
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</label>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<button
|
||||
className={className}
|
||||
disabled={disabled || loading}
|
||||
{...{ ...rest, htmlFor }}
|
||||
>
|
||||
{loading && !iconFlip ? (
|
||||
<FontAwesomeIcon icon={faSpinnerThird} className="animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
{icon && !iconFlip ? (
|
||||
<FontAwesomeIcon icon={icon} className={iconClassName} />
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
{children}
|
||||
{loading && iconFlip ? (
|
||||
<FontAwesomeIcon icon={faSpinnerThird} className="animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
{icon && iconFlip ? (
|
||||
<FontAwesomeIcon icon={icon} className={iconClassName} />
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default Button;
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../../tsconfig.base.json"
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"types": [
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts",
|
||||
"vite/client"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"jsx": "react-jsx",
|
||||
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo"
|
||||
},
|
||||
"exclude": [
|
||||
"out-tsc",
|
||||
"dist",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx",
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.js",
|
||||
"src/**/*.jsx",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/vitest",
|
||||
"types": [
|
||||
"vitest/globals",
|
||||
"vitest/importMeta",
|
||||
"vite/client",
|
||||
"node",
|
||||
"vitest"
|
||||
],
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import * as path from 'path';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
root: __dirname,
|
||||
cacheDir: '../../../node_modules/.vite/libs/components/button',
|
||||
plugins: [react(), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') })],
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
// Configuration for building your library.
|
||||
// See: https://vitejs.dev/guide/build.html#library-mode
|
||||
build: {
|
||||
outDir: './dist',
|
||||
emptyOutDir: true,
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
lib: {
|
||||
// Could also be a dictionary or array of multiple entry points.
|
||||
entry: 'src/index.ts',
|
||||
name: '@storyteller/ui-button',
|
||||
fileName: 'index',
|
||||
// Change this to the formats you want to support.
|
||||
// Don't forget to update your package.json as well.
|
||||
formats: ['es' as const]
|
||||
},
|
||||
rollupOptions: {
|
||||
// External packages that should not be bundled into your library.
|
||||
external: ['react','react-dom','react/jsx-runtime']
|
||||
},
|
||||
},
|
||||
test: {
|
||||
watch: false,
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: './test-output/vitest/coverage',
|
||||
provider: 'v8' as const,
|
||||
}
|
||||
},
|
||||
}));
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# @storyteller/ui-close-button
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test @storyteller/ui-close-button` to execute the unit tests via [Vitest](https://vitest.dev/).
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@storyteller/ui-close-button",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"development": "./src/index.ts",
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
export * from './lib/close-button';
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Replace this with your own classes
|
||||
*
|
||||
* e.g.
|
||||
* .container {
|
||||
* }
|
||||
*/
|
||||
@@ -0,0 +1,12 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import CloseButton from './close-button';
|
||||
|
||||
describe('CloseButton', () => {
|
||||
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<CloseButton />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import { faXmark } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
interface CloseButtonProps {
|
||||
onClick: () => void;
|
||||
className?: string;
|
||||
size?: "sm" | "md" | "lg";
|
||||
}
|
||||
|
||||
export const CloseButton = ({
|
||||
onClick,
|
||||
className,
|
||||
size = "md",
|
||||
}: CloseButtonProps) => {
|
||||
const sizeClasses = {
|
||||
sm: "h-5 w-5 text-sm",
|
||||
md: "h-7 w-7 text-md",
|
||||
lg: "h-9 w-9 text-xl",
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={twMerge(
|
||||
"flex items-center justify-center rounded-full bg-black/40 text-white/60 transition-all hover:bg-black/70 hover:text-white",
|
||||
sizeClasses[size],
|
||||
className
|
||||
)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faXmark} />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default CloseButton;
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../../tsconfig.base.json"
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"types": [
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts",
|
||||
"vite/client"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"jsx": "react-jsx",
|
||||
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo"
|
||||
},
|
||||
"exclude": [
|
||||
"out-tsc",
|
||||
"dist",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx",
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.js",
|
||||
"src/**/*.jsx",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/vitest",
|
||||
"types": [
|
||||
"vitest/globals",
|
||||
"vitest/importMeta",
|
||||
"vite/client",
|
||||
"node",
|
||||
"vitest"
|
||||
],
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import * as path from 'path';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
root: __dirname,
|
||||
cacheDir: '../../../node_modules/.vite/libs/components/close-button',
|
||||
plugins: [react(), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') })],
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
// Configuration for building your library.
|
||||
// See: https://vitejs.dev/guide/build.html#library-mode
|
||||
build: {
|
||||
outDir: './dist',
|
||||
emptyOutDir: true,
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
lib: {
|
||||
// Could also be a dictionary or array of multiple entry points.
|
||||
entry: 'src/index.ts',
|
||||
name: '@storyteller/ui-close-button',
|
||||
fileName: 'index',
|
||||
// Change this to the formats you want to support.
|
||||
// Don't forget to update your package.json as well.
|
||||
formats: ['es' as const]
|
||||
},
|
||||
rollupOptions: {
|
||||
// External packages that should not be bundled into your library.
|
||||
external: ['react','react-dom','react/jsx-runtime']
|
||||
},
|
||||
},
|
||||
test: {
|
||||
watch: false,
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: './test-output/vitest/coverage',
|
||||
provider: 'v8' as const,
|
||||
}
|
||||
},
|
||||
}));
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# @storyteller/ui-input
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test @storyteller/ui-input` to execute the unit tests via [Vitest](https://vitest.dev/).
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@storyteller/ui-input",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"development": "./src/index.ts",
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
export * from './lib/input';
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Replace this with your own classes
|
||||
*
|
||||
* e.g.
|
||||
* .container {
|
||||
* }
|
||||
*/
|
||||
@@ -0,0 +1,12 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import Input from './input';
|
||||
|
||||
describe('Input', () => {
|
||||
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<Input />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
import React from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { IconDefinition } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Label } from "@storyteller/ui-label";
|
||||
|
||||
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
inputClassName?: string;
|
||||
iconClassName?: string;
|
||||
label?: string;
|
||||
icon?: IconDefinition;
|
||||
isError?: boolean;
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
export const Input = React.forwardRef(
|
||||
(
|
||||
{
|
||||
label,
|
||||
icon,
|
||||
inputClassName,
|
||||
iconClassName,
|
||||
className,
|
||||
id,
|
||||
isError,
|
||||
onBlur,
|
||||
onFocus,
|
||||
errorMessage,
|
||||
...rest
|
||||
}: InputProps,
|
||||
ref: React.ForwardedRef<HTMLInputElement>
|
||||
) => {
|
||||
return (
|
||||
<div className={twMerge("flex flex-col", className)}>
|
||||
{label && <Label htmlFor={id ? id : label}>{label}</Label>}
|
||||
|
||||
<div className="relative w-full">
|
||||
{icon && (
|
||||
<FontAwesomeIcon
|
||||
icon={icon}
|
||||
className={twMerge("text-md absolute pl-3 pt-3", iconClassName)}
|
||||
/>
|
||||
)}
|
||||
<input
|
||||
ref={ref}
|
||||
id={id ? id : label ? label : undefined}
|
||||
className={twMerge(
|
||||
"h-10 w-full rounded-lg bg-[#242424] px-3 py-2.5 text-white placeholder-white/50 outline-none",
|
||||
"border border-[#363636] transition-all duration-150 ease-in-out hover:border-brand-primary/60 focus:border-brand-primary focus:!outline-none",
|
||||
icon && "pl-10",
|
||||
isError && "outline-red focus:outline-red",
|
||||
inputClassName
|
||||
)}
|
||||
onFocus={(e: React.FocusEvent<HTMLInputElement>) => {
|
||||
if (onFocus) {
|
||||
onFocus(e);
|
||||
}
|
||||
}}
|
||||
onBlur={(e: React.FocusEvent<HTMLInputElement>) => {
|
||||
if (onBlur) {
|
||||
onBlur(e);
|
||||
}
|
||||
}}
|
||||
{...rest}
|
||||
/>
|
||||
{errorMessage && (
|
||||
<h6 className="absolute z-10 text-red">{errorMessage}</h6>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Input.displayName = "Input";
|
||||
|
||||
export default Input;
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "../label"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../../tsconfig.base.json"
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"types": [
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts",
|
||||
"vite/client"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"jsx": "react-jsx",
|
||||
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo"
|
||||
},
|
||||
"exclude": [
|
||||
"out-tsc",
|
||||
"dist",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx",
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.js",
|
||||
"src/**/*.jsx",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../label/tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/vitest",
|
||||
"types": [
|
||||
"vitest/globals",
|
||||
"vitest/importMeta",
|
||||
"vite/client",
|
||||
"node",
|
||||
"vitest"
|
||||
],
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import * as path from 'path';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
root: __dirname,
|
||||
cacheDir: '../../../node_modules/.vite/libs/components/input',
|
||||
plugins: [react(), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') })],
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
// Configuration for building your library.
|
||||
// See: https://vitejs.dev/guide/build.html#library-mode
|
||||
build: {
|
||||
outDir: './dist',
|
||||
emptyOutDir: true,
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
lib: {
|
||||
// Could also be a dictionary or array of multiple entry points.
|
||||
entry: 'src/index.ts',
|
||||
name: '@storyteller/ui-input',
|
||||
fileName: 'index',
|
||||
// Change this to the formats you want to support.
|
||||
// Don't forget to update your package.json as well.
|
||||
formats: ['es' as const]
|
||||
},
|
||||
rollupOptions: {
|
||||
// External packages that should not be bundled into your library.
|
||||
external: ['react','react-dom','react/jsx-runtime']
|
||||
},
|
||||
},
|
||||
test: {
|
||||
watch: false,
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: './test-output/vitest/coverage',
|
||||
provider: 'v8' as const,
|
||||
}
|
||||
},
|
||||
}));
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# @storyteller/ui-label
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test @storyteller/ui-label` to execute the unit tests via [Vitest](https://vitest.dev/).
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@storyteller/ui-label",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"development": "./src/index.ts",
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
export * from './lib/label';
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Replace this with your own classes
|
||||
*
|
||||
* e.g.
|
||||
* .container {
|
||||
* }
|
||||
*/
|
||||
@@ -0,0 +1,12 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import Label from './label';
|
||||
|
||||
describe('Label', () => {
|
||||
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<Label />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
import { LabelHTMLAttributes, ReactNode } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement> {
|
||||
className?: string;
|
||||
children: ReactNode;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
export const Label = ({
|
||||
className,
|
||||
children,
|
||||
required,
|
||||
...rest
|
||||
}: LabelProps) => (
|
||||
<label
|
||||
className={twMerge("text-white mb-2 text-[15px] font-medium", className)}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
{required && <span className="ml-0.5 text-[#ff6467]">*</span>}
|
||||
</label>
|
||||
);
|
||||
|
||||
export default Label;
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../../tsconfig.base.json"
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"types": [
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts",
|
||||
"vite/client"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"jsx": "react-jsx",
|
||||
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo"
|
||||
},
|
||||
"exclude": [
|
||||
"out-tsc",
|
||||
"dist",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx",
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.js",
|
||||
"src/**/*.jsx",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/vitest",
|
||||
"types": [
|
||||
"vitest/globals",
|
||||
"vitest/importMeta",
|
||||
"vite/client",
|
||||
"node",
|
||||
"vitest"
|
||||
],
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import * as path from 'path';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
root: __dirname,
|
||||
cacheDir: '../../../node_modules/.vite/libs/components/label',
|
||||
plugins: [react(), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') })],
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
// Configuration for building your library.
|
||||
// See: https://vitejs.dev/guide/build.html#library-mode
|
||||
build: {
|
||||
outDir: './dist',
|
||||
emptyOutDir: true,
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
lib: {
|
||||
// Could also be a dictionary or array of multiple entry points.
|
||||
entry: 'src/index.ts',
|
||||
name: '@storyteller/ui-label',
|
||||
fileName: 'index',
|
||||
// Change this to the formats you want to support.
|
||||
// Don't forget to update your package.json as well.
|
||||
formats: ['es' as const]
|
||||
},
|
||||
rollupOptions: {
|
||||
// External packages that should not be bundled into your library.
|
||||
external: ['react','react-dom','react/jsx-runtime']
|
||||
},
|
||||
},
|
||||
test: {
|
||||
watch: false,
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: './test-output/vitest/coverage',
|
||||
provider: 'v8' as const,
|
||||
}
|
||||
},
|
||||
}));
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# @storyteller/ui-modal
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test @storyteller/ui-modal` to execute the unit tests via [Vitest](https://vitest.dev/).
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@storyteller/ui-modal",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"development": "./src/index.ts",
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
export * from './lib/modal';
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Replace this with your own classes
|
||||
*
|
||||
* e.g.
|
||||
* .container {
|
||||
* }
|
||||
*/
|
||||
@@ -0,0 +1,12 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import Modal from './modal';
|
||||
|
||||
describe('Modal', () => {
|
||||
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<Modal />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,138 @@
|
||||
import { Fragment, ReactNode } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogPanel,
|
||||
DialogTitle,
|
||||
Transition,
|
||||
TransitionChild,
|
||||
} from "@headlessui/react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { IconDefinition } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { CloseButton } from "@storyteller/ui-close-button";
|
||||
|
||||
const DialogBackdrop = ({ className }: { className?: string }) => {
|
||||
return (
|
||||
<TransitionChild
|
||||
as="div"
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-100"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div
|
||||
className={twMerge("fixed inset-0 bg-black/60", className)}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
/>
|
||||
</TransitionChild>
|
||||
);
|
||||
};
|
||||
|
||||
export const Modal = ({
|
||||
isOpen,
|
||||
title,
|
||||
titleIcon,
|
||||
onTitleIconClick,
|
||||
onClose,
|
||||
className,
|
||||
backdropClassName,
|
||||
width,
|
||||
children,
|
||||
childPadding = true,
|
||||
titleIconClassName,
|
||||
showClose = true,
|
||||
}: {
|
||||
isOpen: boolean;
|
||||
title?: ReactNode;
|
||||
titleIcon?: IconDefinition;
|
||||
onTitleIconClick?: () => void;
|
||||
titleIconClassName?: string;
|
||||
onClose: () => void;
|
||||
className?: string;
|
||||
backdropClassName?: string;
|
||||
width?: number;
|
||||
children: ReactNode;
|
||||
childPadding?: boolean;
|
||||
showClose?: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-[70]" onClose={onClose}>
|
||||
<DialogBackdrop className={backdropClassName} />
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<TransitionChild
|
||||
as="div"
|
||||
enter="ease-out duration-200"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
className={twMerge(
|
||||
"w-full max-w-lg transform rounded-xl relative",
|
||||
"border border-ui-panel-border bg-[#2C2C2C]",
|
||||
"text-left align-middle shadow-xl transition-all",
|
||||
childPadding ? "p-4" : "",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<DialogPanel
|
||||
className="w-full relative"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{title && (
|
||||
<DialogTitle
|
||||
as="div"
|
||||
className="mb-5 flex justify-between pb-0 text-xl font-bold text-white"
|
||||
>
|
||||
<>
|
||||
{onTitleIconClick ? (
|
||||
<button
|
||||
className="flex items-center gap-3"
|
||||
onClick={onTitleIconClick}
|
||||
>
|
||||
{titleIcon && (
|
||||
<FontAwesomeIcon
|
||||
icon={titleIcon}
|
||||
className={titleIconClassName}
|
||||
/>
|
||||
)}
|
||||
{title}
|
||||
</button>
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
{titleIcon && (
|
||||
<FontAwesomeIcon
|
||||
icon={titleIcon}
|
||||
className={titleIconClassName}
|
||||
/>
|
||||
)}
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</DialogTitle>
|
||||
)}
|
||||
<div className={`h-full`.trim()}>{children}</div>
|
||||
</DialogPanel>
|
||||
{showClose && (
|
||||
<div className="absolute top-0 right-0 p-2.5">
|
||||
<CloseButton onClick={onClose} />
|
||||
</div>
|
||||
)}
|
||||
</TransitionChild>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "../close-button"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../../tsconfig.base.json"
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"types": [
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts",
|
||||
"vite/client"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"jsx": "react-jsx",
|
||||
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo"
|
||||
},
|
||||
"exclude": [
|
||||
"out-tsc",
|
||||
"dist",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx",
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.js",
|
||||
"src/**/*.jsx",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../close-button/tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/vitest",
|
||||
"types": [
|
||||
"vitest/globals",
|
||||
"vitest/importMeta",
|
||||
"vite/client",
|
||||
"node",
|
||||
"vitest"
|
||||
],
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import * as path from 'path';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
root: __dirname,
|
||||
cacheDir: '../../../node_modules/.vite/libs/components/modal',
|
||||
plugins: [react(), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') })],
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
// Configuration for building your library.
|
||||
// See: https://vitejs.dev/guide/build.html#library-mode
|
||||
build: {
|
||||
outDir: './dist',
|
||||
emptyOutDir: true,
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
lib: {
|
||||
// Could also be a dictionary or array of multiple entry points.
|
||||
entry: 'src/index.ts',
|
||||
name: '@storyteller/ui-modal',
|
||||
fileName: 'index',
|
||||
// Change this to the formats you want to support.
|
||||
// Don't forget to update your package.json as well.
|
||||
formats: ['es' as const]
|
||||
},
|
||||
rollupOptions: {
|
||||
// External packages that should not be bundled into your library.
|
||||
external: ['react','react-dom','react/jsx-runtime']
|
||||
},
|
||||
},
|
||||
test: {
|
||||
watch: false,
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: './test-output/vitest/coverage',
|
||||
provider: 'v8' as const,
|
||||
}
|
||||
},
|
||||
}));
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# @storyteller/ui-select
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test @storyteller/ui-select` to execute the unit tests via [Vitest](https://vitest.dev/).
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@storyteller/ui-select",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"development": "./src/index.ts",
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
export * from './lib/select';
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Replace this with your own classes
|
||||
*
|
||||
* e.g.
|
||||
* .container {
|
||||
* }
|
||||
*/
|
||||
@@ -0,0 +1,12 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import Select from './select';
|
||||
|
||||
describe('Select', () => {
|
||||
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<Select />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,104 @@
|
||||
import {
|
||||
Listbox,
|
||||
ListboxButton,
|
||||
ListboxOptions,
|
||||
ListboxOption,
|
||||
Transition,
|
||||
} from "@headlessui/react";
|
||||
import { faCheck, faChevronDown } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
export type SelectValue = string | number;
|
||||
export type SelectOption = { label: string; value: SelectValue };
|
||||
|
||||
interface ListDropdownProps {
|
||||
options: SelectOption[];
|
||||
onChange: (val: SelectValue) => void;
|
||||
placeholder?: string;
|
||||
value?: SelectValue;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export const Select = ({
|
||||
onChange,
|
||||
options,
|
||||
placeholder,
|
||||
value,
|
||||
id,
|
||||
}: ListDropdownProps) => {
|
||||
const selectedOption = options.find(
|
||||
(option: SelectOption) => option.value === value
|
||||
) || { label: placeholder || "", value: "" };
|
||||
|
||||
return (
|
||||
<div className="relative mt-1">
|
||||
<Listbox value={value || ""} onChange={onChange}>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<ListboxButton
|
||||
id={id}
|
||||
className="relative h-10 w-full cursor-pointer rounded-md bg-secondary py-2 pl-3 pr-10 text-left outline-none outline-offset-0 transition-all duration-150 ease-in-out sm:text-sm focus:!outline-none hover:bg-secondary hover:[filter:brightness(1.2)]"
|
||||
>
|
||||
<span
|
||||
className={twMerge("block truncate", !value && "opacity-50")}
|
||||
>
|
||||
{selectedOption.label}
|
||||
</span>
|
||||
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2.5">
|
||||
<FontAwesomeIcon icon={faChevronDown} aria-hidden="true" />
|
||||
</span>
|
||||
</ListboxButton>
|
||||
|
||||
<Transition
|
||||
as="div"
|
||||
show={open}
|
||||
leave="transition ease-in duration-[50ms]"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<ListboxOptions className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-secondary py-1.5 text-base shadow-xl focus:outline-none sm:text-sm">
|
||||
{options.map((option, itemIdx) => (
|
||||
<ListboxOption
|
||||
key={itemIdx}
|
||||
className={({ focus, selected }) =>
|
||||
twMerge(
|
||||
"relative cursor-pointer select-none py-2 pl-7 pr-2 text-white transition-all duration-150 ease-in-out",
|
||||
focus && "bg-white/[8%] text-white",
|
||||
selected && "bg-primary/40",
|
||||
!selected && "text-white/90"
|
||||
)
|
||||
}
|
||||
value={option.value}
|
||||
>
|
||||
{({ selected }) => (
|
||||
<>
|
||||
<span
|
||||
className={`block truncate ${
|
||||
selected ? "font-medium" : "font-normal"
|
||||
}`}
|
||||
>
|
||||
{option.label}
|
||||
</span>
|
||||
{selected ? (
|
||||
<span className="absolute inset-y-0 left-0 flex items-center pl-2.5">
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
aria-hidden="true"
|
||||
className="text-xs"
|
||||
/>
|
||||
</span>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</ListboxOption>
|
||||
))}
|
||||
</ListboxOptions>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Listbox>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Select;
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../../tsconfig.base.json"
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"types": [
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts",
|
||||
"vite/client"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"jsx": "react-jsx",
|
||||
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo"
|
||||
},
|
||||
"exclude": [
|
||||
"out-tsc",
|
||||
"dist",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx",
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.js",
|
||||
"src/**/*.jsx",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/vitest",
|
||||
"types": [
|
||||
"vitest/globals",
|
||||
"vitest/importMeta",
|
||||
"vite/client",
|
||||
"node",
|
||||
"vitest"
|
||||
],
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import * as path from 'path';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
root: __dirname,
|
||||
cacheDir: '../../../node_modules/.vite/libs/components/select',
|
||||
plugins: [react(), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') })],
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
// Configuration for building your library.
|
||||
// See: https://vitejs.dev/guide/build.html#library-mode
|
||||
build: {
|
||||
outDir: './dist',
|
||||
emptyOutDir: true,
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
lib: {
|
||||
// Could also be a dictionary or array of multiple entry points.
|
||||
entry: 'src/index.ts',
|
||||
name: '@storyteller/ui-select',
|
||||
fileName: 'index',
|
||||
// Change this to the formats you want to support.
|
||||
// Don't forget to update your package.json as well.
|
||||
formats: ['es' as const]
|
||||
},
|
||||
rollupOptions: {
|
||||
// External packages that should not be bundled into your library.
|
||||
external: ['react','react-dom','react/jsx-runtime']
|
||||
},
|
||||
},
|
||||
test: {
|
||||
watch: false,
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: './test-output/vitest/coverage',
|
||||
provider: 'v8' as const,
|
||||
}
|
||||
},
|
||||
}));
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# @storyteller/ui-settings-modal
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test @storyteller/ui-settings-modal` to execute the unit tests via [Vitest](https://vitest.dev/).
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user