mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 10:31:47 +00:00
fix(in-app-menu): fix app crash in production
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
import { createSignal } from 'solid-js';
|
import { createSignal } from 'solid-js';
|
||||||
import { render } from 'solid-js/web';
|
import { render } from 'solid-js/web';
|
||||||
|
import { extractCss } from 'solid-styled-components';
|
||||||
|
|
||||||
import { TitleBar } from './renderer/TitleBar';
|
import { TitleBar } from './renderer/TitleBar';
|
||||||
import { defaultInAppMenuConfig, InAppMenuConfig } from './constants';
|
import { defaultInAppMenuConfig, InAppMenuConfig } from './constants';
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import { JSX } from 'solid-js';
|
import { JSX } from 'solid-js';
|
||||||
import { css } from 'solid-styled-components';
|
import { css } from 'solid-styled-components';
|
||||||
|
|
||||||
const iconButton = css`
|
import { cache } from '@/providers/decorators';
|
||||||
|
|
||||||
|
const iconButton = cache(() => css`
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
||||||
width: 24px;
|
width: 24px;
|
||||||
@ -28,12 +30,12 @@ const iconButton = css`
|
|||||||
&:active {
|
&:active {
|
||||||
scale: 0.9;
|
scale: 0.9;
|
||||||
}
|
}
|
||||||
`;
|
`);
|
||||||
|
|
||||||
type CollapseIconButtonProps = JSX.HTMLAttributes<HTMLButtonElement>;
|
type CollapseIconButtonProps = JSX.HTMLAttributes<HTMLButtonElement>;
|
||||||
export const IconButton = (props: CollapseIconButtonProps) => {
|
export const IconButton = (props: CollapseIconButtonProps) => {
|
||||||
return (
|
return (
|
||||||
<button {...props} class={iconButton}>
|
<button {...props} class={iconButton()}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { JSX, splitProps } from 'solid-js';
|
import { JSX, splitProps } from 'solid-js';
|
||||||
import { css } from 'solid-styled-components';
|
import { css } from 'solid-styled-components';
|
||||||
|
import { cache } from '@/providers/decorators';
|
||||||
|
|
||||||
const menuStyle = css`
|
const menuStyle = cache(() => css`
|
||||||
-webkit-app-region: none;
|
-webkit-app-region: none;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -25,7 +26,7 @@ const menuStyle = css`
|
|||||||
&[data-selected="true"] {
|
&[data-selected="true"] {
|
||||||
background-color: rgba(255, 255, 255, 0.2);
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
`;
|
`);
|
||||||
|
|
||||||
export type MenuButtonProps = JSX.HTMLAttributes<HTMLLIElement> & {
|
export type MenuButtonProps = JSX.HTMLAttributes<HTMLLIElement> & {
|
||||||
text?: string;
|
text?: string;
|
||||||
@ -35,7 +36,7 @@ export const MenuButton = (props: MenuButtonProps) => {
|
|||||||
const [local, leftProps] = splitProps(props, ['text']);
|
const [local, leftProps] = splitProps(props, ['text']);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li {...leftProps} class={menuStyle} data-selected={props.selected}>
|
<li {...leftProps} class={menuStyle()} data-selected={props.selected}>
|
||||||
{local.text}
|
{local.text}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -4,8 +4,9 @@ import { css } from 'solid-styled-components';
|
|||||||
import { Transition } from 'solid-transition-group';
|
import { Transition } from 'solid-transition-group';
|
||||||
import { autoUpdate, flip, offset, OffsetOptions, size } from '@floating-ui/dom';
|
import { autoUpdate, flip, offset, OffsetOptions, size } from '@floating-ui/dom';
|
||||||
import { useFloating } from 'solid-floating-ui';
|
import { useFloating } from 'solid-floating-ui';
|
||||||
|
import { cache } from '@/providers/decorators';
|
||||||
|
|
||||||
const panelStyle = css`
|
const panelStyle = cache(() => css`
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: var(--offset-y, 0);
|
top: var(--offset-y, 0);
|
||||||
left: var(--offset-x, 0);
|
left: var(--offset-x, 0);
|
||||||
@ -32,9 +33,9 @@ const panelStyle = css`
|
|||||||
0 2px 8px rgba(0, 0, 0, 0.2);
|
0 2px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
|
||||||
transform-origin: var(--origin-x, 50%) var(--origin-y, 50%);
|
transform-origin: var(--origin-x, 50%) var(--origin-y, 50%);
|
||||||
`;
|
`);
|
||||||
|
|
||||||
const animationStyle = {
|
const animationStyle = cache(() => ({
|
||||||
enter: css`
|
enter: css`
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scale(0.9);
|
transform: scale(0.9);
|
||||||
@ -49,7 +50,7 @@ const animationStyle = {
|
|||||||
exitActive: css`
|
exitActive: css`
|
||||||
transition: opacity 0.225s cubic-bezier(0.32, 0, 0.67, 0), transform 0.225s cubic-bezier(0.32, 0, 0.67, 0);
|
transition: opacity 0.225s cubic-bezier(0.32, 0, 0.67, 0), transform 0.225s cubic-bezier(0.32, 0, 0.67, 0);
|
||||||
`,
|
`,
|
||||||
};
|
}));
|
||||||
|
|
||||||
export type Placement =
|
export type Placement =
|
||||||
'top'
|
'top'
|
||||||
@ -122,16 +123,16 @@ export const Panel = (props: PanelProps) => {
|
|||||||
<Portal>
|
<Portal>
|
||||||
<Transition
|
<Transition
|
||||||
appear
|
appear
|
||||||
enterClass={animationStyle.enter}
|
enterClass={animationStyle().enter}
|
||||||
enterActiveClass={animationStyle.enterActive}
|
enterActiveClass={animationStyle().enterActive}
|
||||||
exitToClass={animationStyle.exitTo}
|
exitToClass={animationStyle().exitTo}
|
||||||
exitActiveClass={animationStyle.exitActive}
|
exitActiveClass={animationStyle().exitActive}
|
||||||
>
|
>
|
||||||
<Show when={local.open}>
|
<Show when={local.open}>
|
||||||
<ul
|
<ul
|
||||||
{...leftProps}
|
{...leftProps}
|
||||||
ref={setPanel}
|
ref={setPanel}
|
||||||
class={panelStyle}
|
class={panelStyle()}
|
||||||
style={{
|
style={{
|
||||||
'--offset-x': `${position.x}px`,
|
'--offset-x': `${position.x}px`,
|
||||||
'--offset-y': `${position.y}px`,
|
'--offset-y': `${position.y}px`,
|
||||||
|
|||||||
@ -8,117 +8,119 @@ import { useFloating } from 'solid-floating-ui';
|
|||||||
import { autoUpdate, offset, size } from '@floating-ui/dom';
|
import { autoUpdate, offset, size } from '@floating-ui/dom';
|
||||||
|
|
||||||
import { Panel } from './Panel';
|
import { Panel } from './Panel';
|
||||||
|
import { cache } from '@/providers/decorators';
|
||||||
|
|
||||||
const itemStyle = css`
|
const itemStyle = cache(() => css`
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
-webkit-app-region: none;
|
-webkit-app-region: none;
|
||||||
min-height: 32px;
|
min-height: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 32px 1fr auto minmax(32px, auto);
|
grid-template-columns: 32px 1fr auto minmax(32px, auto);
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
-webkit-user-drag: none;
|
-webkit-user-drag: none;
|
||||||
|
|
||||||
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(255, 255, 255, 0.1);
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
background-color: rgba(255, 255, 255, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-selected="true"] {
|
|
||||||
background-color: rgba(255, 255, 255, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
& * {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const itemIconStyle = css`
|
|
||||||
height: 32px;
|
|
||||||
padding: 4px;
|
|
||||||
color: white;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const itemLabelStyle = css`
|
|
||||||
font-size: 12px;
|
|
||||||
color: white;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const itemChipStyle = css`
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
min-width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
padding: 0 4px;
|
|
||||||
margin-left: 8px;
|
|
||||||
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: rgba(255, 255, 255, 0.2);
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
color: #f1f1f1;
|
}
|
||||||
font-size: 10px;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const toolTipStyle = css`
|
&[data-selected="true"] {
|
||||||
min-width: 32px;
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
width: 100%;
|
}
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
padding: 4px;
|
& * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
max-width: calc(var(--max-width, 100%) - 8px);
|
const itemIconStyle = cache(() => css`
|
||||||
max-height: calc(var(--max-height, 100%) - 8px);
|
height: 32px;
|
||||||
|
padding: 4px;
|
||||||
|
color: white;
|
||||||
|
`);
|
||||||
|
|
||||||
border-radius: 4px;
|
const itemLabelStyle = cache(() => css`
|
||||||
background-color: rgba(25, 25, 25, 0.8);
|
font-size: 12px;
|
||||||
color: #f1f1f1;
|
color: white;
|
||||||
font-size: 10px;
|
`);
|
||||||
`;
|
|
||||||
|
|
||||||
const popupStyle = css`
|
const itemChipStyle = cache(() => css`
|
||||||
position: fixed;
|
display: flex;
|
||||||
top: var(--offset-y, 0);
|
justify-content: center;
|
||||||
left: var(--offset-x, 0);
|
align-items: center;
|
||||||
|
|
||||||
max-width: var(--max-width, 100%);
|
min-width: 16px;
|
||||||
max-height: var(--max-height, 100%);
|
height: 16px;
|
||||||
|
padding: 0 4px;
|
||||||
|
margin-left: 8px;
|
||||||
|
|
||||||
z-index: 100000000;
|
border-radius: 4px;
|
||||||
pointer-events: none;
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
color: #f1f1f1;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
`);
|
||||||
|
|
||||||
`;
|
const toolTipStyle = cache(() => css`
|
||||||
const animationStyle = {
|
min-width: 32px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
padding: 4px;
|
||||||
|
|
||||||
|
max-width: calc(var(--max-width, 100%) - 8px);
|
||||||
|
max-height: calc(var(--max-height, 100%) - 8px);
|
||||||
|
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: rgba(25, 25, 25, 0.8);
|
||||||
|
color: #f1f1f1;
|
||||||
|
font-size: 10px;
|
||||||
|
`);
|
||||||
|
|
||||||
|
const popupStyle = cache(() => css`
|
||||||
|
position: fixed;
|
||||||
|
top: var(--offset-y, 0);
|
||||||
|
left: var(--offset-x, 0);
|
||||||
|
|
||||||
|
max-width: var(--max-width, 100%);
|
||||||
|
max-height: var(--max-height, 100%);
|
||||||
|
|
||||||
|
z-index: 100000000;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
`);
|
||||||
|
|
||||||
|
const animationStyle = cache(() => ({
|
||||||
enter: css`
|
enter: css`
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scale(0.9);
|
transform: scale(0.9);
|
||||||
`,
|
`,
|
||||||
enterActive: css`
|
enterActive: css`
|
||||||
transition: opacity 0.225s cubic-bezier(0.33, 1, 0.68, 1), transform 0.225s cubic-bezier(0.33, 1, 0.68, 1);
|
transition: opacity 0.225s cubic-bezier(0.33, 1, 0.68, 1), transform 0.225s cubic-bezier(0.33, 1, 0.68, 1);
|
||||||
`,
|
`,
|
||||||
exitTo: css`
|
exitTo: css`
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scale(0.9);
|
transform: scale(0.9);
|
||||||
`,
|
`,
|
||||||
exitActive: css`
|
exitActive: css`
|
||||||
transition: opacity 0.225s cubic-bezier(0.32, 0, 0.67, 0), transform 0.225s cubic-bezier(0.32, 0, 0.67, 0);
|
transition: opacity 0.225s cubic-bezier(0.32, 0, 0.67, 0), transform 0.225s cubic-bezier(0.32, 0, 0.67, 0);
|
||||||
`,
|
`,
|
||||||
};
|
}));
|
||||||
|
|
||||||
const getParents = (element: Element | null): (HTMLElement | null)[] => {
|
const getParents = (element: Element | null): (HTMLElement | null)[] => {
|
||||||
const parents: (HTMLElement | null)[] = [];
|
const parents: (HTMLElement | null)[] = [];
|
||||||
@ -211,7 +213,7 @@ export const PanelItem = (props: PanelItemProps) => {
|
|||||||
const closestLevel = parents.find((it) => it?.dataset?.level)?.dataset.level ?? '';
|
const closestLevel = parents.find((it) => it?.dataset?.level)?.dataset.level ?? '';
|
||||||
const path = event.composedPath();
|
const path = event.composedPath();
|
||||||
|
|
||||||
const isOtherItem = path.some((it) => it instanceof HTMLElement && it.classList.contains(itemStyle));
|
const isOtherItem = path.some((it) => it instanceof HTMLElement && it.classList.contains(itemStyle()));
|
||||||
const isChild = closestLevel.startsWith(props.level.join('/'));
|
const isChild = closestLevel.startsWith(props.level.join('/'));
|
||||||
|
|
||||||
if (isOtherItem && !isChild) {
|
if (isOtherItem && !isChild) {
|
||||||
@ -246,14 +248,14 @@ export const PanelItem = (props: PanelItemProps) => {
|
|||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
ref={setAnchor}
|
ref={setAnchor}
|
||||||
class={itemStyle}
|
class={itemStyle()}
|
||||||
onMouseEnter={handleHover}
|
onMouseEnter={handleHover}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
data-selected={open()}
|
data-selected={open()}
|
||||||
>
|
>
|
||||||
<Switch fallback={<div class={itemIconStyle}/>}>
|
<Switch fallback={<div class={itemIconStyle()}/>}>
|
||||||
<Match when={props.type === 'checkbox' && props.checked}>
|
<Match when={props.type === 'checkbox' && props.checked}>
|
||||||
<svg class={itemIconStyle} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="1.5"
|
<svg class={itemIconStyle()} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="1.5"
|
||||||
stroke="currentColor" fill="none"
|
stroke="currentColor" fill="none"
|
||||||
stroke-linecap="round" stroke-linejoin="round">
|
stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||||
@ -261,28 +263,30 @@ export const PanelItem = (props: PanelItemProps) => {
|
|||||||
</svg>
|
</svg>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={props.type === 'radio' && props.checked}>
|
<Match when={props.type === 'radio' && props.checked}>
|
||||||
<svg class={itemIconStyle} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" style={{ padding: '6px' }}>
|
<svg class={itemIconStyle()} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
||||||
|
style={{ padding: '6px' }}>
|
||||||
<path fill="currentColor"
|
<path fill="currentColor"
|
||||||
d="M10,5 C7.2,5 5,7.2 5,10 C5,12.8 7.2,15 10,15 C12.8,15 15,12.8 15,10 C15,7.2 12.8,5 10,5 L10,5 Z M10,0 C4.5,0 0,4.5 0,10 C0,15.5 4.5,20 10,20 C15.5,20 20,15.5 20,10 C20,4.5 15.5,0 10,0 L10,0 Z M10,18 C5.6,18 2,14.4 2,10 C2,5.6 5.6,2 10,2 C14.4,2 18,5.6 18,10 C18,14.4 14.4,18 10,18 L10,18 Z"/>
|
d="M10,5 C7.2,5 5,7.2 5,10 C5,12.8 7.2,15 10,15 C12.8,15 15,12.8 15,10 C15,7.2 12.8,5 10,5 L10,5 Z M10,0 C4.5,0 0,4.5 0,10 C0,15.5 4.5,20 10,20 C15.5,20 20,15.5 20,10 C20,4.5 15.5,0 10,0 L10,0 Z M10,18 C5.6,18 2,14.4 2,10 C2,5.6 5.6,2 10,2 C14.4,2 18,5.6 18,10 C18,14.4 14.4,18 10,18 L10,18 Z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={props.type === 'radio' && !props.checked}>
|
<Match when={props.type === 'radio' && !props.checked}>
|
||||||
<svg class={itemIconStyle} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" style={{ padding: '6px' }}>
|
<svg class={itemIconStyle()} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
||||||
|
style={{ padding: '6px' }}>
|
||||||
<path fill="currentColor"
|
<path fill="currentColor"
|
||||||
d="M10,0 C4.5,0 0,4.5 0,10 C0,15.5 4.5,20 10,20 C15.5,20 20,15.5 20,10 C20,4.5 15.5,0 10,0 L10,0 Z M10,18 C5.6,18 2,14.4 2,10 C2,5.6 5.6,2 10,2 C14.4,2 18,5.6 18,10 C18,14.4 14.4,18 10,18 L10,18 Z"/>
|
d="M10,0 C4.5,0 0,4.5 0,10 C0,15.5 4.5,20 10,20 C15.5,20 20,15.5 20,10 C20,4.5 15.5,0 10,0 L10,0 Z M10,18 C5.6,18 2,14.4 2,10 C2,5.6 5.6,2 10,2 C14.4,2 18,5.6 18,10 C18,14.4 14.4,18 10,18 L10,18 Z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
<span class={itemLabelStyle}>
|
<span class={itemLabelStyle()}>
|
||||||
{props.name}
|
{props.name}
|
||||||
</span>
|
</span>
|
||||||
<Show when={props.chip} fallback={<div/>}>
|
<Show when={props.chip} fallback={<div/>}>
|
||||||
<span class={itemChipStyle}>
|
<span class={itemChipStyle()}>
|
||||||
{props.chip}
|
{props.chip}
|
||||||
</span>
|
</span>
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={props.type === 'submenu'}>
|
<Show when={props.type === 'submenu'}>
|
||||||
<svg class={itemIconStyle} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="1.5"
|
<svg class={itemIconStyle()} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="1.5"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke-linecap="round" stroke-linejoin="round">
|
stroke-linecap="round" stroke-linejoin="round">
|
||||||
@ -304,7 +308,7 @@ export const PanelItem = (props: PanelItemProps) => {
|
|||||||
<Portal>
|
<Portal>
|
||||||
<div
|
<div
|
||||||
ref={setToolTip}
|
ref={setToolTip}
|
||||||
class={popupStyle}
|
class={popupStyle()}
|
||||||
style={{
|
style={{
|
||||||
'--offset-x': `${position.x}px`,
|
'--offset-x': `${position.x}px`,
|
||||||
'--offset-y': `${position.y}px`,
|
'--offset-y': `${position.y}px`,
|
||||||
@ -312,13 +316,13 @@ export const PanelItem = (props: PanelItemProps) => {
|
|||||||
>
|
>
|
||||||
<Transition
|
<Transition
|
||||||
appear
|
appear
|
||||||
enterClass={animationStyle.enter}
|
enterClass={animationStyle().enter}
|
||||||
enterActiveClass={animationStyle.enterActive}
|
enterActiveClass={animationStyle().enterActive}
|
||||||
exitToClass={animationStyle.exitTo}
|
exitToClass={animationStyle().exitTo}
|
||||||
exitActiveClass={animationStyle.exitActive}
|
exitActiveClass={animationStyle().exitActive}
|
||||||
>
|
>
|
||||||
<Show when={toolTipOpen()}>
|
<Show when={toolTipOpen()}>
|
||||||
<div class={toolTipStyle}>
|
<div class={toolTipStyle()}>
|
||||||
{props.toolTip}
|
{props.toolTip}
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
@ -11,8 +11,9 @@ import { WindowController } from './WindowController';
|
|||||||
|
|
||||||
import type { RendererContext } from '@/types/contexts';
|
import type { RendererContext } from '@/types/contexts';
|
||||||
import type { InAppMenuConfig } from '../constants';
|
import type { InAppMenuConfig } from '../constants';
|
||||||
|
import { cache } from '@/providers/decorators';
|
||||||
|
|
||||||
const titleStyle = css`
|
const titleStyle = cache(() => css`
|
||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
@ -41,17 +42,17 @@ const titleStyle = css`
|
|||||||
&[data-macos="true"] {
|
&[data-macos="true"] {
|
||||||
padding: 4px 4px 4px 74px;
|
padding: 4px 4px 4px 74px;
|
||||||
}
|
}
|
||||||
`;
|
`);
|
||||||
|
|
||||||
const separatorStyle = css`
|
const separatorStyle = cache(() => css`
|
||||||
min-height: 1px;
|
min-height: 1px;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
margin: 4px 0;
|
margin: 4px 0;
|
||||||
|
|
||||||
background-color: rgba(255, 255, 255, 0.2);
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
`;
|
`);
|
||||||
|
|
||||||
const animationStyle = {
|
const animationStyle = cache(() => ({
|
||||||
enter: css`
|
enter: css`
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(-50%) scale(0.8);
|
transform: translateX(-50%) scale(0.8);
|
||||||
@ -76,7 +77,7 @@ const animationStyle = {
|
|||||||
fake: css`
|
fake: css`
|
||||||
transition: all 0.00000000001s;
|
transition: all 0.00000000001s;
|
||||||
`,
|
`,
|
||||||
};
|
}));
|
||||||
|
|
||||||
export type PanelRendererProps = {
|
export type PanelRendererProps = {
|
||||||
items: Electron.Menu['items'];
|
items: Electron.Menu['items'];
|
||||||
@ -140,7 +141,7 @@ const PanelRenderer = (props: PanelRendererProps) => {
|
|||||||
/>
|
/>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={subItem().type === 'separator'}>
|
<Match when={subItem().type === 'separator'}>
|
||||||
<hr class={separatorStyle}/>
|
<hr class={separatorStyle()}/>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</Show>
|
</Show>
|
||||||
@ -251,7 +252,7 @@ export const TitleBar = (props: TitleBarProps) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav class={titleStyle} data-macos={props.isMacOS}>
|
<nav class={titleStyle()} data-macos={props.isMacOS}>
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => setCollapsed(!collapsed())}
|
onClick={() => setCollapsed(!collapsed())}
|
||||||
style={{
|
style={{
|
||||||
@ -266,10 +267,10 @@ export const TitleBar = (props: TitleBarProps) => {
|
|||||||
</svg>
|
</svg>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<TransitionGroup
|
<TransitionGroup
|
||||||
enterClass={ignoreTransition() ? animationStyle.fakeTarget : animationStyle.enter}
|
enterClass={ignoreTransition() ? animationStyle().fakeTarget : animationStyle().enter}
|
||||||
enterActiveClass={ignoreTransition() ? animationStyle.fake : animationStyle.enterActive}
|
enterActiveClass={ignoreTransition() ? animationStyle().fake : animationStyle().enterActive}
|
||||||
exitToClass={ignoreTransition() ? animationStyle.fakeTarget : animationStyle.exitTo}
|
exitToClass={ignoreTransition() ? animationStyle().fakeTarget : animationStyle().exitTo}
|
||||||
exitActiveClass={ignoreTransition() ? animationStyle.fake : animationStyle.exitActive}
|
exitActiveClass={ignoreTransition() ? animationStyle().fake : animationStyle().exitActive}
|
||||||
onBeforeEnter={(element) => {
|
onBeforeEnter={(element) => {
|
||||||
if (ignoreTransition()) return;
|
if (ignoreTransition()) return;
|
||||||
const index = Number(element.getAttribute('data-index') ?? 0);
|
const index = Number(element.getAttribute('data-index') ?? 0);
|
||||||
|
|||||||
@ -2,8 +2,9 @@ import { css } from 'solid-styled-components';
|
|||||||
import { Show } from 'solid-js';
|
import { Show } from 'solid-js';
|
||||||
|
|
||||||
import { IconButton } from './IconButton';
|
import { IconButton } from './IconButton';
|
||||||
|
import { cache } from '@/providers/decorators';
|
||||||
|
|
||||||
const containerStyle = css`
|
const containerStyle = cache(() => css`
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -15,7 +16,7 @@ const containerStyle = css`
|
|||||||
background: rgba(255, 0, 0, 0.5);
|
background: rgba(255, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`);
|
||||||
|
|
||||||
export type WindowControllerProps = {
|
export type WindowControllerProps = {
|
||||||
isMaximize?: boolean;
|
isMaximize?: boolean;
|
||||||
@ -26,7 +27,7 @@ export type WindowControllerProps = {
|
|||||||
}
|
}
|
||||||
export const WindowController = (props: WindowControllerProps) => {
|
export const WindowController = (props: WindowControllerProps) => {
|
||||||
return (
|
return (
|
||||||
<div class={containerStyle}>
|
<div class={containerStyle()}>
|
||||||
<IconButton onClick={props.onMinimize}>
|
<IconButton onClick={props.onMinimize}>
|
||||||
<svg width={16} height={16} fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg width={16} height={16} fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill="currentColor" d="M3.755 12.5h16.492a.75.75 0 0 0 0-1.5H3.755a.75.75 0 0 0 0 1.5Z"/>
|
<path fill="currentColor" d="M3.755 12.5h16.492a.75.75 0 0 0 0-1.5H3.755a.75.75 0 0 0 0 1.5Z"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user