Merge pull request 'ui: use css modules for sidebar components' (#5217) from hgl into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/5217 Reviewed-by: brianmcgee <brian@bmcgee.ie>
This commit is contained in:
@@ -1,10 +1,9 @@
|
|||||||
import "./Divider.css";
|
import styles from "./Divider.module.css";
|
||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
import { Separator, SeparatorRootProps } from "@kobalte/core/separator";
|
import { Separator, SeparatorRootProps } from "@kobalte/core/separator";
|
||||||
|
|
||||||
export interface DividerProps extends Pick<SeparatorRootProps, "orientation"> {
|
export interface DividerProps extends Pick<SeparatorRootProps, "orientation"> {
|
||||||
inverted?: boolean;
|
inverted?: boolean;
|
||||||
class?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Divider = (props: DividerProps) => {
|
export const Divider = (props: DividerProps) => {
|
||||||
@@ -12,7 +11,7 @@ export const Divider = (props: DividerProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Separator
|
<Separator
|
||||||
class={cx({ inverted: inverted }, props?.class)}
|
class={cx({ [styles.inverted]: inverted })}
|
||||||
orientation={props.orientation}
|
orientation={props.orientation}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
div.sidebar-header {
|
|
||||||
@apply flex items-center justify-center w-full px-1 py-1;
|
|
||||||
@apply border border-inv-3 rounded-md rounded-bl-none rounded-br-none;
|
|
||||||
|
|
||||||
background: linear-gradient(
|
|
||||||
90deg,
|
|
||||||
theme(colors.bg.inv.2) 0%,
|
|
||||||
theme(colors.bg.inv.3) 0%
|
|
||||||
);
|
|
||||||
|
|
||||||
& > .dropdown-trigger {
|
|
||||||
@apply flex items-center justify-between flex-grow px-1 py-1;
|
|
||||||
@apply rounded-tl-md rounded-tr-md;
|
|
||||||
@apply border border-transparent border-b-0;
|
|
||||||
|
|
||||||
transition: all 250ms ease-in-out;
|
|
||||||
|
|
||||||
div.clan-label {
|
|
||||||
@apply flex items-center gap-2 justify-start;
|
|
||||||
|
|
||||||
& > .clan-icon {
|
|
||||||
@apply flex justify-center items-center;
|
|
||||||
@apply rounded-full bg-inv-4 w-7 h-7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon[data-icon-name="CaretDown"] {
|
|
||||||
transition: transform 300ms cubic-bezier(0.87, 0, 0.13, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-expanded] {
|
|
||||||
@apply bg-def-1 border-def-2;
|
|
||||||
|
|
||||||
.icon[data-icon-name="CaretDown"] {
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-dropdown-content {
|
|
||||||
@apply flex flex-col w-full px-2 py-1.5 z-10 gap-3;
|
|
||||||
@apply bg-def-1 rounded-bl-md rounded-br-md;
|
|
||||||
@apply border border-def-2;
|
|
||||||
|
|
||||||
animation: sidebarNavContentHide 250ms ease-in forwards;
|
|
||||||
|
|
||||||
.dropdown-item {
|
|
||||||
@apply flex items-center justify-start w-full px-1.5 py-2 gap-2 rounded;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
@apply bg-def-acc-2 cursor-pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-group {
|
|
||||||
@apply flex flex-col gap-2;
|
|
||||||
@apply px-1;
|
|
||||||
|
|
||||||
.dropdown-group-label {
|
|
||||||
@apply flex items-baseline justify-between w-full;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-group-items {
|
|
||||||
@apply rounded px-1 py-1.5 bg-def-2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-dropdown-content[data-expanded] {
|
|
||||||
animation: sidebarNavContentShow 250ms ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes sidebarNavContentShow {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: scale(0.96);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes sidebarNavContentHide {
|
|
||||||
from {
|
|
||||||
opacity: 1;
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 0;
|
|
||||||
transform: scale(0.96);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
.sidebarHeader {
|
||||||
|
@apply flex items-center justify-center w-full px-1 py-1;
|
||||||
|
@apply border border-inv-3 rounded-md rounded-bl-none rounded-br-none;
|
||||||
|
|
||||||
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
theme(colors.bg.inv.2) 0%,
|
||||||
|
theme(colors.bg.inv.3) 0%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropDownTrigger {
|
||||||
|
@apply flex items-center justify-between flex-grow px-1 py-1;
|
||||||
|
@apply rounded-tl-md rounded-tr-md;
|
||||||
|
@apply border border-transparent border-b-0;
|
||||||
|
|
||||||
|
transition: all 250ms ease-in-out;
|
||||||
|
|
||||||
|
.icon[data-icon-name="CaretDown"] {
|
||||||
|
transition: transform 300ms cubic-bezier(0.87, 0, 0.13, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-expanded] {
|
||||||
|
@apply bg-def-1 border-def-2;
|
||||||
|
|
||||||
|
.icon[data-icon-name="CaretDown"] {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.clanLabel {
|
||||||
|
@apply flex items-center gap-2 justify-start;
|
||||||
|
}
|
||||||
|
.clanIcon {
|
||||||
|
@apply flex justify-center items-center;
|
||||||
|
@apply rounded-full bg-inv-4 w-7 h-7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropDownContent {
|
||||||
|
@apply flex flex-col w-full px-2 py-1.5 z-10 gap-3;
|
||||||
|
@apply bg-def-1 rounded-bl-md rounded-br-md;
|
||||||
|
@apply border border-def-2;
|
||||||
|
|
||||||
|
animation: sidebarNavContentHide 250ms ease-in forwards;
|
||||||
|
}
|
||||||
|
.dropDownContent[data-expanded] {
|
||||||
|
animation: sidebarNavContentShow 250ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdownItem {
|
||||||
|
@apply flex items-center justify-start w-full px-1.5 py-2 gap-2 rounded;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
@apply bg-def-acc-2 cursor-pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdownGroup {
|
||||||
|
@apply flex flex-col gap-2;
|
||||||
|
@apply px-1;
|
||||||
|
}
|
||||||
|
.dropdownGroupLabel {
|
||||||
|
@apply flex items-baseline justify-between w-full;
|
||||||
|
}
|
||||||
|
.dropdownGroupItems {
|
||||||
|
@apply rounded px-1 py-1.5 bg-def-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sidebarNavContentShow {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.96);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes sidebarNavContentHide {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.96);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import "./SidebarHeader.css";
|
import styles from "./SidebarHeader.module.css";
|
||||||
import Icon from "@/src/components/Icon/Icon";
|
import Icon from "@/src/components/Icon/Icon";
|
||||||
import { DropdownMenu } from "@kobalte/core/dropdown-menu";
|
import { DropdownMenu } from "@kobalte/core/dropdown-menu";
|
||||||
import { useNavigate } from "@solidjs/router";
|
import { useNavigate } from "@solidjs/router";
|
||||||
@@ -30,7 +30,7 @@ export const SidebarHeader = () => {
|
|||||||
.sort((a, b) => a.details.name.localeCompare(b.details.name));
|
.sort((a, b) => a.details.name.localeCompare(b.details.name));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="sidebar-header">
|
<div class={styles.sidebarHeader}>
|
||||||
<Show when={ctx.activeClanQuery.isSuccess && showSettings()}>
|
<Show when={ctx.activeClanQuery.isSuccess && showSettings()}>
|
||||||
<ClanSettingsModal
|
<ClanSettingsModal
|
||||||
model={ctx.activeClanQuery.data!}
|
model={ctx.activeClanQuery.data!}
|
||||||
@@ -42,9 +42,9 @@ export const SidebarHeader = () => {
|
|||||||
</Show>
|
</Show>
|
||||||
<Suspense fallback={"Loading..."}>
|
<Suspense fallback={"Loading..."}>
|
||||||
<DropdownMenu open={open()} onOpenChange={setOpen} sameWidth={true}>
|
<DropdownMenu open={open()} onOpenChange={setOpen} sameWidth={true}>
|
||||||
<DropdownMenu.Trigger class="dropdown-trigger">
|
<DropdownMenu.Trigger class={styles.dropDownTrigger}>
|
||||||
<div class="clan-label">
|
<div class={styles.clanLabel}>
|
||||||
<div class="clan-icon">
|
<div class={styles.clanIcon}>
|
||||||
<Typography
|
<Typography
|
||||||
hierarchy="label"
|
hierarchy="label"
|
||||||
size="s"
|
size="s"
|
||||||
@@ -68,9 +68,9 @@ export const SidebarHeader = () => {
|
|||||||
</DropdownMenu.Icon>
|
</DropdownMenu.Icon>
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
<DropdownMenu.Portal>
|
<DropdownMenu.Portal>
|
||||||
<DropdownMenu.Content class="sidebar-dropdown-content">
|
<DropdownMenu.Content class={styles.dropDownContent}>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
class="dropdown-item"
|
class={styles.dropdownItem}
|
||||||
onSelect={() => setShowSettings(true)}
|
onSelect={() => setShowSettings(true)}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
@@ -83,8 +83,8 @@ export const SidebarHeader = () => {
|
|||||||
Settings
|
Settings
|
||||||
</Typography>
|
</Typography>
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Group class="dropdown-group">
|
<DropdownMenu.Group class={styles.dropdownGroup}>
|
||||||
<DropdownMenu.GroupLabel class="dropdown-group-label">
|
<DropdownMenu.GroupLabel class={styles.dropdownGroupLabel}>
|
||||||
<Typography
|
<Typography
|
||||||
hierarchy="label"
|
hierarchy="label"
|
||||||
family="mono"
|
family="mono"
|
||||||
@@ -104,12 +104,12 @@ export const SidebarHeader = () => {
|
|||||||
Add
|
Add
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenu.GroupLabel>
|
</DropdownMenu.GroupLabel>
|
||||||
<div class="dropdown-group-items">
|
<div class={styles.dropdownGroupItems}>
|
||||||
<For each={clanList()}>
|
<For each={clanList()}>
|
||||||
{(clan) => (
|
{(clan) => (
|
||||||
<Suspense fallback={"Loading..."}>
|
<Suspense fallback={"Loading..."}>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
class="dropdown-item"
|
class={styles.dropdownItem}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
setActiveClanURI(clan.uri);
|
setActiveClanURI(clan.uri);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,123 +0,0 @@
|
|||||||
div.sidebar-pane {
|
|
||||||
@apply flex flex-col border-none z-20 h-full;
|
|
||||||
|
|
||||||
animation: sidebarPaneShow 250ms ease-in forwards;
|
|
||||||
|
|
||||||
&.open {
|
|
||||||
@apply w-72;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.closing {
|
|
||||||
animation: sidebarPaneHide 250ms ease-out 300ms forwards;
|
|
||||||
|
|
||||||
& > div.header > *,
|
|
||||||
& > div.sub-header > *,
|
|
||||||
& > div.body > * {
|
|
||||||
animation: sidebarFadeOut 250ms ease-out forwards;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& > div.header {
|
|
||||||
@apply flex items-center justify-between px-3 py-2 rounded-t-[0.5rem];
|
|
||||||
@apply border-t-[1px] border-t-bg-inv-3
|
|
||||||
border-r-[1px] border-r-bg-inv-3
|
|
||||||
border-b-2 border-b-bg-inv-4
|
|
||||||
border-l-[1px] border-l-bg-inv-3;
|
|
||||||
|
|
||||||
background: linear-gradient(
|
|
||||||
90deg,
|
|
||||||
theme(colors.bg.inv.3) 0%,
|
|
||||||
theme(colors.bg.inv.4) 100%
|
|
||||||
);
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
@apply opacity-0;
|
|
||||||
animation: sidebarFadeIn 250ms ease-in 250ms forwards;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& > div.sub-header {
|
|
||||||
@apply px-3 py-1;
|
|
||||||
@apply border-b-[1px] border-b-bg-inv-4;
|
|
||||||
@apply border-r-[1px] border-r-bg-inv-3 border-l-[1px] border-l-bg-inv-3;
|
|
||||||
|
|
||||||
background:
|
|
||||||
linear-gradient(0deg, rgba(0, 0, 0, 0.08) 0%, rgba(0, 0, 0, 0.08) 100%),
|
|
||||||
linear-gradient(
|
|
||||||
90deg,
|
|
||||||
theme(colors.bg.inv.3) 0%,
|
|
||||||
theme(colors.bg.inv.4) 100%
|
|
||||||
);
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
@apply opacity-0;
|
|
||||||
animation: sidebarFadeIn 250ms ease-in 250ms forwards;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& > div.body {
|
|
||||||
@apply flex flex-col gap-4 px-2 pt-4 pb-3 w-full h-full;
|
|
||||||
@apply backdrop-blur-md;
|
|
||||||
@apply rounded-b-[0.5rem]
|
|
||||||
border-r-[1px] border-r-bg-inv-3
|
|
||||||
border-b-2 border-b-bg-inv-4
|
|
||||||
border-l-[1px] border-l-bg-inv-3;
|
|
||||||
|
|
||||||
background:
|
|
||||||
linear-gradient(0deg, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0.2) 100%),
|
|
||||||
linear-gradient(
|
|
||||||
180deg,
|
|
||||||
theme(colors.bg.inv.2) 0%,
|
|
||||||
theme(colors.bg.inv.3) 100%
|
|
||||||
);
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
@apply opacity-0;
|
|
||||||
animation: sidebarFadeIn 250ms ease-in 350ms forwards;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes sidebarPaneShow {
|
|
||||||
0% {
|
|
||||||
@apply w-0;
|
|
||||||
@apply opacity-0;
|
|
||||||
}
|
|
||||||
10% {
|
|
||||||
@apply w-8;
|
|
||||||
}
|
|
||||||
30% {
|
|
||||||
@apply opacity-100;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
@apply w-72;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes sidebarPaneHide {
|
|
||||||
90% {
|
|
||||||
@apply w-8;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
@apply w-0;
|
|
||||||
@apply opacity-0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes sidebarFadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes sidebarFadeOut {
|
|
||||||
from {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
120
pkgs/clan-app/ui/src/components/Sidebar/SidebarPane.module.css
Normal file
120
pkgs/clan-app/ui/src/components/Sidebar/SidebarPane.module.css
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
.sidebarPane {
|
||||||
|
@apply flex flex-col border-none z-20 h-full;
|
||||||
|
@apply w-72;
|
||||||
|
|
||||||
|
animation: sidebarPaneShow 250ms ease-in forwards;
|
||||||
|
|
||||||
|
&.closing {
|
||||||
|
animation: sidebarPaneHide 250ms ease-out 300ms forwards;
|
||||||
|
|
||||||
|
& > .header > *,
|
||||||
|
& > .subHeader > *,
|
||||||
|
& > .body > * {
|
||||||
|
animation: sidebarFadeOut 250ms ease-out forwards;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
@apply flex items-center justify-between px-3 py-2 rounded-t-[0.5rem];
|
||||||
|
@apply border-t-[1px] border-t-bg-inv-3
|
||||||
|
border-r-[1px] border-r-bg-inv-3
|
||||||
|
border-b-2 border-b-bg-inv-4
|
||||||
|
border-l-[1px] border-l-bg-inv-3;
|
||||||
|
|
||||||
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
theme(colors.bg.inv.3) 0%,
|
||||||
|
theme(colors.bg.inv.4) 100%
|
||||||
|
);
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
@apply opacity-0;
|
||||||
|
animation: sidebarFadeIn 250ms ease-in 250ms forwards;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.subHeader {
|
||||||
|
@apply px-3 py-1;
|
||||||
|
@apply border-b-[1px] border-b-bg-inv-4;
|
||||||
|
@apply border-r-[1px] border-r-bg-inv-3 border-l-[1px] border-l-bg-inv-3;
|
||||||
|
|
||||||
|
background:
|
||||||
|
linear-gradient(0deg, rgba(0, 0, 0, 0.08) 0%, rgba(0, 0, 0, 0.08) 100%),
|
||||||
|
linear-gradient(
|
||||||
|
90deg,
|
||||||
|
theme(colors.bg.inv.3) 0%,
|
||||||
|
theme(colors.bg.inv.4) 100%
|
||||||
|
);
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
@apply opacity-0;
|
||||||
|
animation: sidebarFadeIn 250ms ease-in 250ms forwards;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
@apply flex flex-col gap-4 px-2 pt-4 pb-3 w-full h-full;
|
||||||
|
@apply backdrop-blur-md;
|
||||||
|
@apply rounded-b-[0.5rem]
|
||||||
|
border-r-[1px] border-r-bg-inv-3
|
||||||
|
border-b-2 border-b-bg-inv-4
|
||||||
|
border-l-[1px] border-l-bg-inv-3;
|
||||||
|
|
||||||
|
background:
|
||||||
|
linear-gradient(0deg, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0.2) 100%),
|
||||||
|
linear-gradient(
|
||||||
|
180deg,
|
||||||
|
theme(colors.bg.inv.2) 0%,
|
||||||
|
theme(colors.bg.inv.3) 100%
|
||||||
|
);
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
@apply opacity-0;
|
||||||
|
animation: sidebarFadeIn 250ms ease-in 350ms forwards;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sidebarPaneShow {
|
||||||
|
0% {
|
||||||
|
@apply w-0;
|
||||||
|
@apply opacity-0;
|
||||||
|
}
|
||||||
|
10% {
|
||||||
|
@apply w-8;
|
||||||
|
}
|
||||||
|
30% {
|
||||||
|
@apply opacity-100;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
@apply w-72;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sidebarPaneHide {
|
||||||
|
90% {
|
||||||
|
@apply w-8;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
@apply w-0;
|
||||||
|
@apply opacity-0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sidebarFadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sidebarFadeOut {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -158,7 +158,7 @@ export const Default: Story = {
|
|||||||
</Field>
|
</Field>
|
||||||
)}
|
)}
|
||||||
</SidebarSectionForm>
|
</SidebarSectionForm>
|
||||||
<SidebarSection title="Simple" class="flex flex-col">
|
<SidebarSection title="Simple">
|
||||||
<Typography tag="h2" hierarchy="title" size="m" inverted>
|
<Typography tag="h2" hierarchy="title" size="m" inverted>
|
||||||
Static Content
|
Static Content
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { createSignal, JSX, onMount, Show } from "solid-js";
|
import { createSignal, JSX, Show } from "solid-js";
|
||||||
import "./SidebarPane.css";
|
import styles from "./SidebarPane.module.css";
|
||||||
import { Typography } from "@/src/components/Typography/Typography";
|
import { Typography } from "@/src/components/Typography/Typography";
|
||||||
import Icon from "../Icon/Icon";
|
import Icon from "../Icon/Icon";
|
||||||
import { Button as KButton } from "@kobalte/core/button";
|
import { Button as KButton } from "@kobalte/core/button";
|
||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
|
|
||||||
export interface SidebarPaneProps {
|
export interface SidebarPaneProps {
|
||||||
class?: string;
|
|
||||||
title: string;
|
title: string;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
subHeader?: JSX.Element;
|
subHeader?: JSX.Element;
|
||||||
@@ -15,26 +14,20 @@ export interface SidebarPaneProps {
|
|||||||
|
|
||||||
export const SidebarPane = (props: SidebarPaneProps) => {
|
export const SidebarPane = (props: SidebarPaneProps) => {
|
||||||
const [closing, setClosing] = createSignal(false);
|
const [closing, setClosing] = createSignal(false);
|
||||||
const [open, setOpened] = createSignal(true);
|
|
||||||
|
|
||||||
|
// FIXME: use animationend event instead of setTimeout
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
setClosing(true);
|
setClosing(true);
|
||||||
setTimeout(() => props.onClose(), 550);
|
setTimeout(() => props.onClose(), 550);
|
||||||
};
|
};
|
||||||
onMount(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
setOpened(true);
|
|
||||||
}, 250);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class={cx("sidebar-pane", props.class, {
|
class={cx(styles.sidebarPane, {
|
||||||
closing: closing(),
|
[styles.closing]: closing(),
|
||||||
open: open(),
|
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div class="header">
|
<div class={styles.header}>
|
||||||
<Typography hierarchy="body" size="s" weight="bold" inverted={true}>
|
<Typography hierarchy="body" size="s" weight="bold" inverted={true}>
|
||||||
{props.title}
|
{props.title}
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -43,9 +36,9 @@ export const SidebarPane = (props: SidebarPaneProps) => {
|
|||||||
</KButton>
|
</KButton>
|
||||||
</div>
|
</div>
|
||||||
<Show when={props.subHeader}>
|
<Show when={props.subHeader}>
|
||||||
<div class="sub-header">{props.subHeader}</div>
|
<div class={styles.subHeader}>{props.subHeader}</div>
|
||||||
</Show>
|
</Show>
|
||||||
<div class="body">{props.children}</div>
|
<div class={styles.body}>{props.children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
div.sidebar-section {
|
|
||||||
@apply flex flex-col gap-2 w-full h-full;
|
|
||||||
|
|
||||||
& > div.header {
|
|
||||||
@apply flex items-center justify-between px-1.5;
|
|
||||||
|
|
||||||
& > div.controls {
|
|
||||||
@apply flex items-center justify-end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& > div.content {
|
|
||||||
@apply w-full h-fit px-1.5 py-3 rounded-md bg-inv-4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
.sidebarSection {
|
||||||
|
@apply flex flex-col gap-2 w-full h-full;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
@apply flex items-center justify-between px-1.5;
|
||||||
|
}
|
||||||
|
.controls {
|
||||||
|
@apply flex items-center justify-end h-4;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
@apply w-full h-fit px-1.5 py-3 rounded-md bg-inv-4;
|
||||||
|
}
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
import { JSX } from "solid-js";
|
import { JSX, Show } from "solid-js";
|
||||||
import "./SidebarSection.css";
|
import styles from "./SidebarSection.module.css";
|
||||||
import { Typography } from "@/src/components/Typography/Typography";
|
import { Typography } from "@/src/components/Typography/Typography";
|
||||||
import cx from "classnames";
|
|
||||||
|
|
||||||
export interface SidebarSectionProps {
|
export interface SidebarSectionProps {
|
||||||
title: string;
|
title: string;
|
||||||
class?: string;
|
controls?: JSX.Element;
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SidebarSection = (props: SidebarSectionProps) => {
|
export const SidebarSection = (props: SidebarSectionProps) => {
|
||||||
return (
|
return (
|
||||||
<div class={cx("sidebar-section", props.class)}>
|
<div class={styles.sidebarSection}>
|
||||||
<div class="header">
|
<div class={styles.header}>
|
||||||
<Typography
|
<Typography
|
||||||
hierarchy="label"
|
hierarchy="label"
|
||||||
size="xs"
|
size="xs"
|
||||||
@@ -23,8 +22,11 @@ export const SidebarSection = (props: SidebarSectionProps) => {
|
|||||||
>
|
>
|
||||||
{props.title}
|
{props.title}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<Show when={props.controls}>
|
||||||
|
<div class={styles.controls}>{props.controls}</div>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">{props.children}</div>
|
<div class={styles.content}>{props.children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import { GenericSchema, GenericSchemaAsync } from "valibot";
|
|||||||
import { Typography } from "@/src/components/Typography/Typography";
|
import { Typography } from "@/src/components/Typography/Typography";
|
||||||
import { Button } from "@/src/components/Button/Button";
|
import { Button } from "@/src/components/Button/Button";
|
||||||
|
|
||||||
import "./SidebarSection.css";
|
|
||||||
import { Loader } from "../../components/Loader/Loader";
|
import { Loader } from "../../components/Loader/Loader";
|
||||||
|
import { SidebarSection } from "./SidebarSection";
|
||||||
|
|
||||||
export interface SidebarSectionFormProps<FormValues extends FieldValues> {
|
export interface SidebarSectionFormProps<FormValues extends FieldValues> {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -71,31 +71,24 @@ export function SidebarSectionForm<
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Form onSubmit={handleSubmit}>
|
<Form onSubmit={handleSubmit}>
|
||||||
<div class="sidebar-section">
|
<SidebarSection
|
||||||
<div class="header">
|
title={props.title}
|
||||||
<Typography
|
controls={
|
||||||
hierarchy="label"
|
<>
|
||||||
size="xs"
|
{editing() &&
|
||||||
family="mono"
|
(formStore.submitting ? (
|
||||||
transform="uppercase"
|
<Loader />
|
||||||
color="tertiary"
|
) : (
|
||||||
inverted
|
<Button
|
||||||
>
|
hierarchy="primary"
|
||||||
{props.title}
|
size="xs"
|
||||||
</Typography>
|
icon="Checkmark"
|
||||||
<div class="controls h-4">
|
ghost
|
||||||
{editing() && !formStore.submitting && (
|
type="submit"
|
||||||
<Button
|
>
|
||||||
hierarchy="primary"
|
Save
|
||||||
size="xs"
|
</Button>
|
||||||
icon="Checkmark"
|
))}
|
||||||
ghost
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{editing() && formStore.submitting && <Loader />}
|
|
||||||
<Button
|
<Button
|
||||||
hierarchy="primary"
|
hierarchy="primary"
|
||||||
ghost
|
ghost
|
||||||
@@ -103,19 +96,18 @@ export function SidebarSectionForm<
|
|||||||
icon={editing() ? "Close" : "Edit"}
|
icon={editing() ? "Close" : "Edit"}
|
||||||
onClick={editOrClose}
|
onClick={editOrClose}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Show when={editing() && formStore.dirty && errorMessage()}>
|
||||||
|
<div class="mb-2.5" role="alert" aria-live="assertive">
|
||||||
|
<Typography hierarchy="body" size="xs" inverted color="error">
|
||||||
|
{errorMessage()}
|
||||||
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Show>
|
||||||
<div class="content">
|
{props.children({ editing: editing(), Field, formStore })}
|
||||||
<Show when={editing() && formStore.dirty && errorMessage()}>
|
</SidebarSection>
|
||||||
<div class="mb-2.5" role="alert" aria-live="assertive">
|
|
||||||
<Typography hierarchy="body" size="xs" inverted color="error">
|
|
||||||
{errorMessage()}
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
{props.children({ editing: editing(), Field, formStore })}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user