mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 18:41: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 { render } from 'solid-js/web';
|
||||
import { extractCss } from 'solid-styled-components';
|
||||
|
||||
import { TitleBar } from './renderer/TitleBar';
|
||||
import { defaultInAppMenuConfig, InAppMenuConfig } from './constants';
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { JSX } from 'solid-js';
|
||||
import { css } from 'solid-styled-components';
|
||||
|
||||
const iconButton = css`
|
||||
import { cache } from '@/providers/decorators';
|
||||
|
||||
const iconButton = cache(() => css`
|
||||
background: transparent;
|
||||
|
||||
width: 24px;
|
||||
@ -28,12 +30,12 @@ const iconButton = css`
|
||||
&:active {
|
||||
scale: 0.9;
|
||||
}
|
||||
`;
|
||||
`);
|
||||
|
||||
type CollapseIconButtonProps = JSX.HTMLAttributes<HTMLButtonElement>;
|
||||
export const IconButton = (props: CollapseIconButtonProps) => {
|
||||
return (
|
||||
<button {...props} class={iconButton}>
|
||||
<button {...props} class={iconButton()}>
|
||||
{props.children}
|
||||
</button>
|
||||
);
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { JSX, splitProps } from 'solid-js';
|
||||
import { css } from 'solid-styled-components';
|
||||
import { cache } from '@/providers/decorators';
|
||||
|
||||
const menuStyle = css`
|
||||
const menuStyle = cache(() => css`
|
||||
-webkit-app-region: none;
|
||||
|
||||
display: flex;
|
||||
@ -25,7 +26,7 @@ const menuStyle = css`
|
||||
&[data-selected="true"] {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
`;
|
||||
`);
|
||||
|
||||
export type MenuButtonProps = JSX.HTMLAttributes<HTMLLIElement> & {
|
||||
text?: string;
|
||||
@ -35,7 +36,7 @@ export const MenuButton = (props: MenuButtonProps) => {
|
||||
const [local, leftProps] = splitProps(props, ['text']);
|
||||
|
||||
return (
|
||||
<li {...leftProps} class={menuStyle} data-selected={props.selected}>
|
||||
<li {...leftProps} class={menuStyle()} data-selected={props.selected}>
|
||||
{local.text}
|
||||
</li>
|
||||
);
|
||||
|
||||
@ -4,8 +4,9 @@ import { css } from 'solid-styled-components';
|
||||
import { Transition } from 'solid-transition-group';
|
||||
import { autoUpdate, flip, offset, OffsetOptions, size } from '@floating-ui/dom';
|
||||
import { useFloating } from 'solid-floating-ui';
|
||||
import { cache } from '@/providers/decorators';
|
||||
|
||||
const panelStyle = css`
|
||||
const panelStyle = cache(() => css`
|
||||
position: fixed;
|
||||
top: var(--offset-y, 0);
|
||||
left: var(--offset-x, 0);
|
||||
@ -32,9 +33,9 @@ const panelStyle = css`
|
||||
0 2px 8px rgba(0, 0, 0, 0.2);
|
||||
|
||||
transform-origin: var(--origin-x, 50%) var(--origin-y, 50%);
|
||||
`;
|
||||
`);
|
||||
|
||||
const animationStyle = {
|
||||
const animationStyle = cache(() => ({
|
||||
enter: css`
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
@ -49,7 +50,7 @@ const animationStyle = {
|
||||
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);
|
||||
`,
|
||||
};
|
||||
}));
|
||||
|
||||
export type Placement =
|
||||
'top'
|
||||
@ -122,16 +123,16 @@ export const Panel = (props: PanelProps) => {
|
||||
<Portal>
|
||||
<Transition
|
||||
appear
|
||||
enterClass={animationStyle.enter}
|
||||
enterActiveClass={animationStyle.enterActive}
|
||||
exitToClass={animationStyle.exitTo}
|
||||
exitActiveClass={animationStyle.exitActive}
|
||||
enterClass={animationStyle().enter}
|
||||
enterActiveClass={animationStyle().enterActive}
|
||||
exitToClass={animationStyle().exitTo}
|
||||
exitActiveClass={animationStyle().exitActive}
|
||||
>
|
||||
<Show when={local.open}>
|
||||
<ul
|
||||
{...leftProps}
|
||||
ref={setPanel}
|
||||
class={panelStyle}
|
||||
class={panelStyle()}
|
||||
style={{
|
||||
'--offset-x': `${position.x}px`,
|
||||
'--offset-y': `${position.y}px`,
|
||||
|
||||
@ -8,8 +8,9 @@ import { useFloating } from 'solid-floating-ui';
|
||||
import { autoUpdate, offset, size } from '@floating-ui/dom';
|
||||
|
||||
import { Panel } from './Panel';
|
||||
import { cache } from '@/providers/decorators';
|
||||
|
||||
const itemStyle = css`
|
||||
const itemStyle = cache(() => css`
|
||||
position: relative;
|
||||
|
||||
-webkit-app-region: none;
|
||||
@ -44,20 +45,20 @@ const itemStyle = css`
|
||||
& * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`;
|
||||
`);
|
||||
|
||||
const itemIconStyle = css`
|
||||
const itemIconStyle = cache(() => css`
|
||||
height: 32px;
|
||||
padding: 4px;
|
||||
color: white;
|
||||
`;
|
||||
`);
|
||||
|
||||
const itemLabelStyle = css`
|
||||
const itemLabelStyle = cache(() => css`
|
||||
font-size: 12px;
|
||||
color: white;
|
||||
`;
|
||||
`);
|
||||
|
||||
const itemChipStyle = css`
|
||||
const itemChipStyle = cache(() => css`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@ -73,9 +74,9 @@ const itemChipStyle = css`
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
`;
|
||||
`);
|
||||
|
||||
const toolTipStyle = css`
|
||||
const toolTipStyle = cache(() => css`
|
||||
min-width: 32px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@ -89,9 +90,9 @@ const toolTipStyle = css`
|
||||
background-color: rgba(25, 25, 25, 0.8);
|
||||
color: #f1f1f1;
|
||||
font-size: 10px;
|
||||
`;
|
||||
`);
|
||||
|
||||
const popupStyle = css`
|
||||
const popupStyle = cache(() => css`
|
||||
position: fixed;
|
||||
top: var(--offset-y, 0);
|
||||
left: var(--offset-x, 0);
|
||||
@ -102,8 +103,9 @@ const popupStyle = css`
|
||||
z-index: 100000000;
|
||||
pointer-events: none;
|
||||
|
||||
`;
|
||||
const animationStyle = {
|
||||
`);
|
||||
|
||||
const animationStyle = cache(() => ({
|
||||
enter: css`
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
@ -118,7 +120,7 @@ const animationStyle = {
|
||||
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);
|
||||
`,
|
||||
};
|
||||
}));
|
||||
|
||||
const getParents = (element: Element | null): (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 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('/'));
|
||||
|
||||
if (isOtherItem && !isChild) {
|
||||
@ -246,14 +248,14 @@ export const PanelItem = (props: PanelItemProps) => {
|
||||
return (
|
||||
<li
|
||||
ref={setAnchor}
|
||||
class={itemStyle}
|
||||
class={itemStyle()}
|
||||
onMouseEnter={handleHover}
|
||||
onClick={handleClick}
|
||||
data-selected={open()}
|
||||
>
|
||||
<Switch fallback={<div class={itemIconStyle}/>}>
|
||||
<Switch fallback={<div class={itemIconStyle()}/>}>
|
||||
<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-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
@ -261,28 +263,30 @@ export const PanelItem = (props: PanelItemProps) => {
|
||||
</svg>
|
||||
</Match>
|
||||
<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"
|
||||
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>
|
||||
</Match>
|
||||
<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"
|
||||
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>
|
||||
</Match>
|
||||
</Switch>
|
||||
<span class={itemLabelStyle}>
|
||||
<span class={itemLabelStyle()}>
|
||||
{props.name}
|
||||
</span>
|
||||
<Show when={props.chip} fallback={<div/>}>
|
||||
<span class={itemChipStyle}>
|
||||
<span class={itemChipStyle()}>
|
||||
{props.chip}
|
||||
</span>
|
||||
</Show>
|
||||
<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"
|
||||
fill="none"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
@ -304,7 +308,7 @@ export const PanelItem = (props: PanelItemProps) => {
|
||||
<Portal>
|
||||
<div
|
||||
ref={setToolTip}
|
||||
class={popupStyle}
|
||||
class={popupStyle()}
|
||||
style={{
|
||||
'--offset-x': `${position.x}px`,
|
||||
'--offset-y': `${position.y}px`,
|
||||
@ -312,13 +316,13 @@ export const PanelItem = (props: PanelItemProps) => {
|
||||
>
|
||||
<Transition
|
||||
appear
|
||||
enterClass={animationStyle.enter}
|
||||
enterActiveClass={animationStyle.enterActive}
|
||||
exitToClass={animationStyle.exitTo}
|
||||
exitActiveClass={animationStyle.exitActive}
|
||||
enterClass={animationStyle().enter}
|
||||
enterActiveClass={animationStyle().enterActive}
|
||||
exitToClass={animationStyle().exitTo}
|
||||
exitActiveClass={animationStyle().exitActive}
|
||||
>
|
||||
<Show when={toolTipOpen()}>
|
||||
<div class={toolTipStyle}>
|
||||
<div class={toolTipStyle()}>
|
||||
{props.toolTip}
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
@ -11,8 +11,9 @@ import { WindowController } from './WindowController';
|
||||
|
||||
import type { RendererContext } from '@/types/contexts';
|
||||
import type { InAppMenuConfig } from '../constants';
|
||||
import { cache } from '@/providers/decorators';
|
||||
|
||||
const titleStyle = css`
|
||||
const titleStyle = cache(() => css`
|
||||
-webkit-app-region: drag;
|
||||
box-sizing: border-box;
|
||||
|
||||
@ -41,17 +42,17 @@ const titleStyle = css`
|
||||
&[data-macos="true"] {
|
||||
padding: 4px 4px 4px 74px;
|
||||
}
|
||||
`;
|
||||
`);
|
||||
|
||||
const separatorStyle = css`
|
||||
const separatorStyle = cache(() => css`
|
||||
min-height: 1px;
|
||||
height: 1px;
|
||||
margin: 4px 0;
|
||||
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
`;
|
||||
`);
|
||||
|
||||
const animationStyle = {
|
||||
const animationStyle = cache(() => ({
|
||||
enter: css`
|
||||
opacity: 0;
|
||||
transform: translateX(-50%) scale(0.8);
|
||||
@ -76,7 +77,7 @@ const animationStyle = {
|
||||
fake: css`
|
||||
transition: all 0.00000000001s;
|
||||
`,
|
||||
};
|
||||
}));
|
||||
|
||||
export type PanelRendererProps = {
|
||||
items: Electron.Menu['items'];
|
||||
@ -140,7 +141,7 @@ const PanelRenderer = (props: PanelRendererProps) => {
|
||||
/>
|
||||
</Match>
|
||||
<Match when={subItem().type === 'separator'}>
|
||||
<hr class={separatorStyle}/>
|
||||
<hr class={separatorStyle()}/>
|
||||
</Match>
|
||||
</Switch>
|
||||
</Show>
|
||||
@ -251,7 +252,7 @@ export const TitleBar = (props: TitleBarProps) => {
|
||||
});
|
||||
|
||||
return (
|
||||
<nav class={titleStyle} data-macos={props.isMacOS}>
|
||||
<nav class={titleStyle()} data-macos={props.isMacOS}>
|
||||
<IconButton
|
||||
onClick={() => setCollapsed(!collapsed())}
|
||||
style={{
|
||||
@ -266,10 +267,10 @@ export const TitleBar = (props: TitleBarProps) => {
|
||||
</svg>
|
||||
</IconButton>
|
||||
<TransitionGroup
|
||||
enterClass={ignoreTransition() ? animationStyle.fakeTarget : animationStyle.enter}
|
||||
enterActiveClass={ignoreTransition() ? animationStyle.fake : animationStyle.enterActive}
|
||||
exitToClass={ignoreTransition() ? animationStyle.fakeTarget : animationStyle.exitTo}
|
||||
exitActiveClass={ignoreTransition() ? animationStyle.fake : animationStyle.exitActive}
|
||||
enterClass={ignoreTransition() ? animationStyle().fakeTarget : animationStyle().enter}
|
||||
enterActiveClass={ignoreTransition() ? animationStyle().fake : animationStyle().enterActive}
|
||||
exitToClass={ignoreTransition() ? animationStyle().fakeTarget : animationStyle().exitTo}
|
||||
exitActiveClass={ignoreTransition() ? animationStyle().fake : animationStyle().exitActive}
|
||||
onBeforeEnter={(element) => {
|
||||
if (ignoreTransition()) return;
|
||||
const index = Number(element.getAttribute('data-index') ?? 0);
|
||||
|
||||
@ -2,8 +2,9 @@ import { css } from 'solid-styled-components';
|
||||
import { Show } from 'solid-js';
|
||||
|
||||
import { IconButton } from './IconButton';
|
||||
import { cache } from '@/providers/decorators';
|
||||
|
||||
const containerStyle = css`
|
||||
const containerStyle = cache(() => css`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
@ -15,7 +16,7 @@ const containerStyle = css`
|
||||
background: rgba(255, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
`;
|
||||
`);
|
||||
|
||||
export type WindowControllerProps = {
|
||||
isMaximize?: boolean;
|
||||
@ -26,7 +27,7 @@ export type WindowControllerProps = {
|
||||
}
|
||||
export const WindowController = (props: WindowControllerProps) => {
|
||||
return (
|
||||
<div class={containerStyle}>
|
||||
<div class={containerStyle()}>
|
||||
<IconButton onClick={props.onMinimize}>
|
||||
<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"/>
|
||||
|
||||
Reference in New Issue
Block a user