mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-10 20:11:49 +00:00
Merge pull request #2 from storytold/styling/tabs-css
tabs and layout styling
This commit is contained in:
22 files changed
+703
-579
No files matched your search
+22
-22
@@ -1,41 +1,41 @@
|
||||
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export interface ButtonPropsI extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
icon?: IconDefinition;
|
||||
variant?: "primary"|"secondary";
|
||||
export interface ButtonPropsI
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
icon?: IconDefinition;
|
||||
variant?: "primary" | "secondary";
|
||||
}
|
||||
|
||||
export const Button = ({
|
||||
icon,
|
||||
children,
|
||||
className : propsClassName,
|
||||
variant = 'primary',
|
||||
className: propsClassName,
|
||||
variant = "primary",
|
||||
...rest
|
||||
}:ButtonPropsI)=>{
|
||||
function getVariantClassNames(variant: string){
|
||||
switch(variant){
|
||||
case "secondary":{
|
||||
}: ButtonPropsI) => {
|
||||
function getVariantClassNames(variant: string) {
|
||||
switch (variant) {
|
||||
case "secondary": {
|
||||
return " bg-brand-secondary hover:bg-brand-secondary-900 text-white focus-visible:outline-brand-secondary";
|
||||
}
|
||||
case "primary":
|
||||
default:{
|
||||
default: {
|
||||
return " bg-brand-primary hover:bg-brand-primary-400 text-white focus-visible:outline-brand-primary-600";
|
||||
}
|
||||
}
|
||||
};
|
||||
const className = "text-sm font-semibold rounded-md px-3.5 py-2.5 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2" + getVariantClassNames(variant)
|
||||
+ (propsClassName ? ` ${propsClassName}` : "");
|
||||
}
|
||||
const className = twMerge(
|
||||
"text-sm font-semibold rounded-md px-3 py-2 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 transition-all duration-150",
|
||||
getVariantClassNames(variant),
|
||||
propsClassName,
|
||||
);
|
||||
|
||||
return(
|
||||
<button
|
||||
className={className}
|
||||
{...rest}
|
||||
>
|
||||
{icon &&
|
||||
<FontAwesomeIcon className="mr-2" icon={icon}/>
|
||||
}
|
||||
return (
|
||||
<button className={className} {...rest}>
|
||||
{icon && <FontAwesomeIcon className="mr-2" icon={icon} />}
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1,35 +1,28 @@
|
||||
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
import {
|
||||
FontAwesomeIcon,
|
||||
FontAwesomeIconProps
|
||||
} from "@fortawesome/react-fontawesome";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
|
||||
export interface ButtonIconPropsI extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
icon: IconDefinition;
|
||||
iconClass?: string;
|
||||
iconProps?: FontAwesomeIconProps
|
||||
interface ButtonIconProps {
|
||||
icon: IconDefinition;
|
||||
fill?: boolean;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export const ButtonIcon = ({
|
||||
icon,
|
||||
iconClass,
|
||||
iconProps,
|
||||
className : propsClassName,
|
||||
...rest
|
||||
}:ButtonIconPropsI)=>{
|
||||
return(
|
||||
<button
|
||||
className={propsClassName}
|
||||
{...rest}
|
||||
>
|
||||
{icon &&
|
||||
<FontAwesomeIcon
|
||||
{...iconProps}
|
||||
className={iconClass}
|
||||
icon={icon}
|
||||
/>
|
||||
}
|
||||
onClick,
|
||||
fill = false,
|
||||
}: ButtonIconProps) => {
|
||||
const className = twMerge(
|
||||
"flex h-8 w-8 items-center justify-center rounded-lg transition-all duration-150",
|
||||
fill
|
||||
? "bg-ui-controls-button hover:bg-ui-controls-button/[0.75]"
|
||||
: "bg-transparent hover:bg-ui-panel/[0.4]",
|
||||
);
|
||||
|
||||
return (
|
||||
<button className={className} onClick={onClick}>
|
||||
<FontAwesomeIcon icon={icon} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useState } from "react";
|
||||
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
interface Option {
|
||||
value: string;
|
||||
icon: IconDefinition;
|
||||
}
|
||||
|
||||
interface ButtonIconSelectProps {
|
||||
options: Option[];
|
||||
onOptionChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
export function ButtonIconSelect({
|
||||
options,
|
||||
onOptionChange,
|
||||
}: ButtonIconSelectProps) {
|
||||
const [selectedOption, setSelectedOption] = useState<string>(
|
||||
options[0].value,
|
||||
);
|
||||
|
||||
const handleOptionChange = (value: string) => {
|
||||
setSelectedOption(value);
|
||||
if (onOptionChange) {
|
||||
onOptionChange(value);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex space-x-1">
|
||||
{options.map(({ value, icon }) => (
|
||||
<button
|
||||
key={value}
|
||||
className={twMerge(
|
||||
`flex h-8 w-8 items-center justify-center rounded-lg border-2 transition-all duration-150`,
|
||||
selectedOption === value
|
||||
? "border-brand-primary bg-ui-panel/[0.2]"
|
||||
: "border-transparent hover:bg-ui-panel/[0.4]",
|
||||
)}
|
||||
onClick={() => handleOptionChange(value)}
|
||||
>
|
||||
<FontAwesomeIcon icon={icon} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,41 +1,44 @@
|
||||
import { Link, LinkProps } from "@remix-run/react";
|
||||
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
interface LinkButtonProps extends LinkProps {
|
||||
icon?: IconDefinition;
|
||||
variant?: "primary"|"secondary";
|
||||
}
|
||||
variant?: "primary" | "secondary";
|
||||
}
|
||||
|
||||
export const ButtonLink = ({
|
||||
export const ButtonLink = ({
|
||||
icon,
|
||||
children,
|
||||
className: propsClassName,
|
||||
variant = "primary",
|
||||
...rest
|
||||
}:LinkButtonProps)=>{
|
||||
}: LinkButtonProps) => {
|
||||
//TODO: Duplicated from Button.tsx
|
||||
function getVariantClassNames(variant: string){
|
||||
switch(variant){
|
||||
case "secondary":{
|
||||
return " bg-brand-secondary hover:bg-brand-secondary-900 text-white focus-visible:outline-brand-secondary";
|
||||
function getVariantClassNames(variant: string) {
|
||||
switch (variant) {
|
||||
case "secondary": {
|
||||
return "bg-brand-secondary hover:bg-brand-secondary-900 text-white focus-visible:outline-brand-secondary";
|
||||
}
|
||||
case "primary":
|
||||
default:{
|
||||
return " bg-brand-primary hover:bg-brand-primary-400 text-white focus-visible:outline-brand-primary-600";
|
||||
default: {
|
||||
return "bg-brand-primary hover:bg-brand-primary-400 text-white focus-visible:outline-brand-primary-600";
|
||||
}
|
||||
}
|
||||
};
|
||||
const className = "text-sm font-semibold rounded-md px-3.5 py-2.5 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2" + getVariantClassNames(variant)
|
||||
+ (propsClassName ? ` ${propsClassName}` : "");
|
||||
}
|
||||
const baseClassName =
|
||||
"text-sm font-semibold rounded-md px-3 py-2 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 transition-all duration-150";
|
||||
const variantClassNames = getVariantClassNames(variant);
|
||||
const className = twMerge(baseClassName, variantClassNames, propsClassName);
|
||||
// END TODO
|
||||
|
||||
return(
|
||||
return (
|
||||
<Link {...rest}>
|
||||
<button className={className}>
|
||||
{icon && <FontAwesomeIcon className="mr-2" icon={icon}/>}
|
||||
{icon && <FontAwesomeIcon className="mr-2" icon={icon} />}
|
||||
{children}
|
||||
</button>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1,9 +1,6 @@
|
||||
import { Button } from "./Button";
|
||||
import { ButtonIcon } from "./ButtonIcon";
|
||||
import { ButtonLink } from "./ButtonLink";
|
||||
import { ButtonIconSelect } from "./ButtonIconSelect";
|
||||
|
||||
export {
|
||||
Button,
|
||||
ButtonIcon,
|
||||
ButtonLink,
|
||||
};
|
||||
export { Button, ButtonIcon, ButtonLink, ButtonIconSelect };
|
||||
@@ -1,20 +1,20 @@
|
||||
import { useState } from 'react'
|
||||
import { useState } from "react";
|
||||
|
||||
interface LowerPanelPropsI{
|
||||
interface LowerPanelPropsI {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const LowerPanel =({
|
||||
children,
|
||||
}:LowerPanelPropsI)=>{
|
||||
const [open, setOpen] = useState(false)
|
||||
const handleOpen = ()=>{
|
||||
export const LowerPanel = ({ children }: LowerPanelPropsI) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const handleOpen = () => {
|
||||
setOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
return (<>
|
||||
<div className="fixed w-screen top-3/4 h-1/4 bg-ui-panel border-t border-ui-panel-border">
|
||||
{children}
|
||||
</div>
|
||||
</>);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="absolute bottom-0 min-h-[220px] w-screen border-t border-ui-panel-border bg-ui-panel">
|
||||
{children}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,67 +1,53 @@
|
||||
import { useState } from "react";
|
||||
import { Tabs } from "../Tabs";
|
||||
import { Transition } from "@headlessui/react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
Fragment,
|
||||
useState
|
||||
} from 'react'
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import {
|
||||
faGears,
|
||||
} from '@fortawesome/pro-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
faChevronLeft,
|
||||
faChevronRight,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
import { Button } from '~/components/Button';
|
||||
export const SidePanel = () => {
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
|
||||
import { SidePanelInner } from './SidePanelInner';
|
||||
|
||||
interface SidePanelPropsI{
|
||||
title?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const SidePanel =({
|
||||
title,
|
||||
children,
|
||||
}:SidePanelPropsI)=>{
|
||||
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
const handleOpen = ()=> setOpen(true);
|
||||
const handleClose = ()=>setOpen(false);
|
||||
|
||||
return (<>
|
||||
<Button
|
||||
className='fixed right-0 top-20 mt-2 mr-4'
|
||||
onClick={handleOpen}
|
||||
>
|
||||
<FontAwesomeIcon className="fa-2xl -mx-2" icon={faGears} />
|
||||
</Button>
|
||||
<Transition.Root show={open} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={setOpen}>
|
||||
<div className="fixed inset-0" />
|
||||
|
||||
<div className="fixed inset-0 overflow-hidden">
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
<div className="pointer-events-none inset-y-0 fixed top-16 mt-2 right-0 flex max-w-full pl-10 sm:pl-16">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="transform transition ease-in-out duration-500 sm:duration-700"
|
||||
enterFrom="translate-x-full"
|
||||
enterTo="translate-x-0"
|
||||
leave="transform transition ease-in-out duration-500 sm:duration-700"
|
||||
leaveFrom="translate-x-0"
|
||||
leaveTo="translate-x-full"
|
||||
>
|
||||
<Dialog.Panel className="pointer-events-auto w-screen lg:max-w-md">
|
||||
<SidePanelInner
|
||||
title={title}
|
||||
closeCallback={handleClose}
|
||||
>
|
||||
{children}
|
||||
</SidePanelInner>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
</>);
|
||||
}
|
||||
return (
|
||||
<div className="relative border-l border-l-ui-panel-border">
|
||||
<button
|
||||
onClick={() => setIsVisible(!isVisible)}
|
||||
className="hover:bg-ui-controls-button absolute left-[-25px] top-1/2 flex rounded-l-lg bg-ui-controls px-2 py-3 align-middle text-sm text-white transition duration-150 ease-in-out"
|
||||
>
|
||||
<FontAwesomeIcon icon={isVisible ? faChevronRight : faChevronLeft} />
|
||||
</button>
|
||||
<Transition
|
||||
show={isVisible}
|
||||
enter="transition-transform duration-300"
|
||||
enterFrom="translate-x-full"
|
||||
enterTo="translate-x-0"
|
||||
leave="transition-transform duration-300"
|
||||
leaveFrom="translate-x-0"
|
||||
leaveTo="translate-x-full"
|
||||
>
|
||||
<Tabs
|
||||
tabs={[
|
||||
{
|
||||
header: "Animation",
|
||||
children: <p>Animation Tab</p>,
|
||||
},
|
||||
{
|
||||
header: "Camera",
|
||||
children: <p>Camera Tab</p>,
|
||||
},
|
||||
{
|
||||
header: "Audio",
|
||||
children: <p>Audio Tab</p>,
|
||||
},
|
||||
{
|
||||
header: "Styling",
|
||||
children: <p>Styling Tab</p>,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Transition>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
+23
-31
@@ -1,35 +1,29 @@
|
||||
import {
|
||||
useCallback,
|
||||
useLayoutEffect,
|
||||
useState
|
||||
} from 'react';
|
||||
import { Dialog } from '@headlessui/react'
|
||||
import { useCallback, useLayoutEffect, useState } from "react";
|
||||
import { Dialog } from "@headlessui/react";
|
||||
|
||||
import {
|
||||
faXmark,
|
||||
} from '@fortawesome/pro-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faXmark } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { TOP_BAR_HEIGHT } from '~/constants';
|
||||
import { TOP_BAR_HEIGHT } from "~/constants";
|
||||
|
||||
export const SidePanelInner = ({
|
||||
title,
|
||||
children,
|
||||
closeCallback
|
||||
}:{
|
||||
closeCallback,
|
||||
}: {
|
||||
title?: string;
|
||||
children: React.ReactNode;
|
||||
closeCallback: ()=>void;
|
||||
})=>{
|
||||
closeCallback: () => void;
|
||||
}) => {
|
||||
const [height, setHeight] = useState(0);
|
||||
const handleWindowResize = useCallback(()=>{
|
||||
if(window){
|
||||
setHeight (Math.ceil(window.innerHeight * 3 / 4) - TOP_BAR_HEIGHT);
|
||||
const handleWindowResize = useCallback(() => {
|
||||
if (window) {
|
||||
setHeight(Math.ceil((window.innerHeight * 3) / 4) - TOP_BAR_HEIGHT);
|
||||
}
|
||||
return 0;
|
||||
},[])
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(()=>{
|
||||
useLayoutEffect(() => {
|
||||
handleWindowResize();
|
||||
window.addEventListener("resize", handleWindowResize);
|
||||
return () => {
|
||||
@@ -37,19 +31,19 @@ export const SidePanelInner = ({
|
||||
};
|
||||
}, [handleWindowResize]);
|
||||
|
||||
return(
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col overflow-y-scroll bg-ui-panel py-6 shadow-xl border-y border-l border-ui-panel-border"
|
||||
style={{height:height+'px'}}
|
||||
className="flex flex-col overflow-y-scroll border-y border-l border-ui-panel-border bg-ui-panel py-6 shadow-xl"
|
||||
style={{ height: height + "px" }}
|
||||
>
|
||||
<div className="px-4 sm:px-6">
|
||||
<div className="flex items-start justify-between">
|
||||
{!title && <span/>}
|
||||
{title &&
|
||||
{!title && <span />}
|
||||
{title && (
|
||||
<Dialog.Title className="text-base font-semibold leading-6 text-white">
|
||||
{title}
|
||||
</Dialog.Title>
|
||||
}
|
||||
)}
|
||||
<div className="ml-3 flex h-7 items-center">
|
||||
<button
|
||||
type="button"
|
||||
@@ -59,7 +53,7 @@ export const SidePanelInner = ({
|
||||
{/* <span className="absolute -inset-2.5" /> */}
|
||||
<span className="sr-only">Close panel</span>
|
||||
<FontAwesomeIcon
|
||||
className="h-6 w-6 mt-1 mx-1"
|
||||
className="mx-1 mt-1 h-6 w-6"
|
||||
aria-hidden="true"
|
||||
icon={faXmark}
|
||||
/>
|
||||
@@ -67,9 +61,7 @@ export const SidePanelInner = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative mt-6 flex-1 px-4 sm:px-6">
|
||||
{children}
|
||||
</div>
|
||||
<div className="relative mt-6 flex-1">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
import { Fragment, useState } from "react";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { faGears } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { Button } from "~/components/Button";
|
||||
|
||||
import { SidePanelInner } from "./SidePanelInnerOld";
|
||||
|
||||
interface SidePanelPropsI {
|
||||
title?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const SidePanel = ({ title, children }: SidePanelPropsI) => {
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
const handleOpen = () => setOpen(true);
|
||||
const handleClose = () => setOpen(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button className="fixed right-0 top-20 mr-4 mt-2" onClick={handleOpen}>
|
||||
<FontAwesomeIcon className="fa-2xl -mx-2" icon={faGears} />
|
||||
</Button>
|
||||
<Transition.Root show={open} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={setOpen}>
|
||||
<div className="fixed inset-0" />
|
||||
|
||||
<div className="fixed inset-0 overflow-hidden">
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
<div className="pointer-events-none fixed inset-y-0 right-0 top-16 mt-2 flex max-w-full pl-10 sm:pl-16">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="transform transition ease-in-out duration-500 sm:duration-700"
|
||||
enterFrom="translate-x-full"
|
||||
enterTo="translate-x-0"
|
||||
leave="transform transition ease-in-out duration-500 sm:duration-700"
|
||||
leaveFrom="translate-x-0"
|
||||
leaveTo="translate-x-full"
|
||||
>
|
||||
<Dialog.Panel className="pointer-events-auto w-screen lg:max-w-md">
|
||||
<SidePanelInner title={title} closeCallback={handleClose}>
|
||||
{children}
|
||||
</SidePanelInner>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { SidePanel } from "./SidePanelOld";
|
||||
+22
-33
@@ -1,55 +1,44 @@
|
||||
import { useState } from 'react'
|
||||
import { Tab } from '@headlessui/react'
|
||||
import { useState } from "react";
|
||||
import { Tab } from "@headlessui/react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { joinClassNames } from '~/helpers';
|
||||
|
||||
export type Tab = {
|
||||
export type TabType = {
|
||||
header: string;
|
||||
children: JSX.Element;
|
||||
}
|
||||
};
|
||||
|
||||
export const Tabs = ({
|
||||
tabs: tabProps
|
||||
}:{
|
||||
tabs:Tab[]
|
||||
}) =>{
|
||||
const [tabs, setTabs] = useState(tabProps);
|
||||
export const Tabs = ({ tabs: tabProps }: { tabs: TabType[] }) => {
|
||||
const [tabs] = useState(tabProps);
|
||||
|
||||
const tabHeaderClassName = (selected: boolean) =>
|
||||
twMerge(
|
||||
"px-4 py-4 text-md font-medium leading-5 focus:outline-none transition duration-150 ease-in-out border-b-[3px] border-white/[.1]",
|
||||
selected
|
||||
? "text-white border-brand-primary"
|
||||
: "text-white/[0.5] hover:text-white hover:border-white/[.3]",
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-md px-2 py-16 sm:px-0">
|
||||
<div className="w-full max-w-md px-2 sm:px-0">
|
||||
<Tab.Group>
|
||||
<Tab.List className="flex space-x-1 rounded-xl bg-blue-900/20 p-1">
|
||||
<Tab.List className="flex">
|
||||
{tabs.map((tab, idx) => (
|
||||
<Tab
|
||||
key={idx}
|
||||
className={({ selected }) =>
|
||||
joinClassNames(
|
||||
'w-full rounded-lg py-2.5 text-sm font-medium leading-5',
|
||||
'ring-white/60 ring-offset-2 ring-offset-blue-400 focus:outline-none focus:ring-2',
|
||||
selected
|
||||
? 'bg-white text-blue-700 shadow'
|
||||
: 'text-blue-100 hover:bg-white/[0.12] hover:text-white'
|
||||
)
|
||||
}
|
||||
className={({ selected }) => tabHeaderClassName(selected)}
|
||||
>
|
||||
{tab.header}
|
||||
</Tab>
|
||||
))}
|
||||
</Tab.List>
|
||||
<Tab.Panels className="mt-2">
|
||||
<Tab.Panels>
|
||||
{tabs.map((tab, idx) => (
|
||||
<Tab.Panel
|
||||
key={idx}
|
||||
className={joinClassNames(
|
||||
'rounded-xl bg-white p-3',
|
||||
'ring-white/60 ring-offset-2 ring-offset-blue-400 focus:outline-none focus:ring-2'
|
||||
)}
|
||||
>
|
||||
<Tab.Panel key={idx} className="p-4 text-white focus:outline-none">
|
||||
{tab.children}
|
||||
</Tab.Panel>
|
||||
))}
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -1,32 +1,52 @@
|
||||
import { useContext } from 'react';
|
||||
import { useContext } from "react";
|
||||
|
||||
import { Button } from '~/components/Button'
|
||||
import { faRightToBracket } from '@fortawesome/pro-solid-svg-icons'
|
||||
import { TopBarInnerContext } from '~/contexts/TopBarInner';
|
||||
import { Button } from "~/components/Button";
|
||||
import {
|
||||
faChevronLeft,
|
||||
faRightToBracket,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { TopBarInnerContext } from "~/contexts/TopBarInner";
|
||||
import { ButtonLink } from "~/components";
|
||||
|
||||
export const TopBar = () => {
|
||||
return (
|
||||
<header className="
|
||||
fixed top-0 left-0 w-full
|
||||
bg-ui-panel border-b border-ui-panel-border
|
||||
">
|
||||
<nav className="mx-auto flex max-w-full items-center justify-between p-4" aria-label="Global">
|
||||
<div className="flex lg:flex-1">
|
||||
<a href="#" className="-m-1.5 p-1.5">
|
||||
<span className="sr-only">FakeYou</span>
|
||||
<img className="h-10 w-auto ml-0.5" src="/resources/images/Storyteller-Logo-1.png" alt="Logo FakeYou StoryTeller.ai" />
|
||||
</a>
|
||||
<span className="w-4 lg:w-8"/>
|
||||
<TopBarInner />
|
||||
<header
|
||||
className="fixed left-0 top-0 w-full
|
||||
border-b border-ui-panel-border bg-ui-panel"
|
||||
>
|
||||
<nav
|
||||
className="mx-auto flex max-w-full items-center justify-between p-4"
|
||||
aria-label="Global"
|
||||
>
|
||||
<div className="grid w-full grid-cols-3 gap-2">
|
||||
<div className="flex gap-4">
|
||||
<a href="/" className="">
|
||||
<span className="sr-only">Storyteller.ai</span>
|
||||
<img
|
||||
className="h-9 w-auto"
|
||||
src="/resources/images/Storyteller-Logo-1.png"
|
||||
alt="Logo FakeYou StoryTeller.ai"
|
||||
/>
|
||||
</a>
|
||||
<ButtonLink to={"/"} variant="secondary" icon={faChevronLeft}>
|
||||
Back to Dashboard
|
||||
</ButtonLink>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center">
|
||||
<TopBarInner />
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button icon={faRightToBracket}>Login</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Button icon={faRightToBracket}>Login</Button>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const TopBarInner = ()=>{
|
||||
const { TopBarInner } = useContext(TopBarInnerContext) || {}
|
||||
return(TopBarInner);
|
||||
}
|
||||
const TopBarInner = () => {
|
||||
const { TopBarInner } = useContext(TopBarInnerContext) || {};
|
||||
return TopBarInner;
|
||||
};
|
||||
@@ -1,61 +1,50 @@
|
||||
import {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useCallback, useContext, useEffect, useRef, useState } from "react";
|
||||
|
||||
import {
|
||||
faChevronLeft
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { faChevronLeft } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
import { Button } from '~/components/Button';
|
||||
import { ButtonLink } from '~/components/ButtonLink';
|
||||
import { TopBarHelmet } from '~/modules/TopBarHelmet';
|
||||
import { Button } from "~/components/Button";
|
||||
import { ButtonLink } from "~/components/ButtonLink";
|
||||
import { TopBarHelmet } from "~/modules/TopBarHelmet";
|
||||
|
||||
import { SidePanel } from '~/modules/SidePanel';
|
||||
import { SceneWithCube } from './SceneWithCube';
|
||||
import { SidePanel } from "~/modules/SidePanelOld";
|
||||
import { SceneWithCube } from "./SceneWithCube";
|
||||
|
||||
export const PageCube = () => {
|
||||
|
||||
|
||||
const mountRef = useRef<HTMLDivElement | null>(null);
|
||||
const sceneRef = useRef<SceneWithCube | null>(null);
|
||||
const [{
|
||||
x:rotationX,
|
||||
y:rotationY
|
||||
}, setRotations] = useState<{
|
||||
x: number | undefined,
|
||||
y:number | undefined
|
||||
}>({x:undefined, y:undefined});
|
||||
const [{ x: rotationX, y: rotationY }, setRotations] = useState<{
|
||||
x: number | undefined;
|
||||
y: number | undefined;
|
||||
}>({ x: undefined, y: undefined });
|
||||
|
||||
const toggleStopped = ()=>{
|
||||
if(sceneRef.current!==null){
|
||||
sceneRef.current.stopped = !sceneRef.current.stopped;
|
||||
}
|
||||
const toggleStopped = () => {
|
||||
if (sceneRef.current !== null) {
|
||||
sceneRef.current.stopped = !sceneRef.current.stopped;
|
||||
}
|
||||
};
|
||||
|
||||
const listeningXYRotation = useCallback((
|
||||
{x,y}:{x:number;y:number}
|
||||
)=>{
|
||||
setRotations({
|
||||
x:Math.round(x*100)/100,
|
||||
y:Math.round(y*100)/100,
|
||||
})
|
||||
},[]);
|
||||
const listeningXYRotation = useCallback(
|
||||
({ x, y }: { x: number; y: number }) => {
|
||||
setRotations({
|
||||
x: Math.round(x * 100) / 100,
|
||||
y: Math.round(y * 100) / 100,
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => { //componentDidMount
|
||||
useEffect(() => {
|
||||
//componentDidMount
|
||||
sceneRef.current = new SceneWithCube(listeningXYRotation);
|
||||
mountRef.current?.appendChild(sceneRef.current.renderer.domElement);
|
||||
|
||||
sceneRef.current.renderer.setAnimationLoop(()=>{
|
||||
|
||||
sceneRef.current.renderer.setAnimationLoop(() => {
|
||||
sceneRef.current?.update();
|
||||
});
|
||||
|
||||
// Clean up
|
||||
return () => {
|
||||
if(sceneRef.current)
|
||||
if (sceneRef.current)
|
||||
mountRef.current?.removeChild(sceneRef.current.renderer.domElement);
|
||||
};
|
||||
}, []);
|
||||
@@ -63,11 +52,7 @@ export const PageCube = () => {
|
||||
return (
|
||||
<div className="grid grid-cols-12 gap-2 ">
|
||||
<TopBarHelmet>
|
||||
<ButtonLink
|
||||
to={"/"}
|
||||
variant='secondary'
|
||||
icon={faChevronLeft}
|
||||
>
|
||||
<ButtonLink to={"/"} variant="secondary" icon={faChevronLeft}>
|
||||
Back to Dashboard
|
||||
</ButtonLink>
|
||||
</TopBarHelmet>
|
||||
@@ -76,15 +61,12 @@ export const PageCube = () => {
|
||||
</div>
|
||||
<div className="col-span-6 ">
|
||||
<Button onClick={toggleStopped}> STOP </Button>
|
||||
<p className='text-white'>Ration X: {rotationX ? rotationX : ""}</p>
|
||||
<p className='text-white'>Ration Y: {rotationY ? rotationY : ""}</p>
|
||||
<p className="text-white">Ration X: {rotationX ? rotationX : ""}</p>
|
||||
<p className="text-white">Ration Y: {rotationY ? rotationY : ""}</p>
|
||||
</div>
|
||||
<SidePanel
|
||||
title="Side Panel Title"
|
||||
>
|
||||
<SidePanel title="Side Panel Title">
|
||||
<p className="text-white">Side Panel Context</p>
|
||||
</SidePanel>
|
||||
</div>
|
||||
);
|
||||
|
||||
};
|
||||
};
|
||||
@@ -1,118 +1,133 @@
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
import {
|
||||
faChevronLeft,
|
||||
faWandSparkles,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { faWandSparkles } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
|
||||
import {
|
||||
Button,
|
||||
ButtonLink,
|
||||
} from '~/components';
|
||||
import { ButtonDialogue } from '~/modules/ButtonDialogue';
|
||||
import { TopBarHelmet } from '~/modules/TopBarHelmet/TopBarHelmet';
|
||||
import { SidePanel } from '~/modules/SidePanel';
|
||||
import { Tabs } from '~/modules/Tabs';
|
||||
import { Controls3D } from './comps/Controls3D';
|
||||
import { ControlsVideo } from './comps/ControlsVideo';
|
||||
import { Timeline } from './comps/Timeline';
|
||||
|
||||
import Editor from './js/editor';
|
||||
import { Button } from "~/components";
|
||||
import { ButtonDialogue } from "~/modules/ButtonDialogue";
|
||||
import { TopBarHelmet } from "~/modules/TopBarHelmet/TopBarHelmet";
|
||||
import { Controls3D } from "./comps/Controls3D";
|
||||
import { ControlsVideo } from "./comps/ControlsVideo";
|
||||
import { Timeline } from "./comps/Timeline";
|
||||
import { SidePanel } from "~/modules/SidePanel";
|
||||
import Editor from "./js/editor";
|
||||
|
||||
export const PageEnigma = () => {
|
||||
// const { setTopBarInner } = useContext(TopBarInnerContext) || {};
|
||||
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
const editorRef = useRef<Editor | null>(null);
|
||||
const [timelineHeight, setTimelineHeight] = useState(0);
|
||||
const timelineRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const editorCallback = useCallback(() => {
|
||||
// handle editorCallback here
|
||||
},[]);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
//componentDidMount
|
||||
|
||||
useEffect(() => { //componentDidMount
|
||||
|
||||
if (editorRef.current == null) {
|
||||
editorRef.current = new Editor();
|
||||
editorRef.current.initialize();
|
||||
}
|
||||
|
||||
}, []);
|
||||
|
||||
const handleButtonSave = ()=>{
|
||||
const handleButtonSave = () => {
|
||||
editorRef.current?.save();
|
||||
}
|
||||
};
|
||||
|
||||
const handleButtonCameraView = ()=> {
|
||||
const handleButtonCameraView = () => {
|
||||
editorRef.current?.change_camera_view();
|
||||
}
|
||||
};
|
||||
|
||||
const handleButtonPlayBack = ()=> {
|
||||
const handleButtonPlayBack = () => {
|
||||
editorRef.current?.start_playback();
|
||||
}
|
||||
};
|
||||
|
||||
return(
|
||||
const updateTimelineHeight = useCallback(() => {
|
||||
if (timelineRef.current) {
|
||||
setTimelineHeight(timelineRef.current.offsetHeight);
|
||||
}
|
||||
}, []);
|
||||
|
||||
//for updating timeline/engine div height (for resizing)
|
||||
useEffect(() => {
|
||||
const observer = new ResizeObserver((entries) => {
|
||||
for (let entry of entries) {
|
||||
updateTimelineHeight();
|
||||
}
|
||||
});
|
||||
|
||||
if (timelineRef.current) {
|
||||
observer.observe(timelineRef.current);
|
||||
}
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [updateTimelineHeight]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TopBarHelmet>
|
||||
<div className="flex grow justify-between">
|
||||
<ButtonLink
|
||||
to={"/"}
|
||||
variant='secondary'
|
||||
icon={faChevronLeft}
|
||||
>
|
||||
Back to Dashboard
|
||||
</ButtonLink>
|
||||
<Button
|
||||
icon={faWandSparkles}
|
||||
>
|
||||
Generate Movie
|
||||
</Button>
|
||||
<span className="w-8"/>
|
||||
</div>
|
||||
<Button icon={faWandSparkles}>Generate Movie</Button>
|
||||
</TopBarHelmet>
|
||||
<canvas ref={canvasRef} id="video-scene" width="1280px" height="720px" />
|
||||
<div className='fixed top-20 left-4'>
|
||||
<div className="flex mt-2 gap-2">
|
||||
<Button variant="secondary" onClick={handleButtonPlayBack}>Toggle Camera View</Button>
|
||||
<Button variant="secondary" onClick={handleButtonSave}>Save Scene</Button>
|
||||
<ButtonDialogue
|
||||
buttonProps={{
|
||||
variant: 'secondary',
|
||||
label: 'Help'
|
||||
}}
|
||||
title="Help"
|
||||
>
|
||||
<p>Do you need help?</p>
|
||||
<p>Ask Michael about this project</p>
|
||||
<p>Ask Miles about ThreeJS</p>
|
||||
<p>Ask Wil about React</p>
|
||||
</ButtonDialogue>
|
||||
<Controls3D/>
|
||||
<ControlsVideo/>
|
||||
|
||||
<div style={{ height: "calc(100vh - 68px)" }}>
|
||||
{/* Engine section/side panel */}
|
||||
<div
|
||||
className="flex"
|
||||
style={{ height: `calc(100% - ${timelineHeight}px)` }}
|
||||
>
|
||||
<div className="relative w-full overflow-hidden bg-gray-400">
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
id="video-scene"
|
||||
width="1280px"
|
||||
height="720px"
|
||||
/>
|
||||
|
||||
{/* Top controls */}
|
||||
<div className="absolute left-0 top-0 w-full">
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="flex gap-2 pl-3 pt-3">
|
||||
<Button variant="secondary" onClick={handleButtonPlayBack}>
|
||||
Toggle Camera View
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={handleButtonSave}>
|
||||
Save Scene
|
||||
</Button>
|
||||
<ButtonDialogue
|
||||
buttonProps={{
|
||||
variant: "secondary",
|
||||
label: "Help",
|
||||
}}
|
||||
title="Help"
|
||||
>
|
||||
<p>Do you need help?</p>
|
||||
<p>Ask Michael about this project</p>
|
||||
<p>Ask Miles about ThreeJS</p>
|
||||
<p>Ask Wil about React</p>
|
||||
</ButtonDialogue>
|
||||
</div>
|
||||
|
||||
<Controls3D />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom controls */}
|
||||
<div className="absolute bottom-0 left-0 w-full">
|
||||
<ControlsVideo />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Side panel */}
|
||||
<SidePanel />
|
||||
</div>
|
||||
|
||||
{/* Timeline */}
|
||||
<div className="min-h-[220px]" ref={timelineRef}>
|
||||
<Timeline editorCurrent={editorRef.current} />
|
||||
</div>
|
||||
</div>
|
||||
<SidePanel>
|
||||
<Tabs tabs={[
|
||||
{
|
||||
header: 'Animation',
|
||||
children: <p>Animation Tab</p>
|
||||
},{
|
||||
header: 'Camera',
|
||||
children: <p>Camera Tab</p>
|
||||
},{
|
||||
header: 'Audio',
|
||||
children: <p>Audio Tab</p>
|
||||
},{
|
||||
header: 'Styling',
|
||||
children: <p>Styling Tab</p>
|
||||
}
|
||||
]}/>
|
||||
</SidePanel>
|
||||
<Timeline editorCurrent={editorRef.current}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1,76 +1,90 @@
|
||||
|
||||
import {
|
||||
faArrowsRotate,
|
||||
faArrowsUpDownLeftRight,
|
||||
faCube,
|
||||
faPlus,
|
||||
faUpRightAndDownLeftFromCenter,
|
||||
} from '@fortawesome/pro-solid-svg-icons';
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import {
|
||||
fa3dCylinder,
|
||||
fa3dTorus,
|
||||
fa3dSphere,
|
||||
} from '@awesome.me/kit-fde2be5eb0/icons/kit/custom';
|
||||
} from "@awesome.me/kit-fde2be5eb0/icons/kit/custom";
|
||||
import { ButtonIcon, ButtonIconSelect } from "~/components";
|
||||
|
||||
import { ButtonIcon } from '~/components';
|
||||
|
||||
export const Controls3D = ()=>{
|
||||
export const Controls3D = () => {
|
||||
const handlePlus = () => {
|
||||
console.log('Controls 3D: Plus button clicked');
|
||||
console.log("Controls 3D: Plus button clicked");
|
||||
};
|
||||
const handleCube = () => {
|
||||
console.log("Controls 3D: Cube button clicked");
|
||||
};
|
||||
const handleCylinder = () => {
|
||||
console.log('Controls 3D: Cylinder button clicked');
|
||||
console.log("Controls 3D: Cylinder button clicked");
|
||||
};
|
||||
const handleTorus= () => {
|
||||
console.log('Controls 3D: Torus Button clicked');
|
||||
const handleTorus = () => {
|
||||
console.log("Controls 3D: Torus Button clicked");
|
||||
};
|
||||
const handleSphere= () => {
|
||||
console.log('Controls 3D: Sphere Button clicked');
|
||||
const handleSphere = () => {
|
||||
console.log("Controls 3D: Sphere Button clicked");
|
||||
};
|
||||
const handleMoveArrows = () => {
|
||||
console.log('Controls 3D: Move Arrows clicked');
|
||||
console.log("Controls 3D: Move Arrows clicked");
|
||||
};
|
||||
const handleRotateArrows = () => {
|
||||
console.log('Controls 3D: Rotate Arrows clicked');
|
||||
console.log("Controls 3D: Rotate Arrows clicked");
|
||||
};
|
||||
const handleZoomArrows = () => {
|
||||
console.log('Controls 3D: Zoom Arrows clicked');
|
||||
console.log("Controls 3D: Zoom Arrows clicked");
|
||||
};
|
||||
return(
|
||||
<div
|
||||
className="fixed left-1/2 -translate-x-1/2 bg-ui-panel border-b border-x border-ui-panel-border rounded-b-md px-4 py-2 text-white"
|
||||
style={{top: "72px"}}
|
||||
>
|
||||
<div className='flex gap-4'>
|
||||
<ButtonIcon
|
||||
icon={faPlus}
|
||||
onClick={handlePlus}
|
||||
/>
|
||||
<span className="w-0 h-5 border-l border-ui-panel-border" />
|
||||
<ButtonIcon
|
||||
icon={fa3dCylinder}
|
||||
onClick={handleCylinder}
|
||||
/>
|
||||
<ButtonIcon
|
||||
icon={fa3dTorus}
|
||||
onClick={handleTorus}
|
||||
/>
|
||||
<ButtonIcon
|
||||
icon={fa3dSphere}
|
||||
onClick={handleSphere}
|
||||
/>
|
||||
<ButtonIcon
|
||||
icon={faArrowsUpDownLeftRight}
|
||||
onClick={handleMoveArrows}
|
||||
/>
|
||||
<ButtonIcon
|
||||
icon={faArrowsRotate}
|
||||
onClick={handleRotateArrows}
|
||||
/>
|
||||
<ButtonIcon
|
||||
icon={faUpRightAndDownLeftFromCenter}
|
||||
onClick={handleZoomArrows}
|
||||
/>
|
||||
|
||||
const handleModeChange = (value: string) => {
|
||||
switch (value) {
|
||||
case "move":
|
||||
handleMoveArrows();
|
||||
break;
|
||||
case "rotate":
|
||||
handleRotateArrows();
|
||||
break;
|
||||
case "scale":
|
||||
handleZoomArrows();
|
||||
break;
|
||||
default:
|
||||
console.log("Unknown option");
|
||||
}
|
||||
};
|
||||
|
||||
const modes = [
|
||||
{ value: "move", icon: faArrowsUpDownLeftRight },
|
||||
{ value: "rotate", icon: faArrowsRotate },
|
||||
{ value: "scale", icon: faUpRightAndDownLeftFromCenter },
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex justify-center">
|
||||
<div className="rounded-b-lg border-x border-b border-ui-panel-border bg-ui-controls p-2 text-white">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<ButtonIcon icon={faPlus} onClick={handlePlus} fill={true} />
|
||||
|
||||
<span className="h-4 w-0 border-l border-white/[0.15]" />
|
||||
|
||||
<div className="flex gap-1">
|
||||
<ButtonIcon icon={faCube} onClick={handleCube} />
|
||||
<ButtonIcon icon={fa3dCylinder} onClick={handleCylinder} />
|
||||
<ButtonIcon icon={fa3dTorus} onClick={handleTorus} />
|
||||
<ButtonIcon icon={fa3dSphere} onClick={handleSphere} />
|
||||
</div>
|
||||
|
||||
<span className="h-4 w-0 border-l border-white/[0.15]" />
|
||||
|
||||
<ButtonIconSelect
|
||||
options={modes}
|
||||
onOptionChange={handleModeChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -4,54 +4,38 @@ import {
|
||||
faForwardFast,
|
||||
faForwardStep,
|
||||
faPlay,
|
||||
} from '@fortawesome/pro-solid-svg-icons';
|
||||
import { ButtonIcon } from '~/components';
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { ButtonIcon } from "~/components";
|
||||
|
||||
export const ControlsVideo = ()=>{
|
||||
export const ControlsVideo = () => {
|
||||
const handleBackwardFast = () => {
|
||||
console.log('Controls Video: Backward-Fast clicked');
|
||||
console.log("Controls Video: Backward-Fast clicked");
|
||||
};
|
||||
const handleBackwardStep = () => {
|
||||
console.log('Controls Video: Backward-Step clicked');
|
||||
console.log("Controls Video: Backward-Step clicked");
|
||||
};
|
||||
const handlePlay= () => {
|
||||
console.log('Controls Video: Play clicked');
|
||||
const handlePlay = () => {
|
||||
console.log("Controls Video: Play clicked");
|
||||
};
|
||||
const handleForwardStep = () => {
|
||||
console.log('Controls Video: Forward-Step clicked');
|
||||
console.log("Controls Video: Forward-Step clicked");
|
||||
};
|
||||
const handleForwardFast = () => {
|
||||
console.log('Controls Video: Forward-Fast clicked');
|
||||
console.log("Controls Video: Forward-Fast clicked");
|
||||
};
|
||||
return(
|
||||
<div className="fixed top-3/4 left-1/2 -translate-x-1/2 -mt-10 bg-ui-panel border-t border-x border-ui-panel-border rounded-t-md px-6 py-2 text-white">
|
||||
<div className='flex gap-6 content-center '>
|
||||
<ButtonIcon
|
||||
className="h-6"
|
||||
icon={faBackwardFast}
|
||||
onClick={handleBackwardFast}
|
||||
/>
|
||||
<ButtonIcon
|
||||
className="h-6"
|
||||
icon={faBackwardStep}
|
||||
onClick={handleBackwardStep}
|
||||
/>
|
||||
<ButtonIcon
|
||||
className="h-6"
|
||||
icon={faPlay}
|
||||
onClick={handlePlay}
|
||||
/>
|
||||
<ButtonIcon
|
||||
className="h-6"
|
||||
icon={faForwardStep}
|
||||
onClick={handleForwardStep}
|
||||
/>
|
||||
<ButtonIcon
|
||||
className="h-6"
|
||||
icon={faForwardFast}
|
||||
onClick={handleForwardFast}
|
||||
/>
|
||||
return (
|
||||
<div>
|
||||
<div className="flex justify-center">
|
||||
<div className="rounded-t-lg border-x border-t border-ui-panel-border bg-ui-controls p-2 text-white">
|
||||
<div className="flex content-center gap-2">
|
||||
<ButtonIcon icon={faBackwardFast} onClick={handleBackwardFast} />
|
||||
<ButtonIcon icon={faBackwardStep} onClick={handleBackwardStep} />
|
||||
<ButtonIcon icon={faPlay} onClick={handlePlay} />
|
||||
<ButtonIcon icon={faForwardStep} onClick={handleForwardStep} />
|
||||
<ButtonIcon icon={faForwardFast} onClick={handleForwardFast} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
@@ -1,31 +1,32 @@
|
||||
import { LowerPanel } from "~/modules/LowerPanel";
|
||||
import {
|
||||
Button,
|
||||
} from '~/components';
|
||||
import Editor from '../../js/editor';
|
||||
import { Button } from "~/components";
|
||||
import Editor from "../../js/editor";
|
||||
|
||||
export const Timeline = ({
|
||||
editorCurrent
|
||||
}:{
|
||||
editorCurrent: Editor|null;
|
||||
})=>{
|
||||
const handleButtonLoad = ()=>{
|
||||
editorCurrent,
|
||||
}: {
|
||||
editorCurrent: Editor | null;
|
||||
}) => {
|
||||
const handleButtonLoad = () => {
|
||||
document.getElementById("load-upload")?.click();
|
||||
}
|
||||
const handleButtonRender = ()=>{
|
||||
};
|
||||
const handleButtonRender = () => {
|
||||
editorCurrent?.togglePlayback();
|
||||
}
|
||||
const handleButtonPlay = ()=>{
|
||||
};
|
||||
const handleButtonPlay = () => {};
|
||||
|
||||
}
|
||||
|
||||
return(
|
||||
return (
|
||||
<LowerPanel>
|
||||
<div className='h-10 w-full border-b border-ui-panel-border'></div>
|
||||
<input style={{ display: 'none' }} type="file" id="load-upload" name="load-upload"></input>
|
||||
<div className="h-10 w-full border-b border-ui-panel-border"></div>
|
||||
<input
|
||||
style={{ display: "none" }}
|
||||
type="file"
|
||||
id="load-upload"
|
||||
name="load-upload"
|
||||
></input>
|
||||
<Button onClick={handleButtonLoad}>Load</Button>
|
||||
<Button onClick={handleButtonRender}>Render</Button>
|
||||
<Button onClick={handleButtonPlay}>Play</Button>
|
||||
<Button onClick={handleButtonRender}>Render</Button>
|
||||
<Button onClick={handleButtonPlay}>Play</Button>
|
||||
</LowerPanel>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
+38
-20
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useState } from "react";
|
||||
import { LinksFunction } from "@remix-run/deno";
|
||||
import {
|
||||
Links,
|
||||
@@ -13,22 +13,39 @@ import tailwindCss from "./styles/tailwind.css?url";
|
||||
import baseCss from "./styles/base.css?url";
|
||||
|
||||
import { TopBar } from "./modules/TopBar";
|
||||
import { TopBarInnerContext } from '~/contexts/TopBarInner';
|
||||
import { TopBarInnerContext } from "~/contexts/TopBarInner";
|
||||
|
||||
|
||||
export const links : LinksFunction = () => [{
|
||||
rel: "stylesheet",
|
||||
href: normalizeCss,
|
||||
},{
|
||||
rel: "stylesheet",
|
||||
href: tailwindCss,
|
||||
},{
|
||||
rel: "stylesheet",
|
||||
href: baseCss,
|
||||
}];
|
||||
export const links: LinksFunction = () => [
|
||||
{
|
||||
rel: "stylesheet",
|
||||
href: normalizeCss,
|
||||
},
|
||||
{
|
||||
rel: "stylesheet",
|
||||
href: tailwindCss,
|
||||
},
|
||||
{
|
||||
rel: "stylesheet",
|
||||
href: baseCss,
|
||||
},
|
||||
{
|
||||
rel: "preconnect",
|
||||
href: "https://fonts.googleapis.com",
|
||||
},
|
||||
{
|
||||
rel: "preconnect",
|
||||
href: "https://fonts.gstatic.com",
|
||||
crossOrigin: "anonymous",
|
||||
},
|
||||
{
|
||||
rel: "stylesheet",
|
||||
href: "https://fonts.googleapis.com/css2?family=Fira+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap",
|
||||
},
|
||||
];
|
||||
|
||||
export default function App() {
|
||||
const [topBarInnerComponent, setTopBarInnerComponent] = useState<JSX.Element | null>(null);
|
||||
const [topBarInnerComponent, setTopBarInnerComponent] =
|
||||
useState<JSX.Element | null>(null);
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
@@ -38,13 +55,14 @@ export default function App() {
|
||||
<Meta />
|
||||
<Links />
|
||||
</head>
|
||||
<body className="bg-ui-background">
|
||||
<TopBarInnerContext.Provider value={{
|
||||
<body className="overflow-hidden bg-ui-background">
|
||||
<TopBarInnerContext.Provider
|
||||
value={{
|
||||
TopBarInner: topBarInnerComponent,
|
||||
setTopBarInner: setTopBarInnerComponent
|
||||
}}>
|
||||
|
||||
<div className="topbar-spacer"/>
|
||||
setTopBarInner: setTopBarInnerComponent,
|
||||
}}
|
||||
>
|
||||
<div className="topbar-spacer" />
|
||||
<Outlet />
|
||||
<TopBar />
|
||||
</TopBarInnerContext.Provider>
|
||||
|
||||
+7
-3
@@ -1,4 +1,8 @@
|
||||
.topbar-spacer{
|
||||
height: 73px;
|
||||
body {
|
||||
font-family: "Fira Sans", sans-serif;
|
||||
}
|
||||
|
||||
.topbar-spacer {
|
||||
height: 68px;
|
||||
width: 100vw;
|
||||
}
|
||||
}
|
||||
Generated
+17
-6
@@ -27,6 +27,7 @@
|
||||
"isbot": "^4.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"tailwind-merge": "^2.2.2",
|
||||
"three": "^0.162.0",
|
||||
"uuid": "^9.0.1",
|
||||
"webm-writer": "^1.0.0"
|
||||
@@ -722,10 +723,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.23.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz",
|
||||
"integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==",
|
||||
"dev": true,
|
||||
"version": "7.24.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz",
|
||||
"integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
@@ -10600,8 +10600,7 @@
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||
},
|
||||
"node_modules/regexp.prototype.flags": {
|
||||
"version": "1.5.2",
|
||||
@@ -11720,6 +11719,18 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwind-merge": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.2.tgz",
|
||||
"integrity": "sha512-tWANXsnmJzgw6mQ07nE3aCDkCK4QdT3ThPMCzawoYA2Pws7vSTCvz3Vrjg61jVUGfFZPJzxEP+NimbcW+EdaDw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.24.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/dcastil"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz",
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"isbot": "^4.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"tailwind-merge": "^2.2.2",
|
||||
"three": "^0.162.0",
|
||||
"uuid": "^9.0.1",
|
||||
"webm-writer": "^1.0.0"
|
||||
|
||||
+74
-64
@@ -1,5 +1,5 @@
|
||||
import type { Config } from 'tailwindcss';
|
||||
import colors from 'tailwindcss/colors';
|
||||
import type { Config } from "tailwindcss";
|
||||
import colors from "tailwindcss/colors";
|
||||
|
||||
export default {
|
||||
content: [
|
||||
@@ -8,76 +8,86 @@ export default {
|
||||
"./app/*.{js,jsx,ts,tsx}",
|
||||
],
|
||||
theme: {
|
||||
// extend: {},
|
||||
colors:{
|
||||
transparent: 'transparent',
|
||||
current: 'currentColor',
|
||||
extend: {
|
||||
fontFamily: {
|
||||
"custom-font": ["Fira Sans", "sans-serif"],
|
||||
},
|
||||
},
|
||||
colors: {
|
||||
transparent: "transparent",
|
||||
current: "currentColor",
|
||||
|
||||
'brand-primary': { //sunglo
|
||||
DEFAULT: '#e66462', //=500
|
||||
'50': '#fdf3f3',
|
||||
'100': '#fce4e4',
|
||||
'200': '#facfce',
|
||||
'300': '#f6acab',
|
||||
'400': '#ef7c7a',
|
||||
'500': '#e66462',
|
||||
'600': '#cf3533',
|
||||
'700': '#ae2927',
|
||||
'800': '#902624',
|
||||
'900': '#782524',
|
||||
'950': '#410f0e',
|
||||
"brand-primary": {
|
||||
//sunglo
|
||||
DEFAULT: "#e66462", //=500
|
||||
"50": "#fdf3f3",
|
||||
"100": "#fce4e4",
|
||||
"200": "#facfce",
|
||||
"300": "#f6acab",
|
||||
"400": "#ef7c7a",
|
||||
"500": "#e66462",
|
||||
"600": "#cf3533",
|
||||
"700": "#ae2927",
|
||||
"800": "#902624",
|
||||
"900": "#782524",
|
||||
"950": "#410f0e",
|
||||
},
|
||||
'brand-secondary': { //gunpowder
|
||||
DEFAULT: '#39394c', //=950
|
||||
'50': '#f5f6f9',
|
||||
'100': '#e8eaf1',
|
||||
'200': '#d7dbe6',
|
||||
'300': '#bbc1d5',
|
||||
'400': '#9aa2c0',
|
||||
'500': '#8188b0',
|
||||
'600': '#6f74a1',
|
||||
'700': '#636492',
|
||||
'800': '#545579',
|
||||
'900': '#464762',
|
||||
'950': '#39394c',
|
||||
"brand-secondary": {
|
||||
//gunpowder
|
||||
DEFAULT: "#39394c", //=950
|
||||
"50": "#f5f6f9",
|
||||
"100": "#e8eaf1",
|
||||
"200": "#d7dbe6",
|
||||
"300": "#bbc1d5",
|
||||
"400": "#9aa2c0",
|
||||
"500": "#8188b0",
|
||||
"600": "#6f74a1",
|
||||
"700": "#636492",
|
||||
"800": "#545579",
|
||||
"900": "#464762",
|
||||
"950": "#39394c",
|
||||
},
|
||||
danger: { //copper rust
|
||||
DEFAULT: '#8f4951', //=700
|
||||
'50': '#fbf5f5',
|
||||
'100': '#f7ecec',
|
||||
'200': '#efdcdc',
|
||||
'300': '#e2c0bf',
|
||||
'400': '#d19b9c',
|
||||
'500': '#bd7679',
|
||||
'600': '#a5595f',
|
||||
'700': '#8f4951',
|
||||
'800': '#743d45',
|
||||
'900': '#64373f',
|
||||
'950': '#361b1e',
|
||||
danger: {
|
||||
//copper rust
|
||||
DEFAULT: "#8f4951", //=700
|
||||
"50": "#fbf5f5",
|
||||
"100": "#f7ecec",
|
||||
"200": "#efdcdc",
|
||||
"300": "#e2c0bf",
|
||||
"400": "#d19b9c",
|
||||
"500": "#bd7679",
|
||||
"600": "#a5595f",
|
||||
"700": "#8f4951",
|
||||
"800": "#743d45",
|
||||
"900": "#64373f",
|
||||
"950": "#361b1e",
|
||||
},
|
||||
|
||||
success:{ //apple
|
||||
DEFAULT: '#40ad48', //=500
|
||||
'50': '#f2fbf2',
|
||||
'100': '#e2f6e3',
|
||||
'200': '#c5edc7',
|
||||
'300': '#98dd9d',
|
||||
'400': '#64c46b',
|
||||
'500': '#40ad48',
|
||||
'600': '#2f8a35',
|
||||
'700': '#286d2e',
|
||||
'800': '#245728',
|
||||
'900': '#1f4823',
|
||||
'950': '#0c270f',
|
||||
success: {
|
||||
//apple
|
||||
DEFAULT: "#40ad48", //=500
|
||||
"50": "#f2fbf2",
|
||||
"100": "#e2f6e3",
|
||||
"200": "#c5edc7",
|
||||
"300": "#98dd9d",
|
||||
"400": "#64c46b",
|
||||
"500": "#40ad48",
|
||||
"600": "#2f8a35",
|
||||
"700": "#286d2e",
|
||||
"800": "#245728",
|
||||
"900": "#1f4823",
|
||||
"950": "#0c270f",
|
||||
},
|
||||
|
||||
'ui-background': '#1a1a27',
|
||||
'ui-panel': '#242433',
|
||||
'ui-panel-border': '#5e6097',
|
||||
"ui-background": "#1a1a27",
|
||||
"ui-panel": "#242433",
|
||||
"ui-panel-border": "#393948",
|
||||
"ui-controls": "#39394D",
|
||||
"ui-controls-button": "#676781",
|
||||
|
||||
gray: colors.gray,
|
||||
white: colors.white
|
||||
}
|
||||
white: colors.white,
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
} satisfies Config
|
||||
} satisfies Config;
|
||||
Reference in new issue
Block a user