Compare commits

..

10 Commits

9 changed files with 245 additions and 8 deletions

View File

@ -0,0 +1,53 @@
import cx from "clsx";
import { prop } from "ramda";
import { mergeProps } from "solid-js";
import classes from "./ActionIcon.module.css";
/**
* @typedef {object} ActionIconProps
* @property {"s" | "m" | "l" | "xl" | "xxl"} [size]
* @property {"filled" | "subtle" | "transparent"} [variant]
* @property {"primary" | "secondary" | "danger" | "warn" | "success" | "info"} [color]
* @property {string[]} [additionalClasses]
* @property {boolean} [disabled]
* @property {JSX.EventHandler<HTMLButtonElement, PointerEvent>} [onClick]
*/
/**
* @param {import("solid-js").ParentProps<ActionIconProps>} props
*/
export default function ActionIcon(props) {
const mergedProps = mergeProps(
{
size: "m",
color: "secondary",
variant: "transparent",
additionalClasses: [],
disabled: false,
onClick: (e) => {},
},
props
);
const handleClick = (e) => {
if (mergedProps.disabled) {
return;
}
mergedProps.onClick(e);
};
return (
<button
class={cx(
prop("action-icon-button", classes),
prop(`size-${mergedProps.size}`, classes),
prop(`variant-${mergedProps.variant}`, classes),
prop(`color-${mergedProps.color}`, classes),
...mergedProps.additionalClasses
)}
disabled={mergedProps.disabled}
onClick={handleClick}
>
{mergedProps.children}
</button>
);
}

View File

@ -0,0 +1,80 @@
@import "@/mixins.css";
.action-icon-button {
--size: calc(var(--border-radius-xxs) * 2);
border: none;
@util padding(0);
@each $size in (s, m, l, xl, xxl) {
&.size-#{$size} {
--size: calc(var(--border-radius-#{$size}) * 2);
@util size(calc(var(--size) * 2), calc(var(--size) * 2));
border-radius: var(--size);
& svg {
@util size(calc(var(--size) * 1.4), calc(var(--size) * 1.4));
}
}
}
@each $color in (primary, secondary, danger, warn, success, info) {
&.color-#{$color} {
color: var(--palette-white-3);
&.variant-transparent {
color: var(--color-#{$color});
background-color: transparent;
&[disabled] {
color: var(--palette-gray-4);
@include dark-mode {
color: var(--palette-gray-8);
}
}
}
&.variant-subtle {
background-color: color-mod(var(--color-#{$color}) a(50%));
&[disabled] {
color: var(--palette-gray-4);
background-color: color-mod(var(--palette-gray-6) a(50%));
@include dark-mode {
color: var(--palette-gray-8);
background-color: color-mod(var(--palette-gray-6) a(50%));
}
}
}
&.variant-fill {
background-color: var(--color-#{$color});
&[disabled] {
color: var(--palette-gray-4);
background-color: var(--palette-gray-6);
@include dark-mode {
color: var(--palette-gray-8);
background-color: var(--palette-gray-6);
}
}
}
&:hover {
color: var(--palette-white-0);
&.variant-transparent,
&.variant-subtle,
&.variant-fill {
background-color: var(--color-#{$color}-hover);
}
}
&:active {
color: var(--palette-white-2);
&.variant-transparent {
color: var(--color-#{$color}-active);
background-color: transparent;
}
&.variant-subtle {
background-color: color-mod(var(--color-#{$color}-active) a(50%));
}
&.variant-fill {
background-color: var(--color-#{$color}-active);
}
}
}
}
&[disabled] {
border: none;
background: none;
pointer-events: none;
}
}

View File

@ -1,6 +1,6 @@
import { A } from "@solidjs/router";
import { defaultTo, isNil, pick, pipe, prop } from "ramda";
import { Show } from "solid-js";
import { Show, mergeProps } from "solid-js";
import classes from "./ActivatableLink.module.css";
/**
@ -24,17 +24,21 @@ const defaultActivatable = pipe(defaultTo(true), prop("activatable"));
* @returns {import("solid-js").Component<ActivatableLinkProps>}
*/
export default function ActivatableLink(props) {
const mergedProps = mergeProps(
{ activatable: true, leftIcon: null, rightIcon: null },
props
);
return (
<A
class={prop("activatable-link", classes)}
{...(defaultActivatable(props) && {
{...(prop("activatable", mergedProps) && {
activeClass: "activated",
})}
{...pick(["replace", "end", "href"], props)}
{...pick(["replace", "end", "href"], mergedProps)}
>
<Show when={!isNil(props.leftIcon)}>{props.leftIcon}</Show>
<Show when={!isNil(props.children)}>{props.children}</Show>
<Show when={!isNil(props.rightIcon)}>{props.rightIcon}</Show>
<Show when={!isNil(mergedProps.leftIcon)}>{mergedProps.leftIcon}</Show>
<Show when={!isNil(mergedProps.children)}>{mergedProps.children}</Show>
<Show when={!isNil(mergedProps.rightIcon)}>{mergedProps.rightIcon}</Show>
</A>
);
}

View File

@ -1,5 +1,6 @@
import { prop } from "ramda";
import classes from "./Spacer.module.css";
export default function Spacer() {
return <div class={classes.Spacer}></div>;
return <div class={prop("spacer", classes)}></div>;
}

View File

@ -0,0 +1,39 @@
import ActionIcon from "@/components/ActionIcon/ActionIcon";
import { useNavigate } from "@solidjs/router";
import { IconX } from "@tabler/icons-solidjs";
import { prop } from "ramda";
import { children, mergeProps } from "solid-js";
import classes from "./ContentLayout.module.css";
/**
* @typedef {Object} ContentLayoutProps
* @property {boolean} [disableClose]
* @property {JSX.Element} [title]
*/
/**
* @param {import("solid-js").ParentProps<ContentLayoutProps>} props
*/
export default function ContentLayout(props) {
const mergedProps = mergeProps({ disableClose: false, title: "" }, props);
const childContent = children(() => props.children);
const navigate = useNavigate();
return (
<div class={prop("content-layout", classes)}>
<section class={prop("title", classes)}>
<div class={prop("title-content", classes)}>{mergedProps.title}</div>
<ActionIcon
size="s"
color="secondary"
variant="transparent"
disabled={mergedProps.disableClose}
onClick={() => navigate("/", { replace: true })}
>
<IconX stroke="1.5" />
</ActionIcon>
</section>
<section>{childContent()}</section>
</div>
);
}

View File

@ -0,0 +1,22 @@
@import "@/mixins.css";
.content-layout {
@util size(100%, 100%);
@include flex(column, flex-start, stretch);
z-index: 100;
.title {
@util size(100%, 32px);
@util padding(var(--spacing-xs) var(--spacing-sm));
@include flex(row, flex-start, center, --spacing-xs);
@util border-bottom-radius(0px);
.title-content {
flex-grow: 1;
font-size: var(--font-size-s);
}
}
.content {
@util size(100%);
flex: 1;
overflow: hidden;
}
}

View File

@ -1,10 +1,14 @@
import ActionIcon from "@/components/ActionIcon/ActionIcon";
import ActivatableLink from "@/components/ActivatableLink/ActivatableLink";
import Divider from "@/components/Divider/Divider";
import Spacer from "@/components/Spacer/Spacer";
import {
IconBrandGithub,
IconDna2,
IconFileExport,
IconFileImport,
IconFolderOpen,
IconHelp,
IconKeyboard,
IconListSearch,
IconTextPlus,
@ -39,6 +43,15 @@ export default function NavigationSection() {
<ActivatableLink href="/" leftIcon={<IconKeyboard stroke="1.5" />}>
码表配置
</ActivatableLink>
<Spacer />
<div class={prop("footer", classes)}>
<ActionIcon size="m" color="primary" variant="transparent">
<IconBrandGithub stroke="1.5" />
</ActionIcon>
<ActionIcon size="m" color="secondary" variant="transparent">
<IconHelp stroke="1.5" />
</ActionIcon>
</div>
</aside>
);
}

View File

@ -9,4 +9,8 @@
.library-name {
font-size: var(--font-size-xs);
}
.footer {
width: 100%;
@include flex(row, flex-start, center);
}
}

View File

@ -8,7 +8,28 @@
line-height: 1.5;
--color-primary: var(--palette-bright-blue-6);
--color-danger: var(--palette-tomato-6);
--color-primary-hover: var(--palette-bright-blue-3);
--color-primary-active: var(--palette-bright-blue-4);
--color-secondary: var(--palette-gray-6);
--color-secondary-hover: var(--palette-gray-3);
--color-secondary-active: var(--palette-gray-4);
--color-danger: var(--palette-red-6);
--color-danger-hover: var(--palette-red-3);
--color-danger-active: var(--palette-red-4);
--color-warn: var(--palette-orange-6);
--color-warn-hover: var(--palette-orange-3);
--color-warn-active: var(--palette-orange-4);
--color-success: var(--palette-green-6);
--color-success-hover: var(--palette-green-3);
--color-success-active: var(--palette-green-4);
--color-info: var(--palette-blue-6);
--color-info-hover: var(--palette-blue-3);
--color-info-active: var(--palette-blue-4);
color: var(--color-light-fg);
background-color: var(--color-light-bg);