Home page in desktop, separate file support (#252)

* Bugfix: don't toast on every change of defaultDir

* Refactor app to live under /file/:id

* Stub out Tauri-only home page

* home reads and writes blank files to defaultDir

* Fix initial directory creation

* Make file names editable

* Refactor onboarding to use normal fns for load issues

* Feature: load and write files to and from disk

* Feature: Add file deletion, break out FileCard component

* Fix settings close URLs to be relative, button types

* Add filename and link to AppHeader

* Style tweaks: scrollbar, header name, card size

* Style: add header, empty state to Home

* Refactor: load file in route loader

* Move makePathRelative to lib to fix tests

* Fix App test

* Use '$nnn' default name scheme

* Fix type error on ActionButton

* Fix type error on ActionButton

* @adamchalmers review

* Fix merge mistake

* Refactor: rename all things "file" to "project"

* Feature: migrate to <project-name>/main.kcl setup

* Fix tsc test

* @Irev-Dev review part 1: renames and imports

* @Irev-Dev review pt 2: simplify file list refresh

* @Irev-Dev review pt 3: filter out non-projects

* @Irev-review pt 4: folder conventions + home auth

* Add sort functionality to new welcome page (#255)

* Add todo for Sentry
This commit is contained in:
Frank Noirot
2023-08-15 21:56:24 -04:00
committed by GitHub
parent 826ad267b4
commit 19761baba6
29 changed files with 1003 additions and 157 deletions

View File

@ -1,52 +1,92 @@
import { Link } from 'react-router-dom'
import { ActionIcon, ActionIconProps } from './ActionIcon'
import React from 'react'
import { paths } from '../Router'
import { Link } from 'react-router-dom'
import type { LinkProps } from 'react-router-dom'
interface ActionButtonProps extends React.PropsWithChildren {
interface BaseActionButtonProps {
icon?: ActionIconProps
className?: string
onClick?: () => void
to?: string
Element?:
| 'button'
| 'link'
| React.ComponentType<React.HTMLAttributes<HTMLButtonElement>>
}
export const ActionButton = ({
icon,
className,
onClick,
to = paths.INDEX,
Element = 'button',
children,
...props
}: ActionButtonProps) => {
const classNames = `group mono text-base flex items-center gap-2 rounded-sm border border-chalkboard-40 dark:border-chalkboard-60 hover:border-liquid-40 dark:hover:bg-chalkboard-90 p-[3px] text-chalkboard-110 dark:text-chalkboard-10 hover:text-chalkboard-110 hover:dark:text-chalkboard-10 ${
icon ? 'pr-2' : 'px-2'
} ${className}`
type ActionButtonAsButton = BaseActionButtonProps &
Omit<
React.ButtonHTMLAttributes<HTMLButtonElement>,
keyof BaseActionButtonProps
> & {
Element: 'button'
}
if (Element === 'button') {
return (
<button onClick={onClick} className={classNames} {...props}>
{icon && <ActionIcon {...icon} />}
{children}
</button>
)
} else if (Element === 'link') {
return (
<Link to={to} className={classNames} {...props}>
{icon && <ActionIcon {...icon} />}
{children}
</Link>
)
} else {
return (
<Element onClick={onClick} className={classNames} {...props}>
{icon && <ActionIcon {...icon} />}
{children}
</Element>
)
type ActionButtonAsLink = BaseActionButtonProps &
Omit<LinkProps, keyof BaseActionButtonProps> & {
Element: 'link'
}
type ActionButtonAsExternal = BaseActionButtonProps &
Omit<
React.AnchorHTMLAttributes<HTMLAnchorElement>,
keyof BaseActionButtonProps
> & {
Element: 'externalLink'
}
type ActionButtonAsElement = BaseActionButtonProps &
Omit<React.HTMLAttributes<HTMLElement>, keyof BaseActionButtonProps> & {
Element: React.ComponentType<React.HTMLAttributes<HTMLButtonElement>>
}
type ActionButtonProps =
| ActionButtonAsButton
| ActionButtonAsLink
| ActionButtonAsExternal
| ActionButtonAsElement
export const ActionButton = (props: ActionButtonProps) => {
const classNames = `group mono text-base flex items-center gap-2 rounded-sm border border-chalkboard-40 dark:border-chalkboard-60 hover:border-liquid-40 dark:hover:bg-chalkboard-90 p-[3px] text-chalkboard-110 dark:text-chalkboard-10 hover:text-chalkboard-110 hover:dark:text-chalkboard-10 ${
props.icon ? 'pr-2' : 'px-2'
} ${props.className || ''}`
switch (props.Element) {
case 'button': {
// Note we have to destructure 'className' and 'Element' out of props
// because we don't want to pass them to the button element;
// the same is true for the other cases below.
const { Element, icon, children, className, ...rest } = props
return (
<button className={classNames} {...rest}>
{props.icon && <ActionIcon {...icon} />}
{children}
</button>
)
}
case 'link': {
const { Element, to, icon, children, className, ...rest } = props
return (
<Link to={to || paths.INDEX} className={classNames} {...rest}>
{icon && <ActionIcon {...icon} />}
{children}
</Link>
)
}
case 'externalLink': {
const { Element, icon, children, className, ...rest } = props
return (
<a className={classNames} {...rest}>
{icon && <ActionIcon {...icon} />}
{children}
</a>
)
}
default: {
const { Element, icon, children, className, ...rest } = props
if (!Element) throw new Error('Element is required')
return (
<Element className={classNames} {...rest}>
{props.icon && <ActionIcon {...props.icon} />}
{children}
</Element>
)
}
}
}