Skip to content

Commit

Permalink
Merge pull request #48 from Pkcarreno/feats/3
Browse files Browse the repository at this point in the history
Feat
  • Loading branch information
Pkcarreno authored Nov 2, 2024
2 parents 4128b0f + 8ae8659 commit 461d660
Show file tree
Hide file tree
Showing 16 changed files with 506 additions and 38 deletions.
22 changes: 21 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,27 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JSoD - JS on Demand</title>
<link rel="canonical" href="https://pkcarreno.github.io/jsod" />
<title>JSoD</title>

<meta
name="description"
content="This is an app to run and share JavaScript code from your browser. It is installable, offline and has a simple-to-use interface."
/>
<meta
name="keywords"
content="JS, JavaScript, run, quickjs, execute, online, share"
/>
<meta name="author" content="Pkcarreno" />

<meta property="og:title" content="JSoD" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://pkcarreno.github.io/jsod" />
<meta property="twitter:title" content="JSoD" />
<meta
property="twitter:description"
content="This is an app to run and share JavaScript code from your browser. It is installable, offline and has a simple-to-use interface."
/>

<link rel="apple-touch-icon" href="/icons/pwa-icon_x180.png" />
<link rel="icon" href="/icons/favicon.ico" sizes="32x32" />
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@codemirror/lint": "^6.8.1",
"@fontsource/ibm-plex-mono": "^5.1.0",
"@fontsource/ibm-plex-sans": "^5.1.0",
"@hookform/resolvers": "^3.9.1",
"@jitl/quickjs-ng-wasmfile-release-sync": "0.31.0",
"@lezer/highlight": "^1.2.1",
"@radix-ui/react-accordion": "^1.2.1",
Expand Down Expand Up @@ -60,13 +61,16 @@
"quickjs-emscripten-sync": "^1.5.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet-async": "^2.0.5",
"react-hook-form": "^7.53.1",
"react-hotkeys-hook": "^4.5.1",
"react-resizable-panels": "^2.1.6",
"react-virtuoso": "^4.12.0",
"rfdc": "^1.4.1",
"sonner": "^1.5.0",
"tailwind-merge": "^2.5.4",
"wouter": "^3.3.5",
"zod": "^3.23.8",
"zustand": "^5.0.0"
},
"devDependencies": {
Expand Down
53 changes: 53 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 10 additions & 6 deletions src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { HelmetProvider } from 'react-helmet-async';

import { Toaster } from '@/components/ui/sonner';
import { ThemeProvider } from '@/providers/theme-provider';
import Router from '@/routes';

import { PromptProvider } from './providers/prompt-provider';

const App = () => (
<ThemeProvider storageKey="theme">
<PromptProvider>
<Router />
</PromptProvider>
<Toaster />
</ThemeProvider>
<HelmetProvider>
<ThemeProvider storageKey="theme">
<PromptProvider>
<Router />
</PromptProvider>
<Toaster />
</ThemeProvider>
</HelmetProvider>
);

export default App;
171 changes: 171 additions & 0 deletions src/components/ui/form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import type * as LabelPrimitive from '@radix-ui/react-label';
import { Slot } from '@radix-ui/react-slot';
import * as React from 'react';
import type { ControllerProps, FieldPath, FieldValues } from 'react-hook-form';
import { Controller, FormProvider, useFormContext } from 'react-hook-form';

import { Label } from '@/components/ui/label';
import { cn } from '@/lib/utils';

const Form = FormProvider;

type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
name: TName;
};

const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue,
);

const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
</FormFieldContext.Provider>
);
};

const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext);
const itemContext = React.useContext(FormItemContext);
const { getFieldState, formState } = useFormContext();

const fieldState = getFieldState(fieldContext.name, formState);

if (!fieldContext) {
throw new Error('useFormField should be used within <FormField>');
}

const { id } = itemContext;

return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
};
};

type FormItemContextValue = {
id: string;
};

const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue,
);

const FormItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const id = React.useId();

return (
<FormItemContext.Provider value={{ id }}>
<div ref={ref} className={cn('space-y-2', className)} {...props} />
</FormItemContext.Provider>
);
});
FormItem.displayName = 'FormItem';

const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField();

return (
<Label
ref={ref}
className={cn(error && 'text-destructive', className)}
htmlFor={formItemId}
{...props}
/>
);
});
FormLabel.displayName = 'FormLabel';

const FormControl = React.forwardRef<
React.ElementRef<typeof Slot>,
React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } =
useFormField();

return (
<Slot
ref={ref}
id={formItemId}
aria-describedby={
!error
? `${formDescriptionId}`
: `${formDescriptionId} ${formMessageId}`
}
aria-invalid={!!error}
{...props}
/>
);
});
FormControl.displayName = 'FormControl';

const FormDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField();

return (
<p
ref={ref}
id={formDescriptionId}
className={cn('text-muted-foreground text-sm', className)}
{...props}
/>
);
});
FormDescription.displayName = 'FormDescription';

const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField();
const body = error ? String(error?.message) : children;

if (!body) {
return null;
}

return (
<p
ref={ref}
id={formMessageId}
className={cn('text-destructive text-sm font-medium', className)}
{...props}
>
{body}
</p>
);
});
FormMessage.displayName = 'FormMessage';

export {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
useFormField,
};
23 changes: 23 additions & 0 deletions src/components/ui/textarea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from 'react';

import { cn } from '@/lib/utils';

export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;

const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => {
return (
<textarea
className={cn(
'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex min-h-[80px] w-full rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
className,
)}
ref={ref}
{...props}
/>
);
},
);
Textarea.displayName = 'Textarea';

export { Textarea };
Loading

0 comments on commit 461d660

Please sign in to comment.