Skip to content

Commit 06613cc

Browse files
authored
Merge pull request #267 from GBSL-Informatik/feature/fullscreen-code-support
feature: support fullscreen mode for code blocks
2 parents 263650b + 11dcc6f commit 06613cc

15 files changed

Lines changed: 268 additions & 105 deletions

File tree

packages/tdev/brython-code/components/Header/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ interface Props {
1111
}
1212

1313
const Header = observer((props: Props) => {
14-
const { code: code } = props;
14+
const { code } = props;
1515
if (!code) {
1616
return null;
1717
}

packages/tdev/brython-code/components/Meta/Graphics/styles.module.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
}
2424
.brythonGraphicsResultHead > span {
2525
font-family: var(--ifm-font-family-monospace);
26-
font-size: var(--ifm-code-font-size);
26+
font-size: var(--ifm-h4-font-size);
2727
color: var(--ifm-color-primary);
2828
margin-left: 8px;
2929
}

packages/tdev/excalidoc/Component/index.tsx

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import _ from 'es-toolkit/compat';
77
import Preview from './Preview';
88
import Editor from './Editor';
99
import SyncStatus from '@tdev-components/SyncStatus';
10-
import { mdiCircleEditOutline, mdiClose, mdiLoading } from '@mdi/js';
10+
import RequestFullscreen from '@tdev-components/shared/RequestFullscreen';
11+
import { mdiCircleEditOutline, mdiClose, mdiFullscreen, mdiFullscreenExit, mdiLoading } from '@mdi/js';
1112
import clsx from 'clsx';
1213
import Button from '@tdev-components/shared/Button';
1314
import type { LibraryItems } from '@excalidraw/excalidraw/types';
@@ -17,6 +18,7 @@ import PermissionsPanel from '@tdev-components/PermissionsPanel';
1718
import { useDocument } from '@tdev-hooks/useDocument';
1819
import { useClientLib } from '@tdev-hooks/useClientLib';
1920
import { MetaInit, ModelMeta } from '@tdev/excalidoc/model/ModelMeta';
21+
import { useStore } from '@tdev-hooks/useStore';
2022

2123
export const DEFAULT_HEIGHT = '600px' as const;
2224
export const mdiExcalidraw =
@@ -62,11 +64,13 @@ export const ExcalidocComponent = observer(
6264
}
6365
) => {
6466
const [edit, setEdit] = React.useState(false);
67+
const viewStore = useStore('viewStore');
6568
const Lib = useClientLib<typeof ExcalidrawLib>(
6669
() => import('@excalidraw/excalidraw'),
6770
'@excalidraw/excalidraw'
6871
);
6972
const doc = useDocument<'excalidoc'>(props.documentId);
73+
const id = React.useId();
7074
const onEdit = React.useCallback(
7175
(edit: boolean) => {
7276
setEdit(edit);
@@ -100,7 +104,8 @@ export const ExcalidocComponent = observer(
100104
return (
101105
<div
102106
style={{ height: props.height || DEFAULT_HEIGHT, width: '100%' }}
103-
className={clsx(styles.excalidraw)}
107+
className={clsx(styles.excalidraw, viewStore.isFullscreenTarget(id) && styles.fullscreen)}
108+
id={id}
104109
>
105110
<div className={styles.actions}>
106111
<SyncStatus model={doc} />
@@ -116,14 +121,18 @@ export const ExcalidocComponent = observer(
116121
/>
117122
)}
118123
{edit && (
119-
<Button
120-
onClick={() => {
121-
onEdit(false);
122-
}}
123-
icon={mdiClose}
124-
color="red"
125-
title="Bearbeitung beenden"
126-
/>
124+
<>
125+
<RequestFullscreen targetId={id} />
126+
<Button
127+
onClick={() => {
128+
onEdit(false);
129+
viewStore.exitFullscreen();
130+
}}
131+
icon={mdiClose}
132+
color="red"
133+
title="Bearbeitung beenden"
134+
/>
135+
</>
127136
)}
128137
</div>
129138
<Editor

packages/tdev/excalidoc/Component/styles.module.scss

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
.excalidraw {
22
position: relative;
3+
&.fullscreen {
4+
.actions {
5+
top: 1.5em;
6+
right: 1.5em;
7+
}
8+
}
39
&.preview {
410
display: flex;
511
align-items: center;
@@ -13,6 +19,11 @@
1319
display: flex;
1420
flex-direction: row;
1521
align-items: center;
22+
button {
23+
&:not(:hover) {
24+
background: var(--ifm-color-secondary-contrast-background);
25+
}
26+
}
1627
}
1728
max-height: 70vh;
1829
:global(.excalidraw) {

packages/tdev/pyodide-code/components/Header/styles.module.scss

Lines changed: 0 additions & 42 deletions
This file was deleted.

src/components/documents/CodeEditor/Editor/Footer/Logs/index.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import React from 'react';
22
import clsx from 'clsx';
33
import styles from './styles.module.scss';
44
import { observer } from 'mobx-react-lite';
5-
import { useStore } from '@tdev-hooks/useStore';
65
import Button from '@tdev-components/shared/Button';
76
import { mdiCardRemoveOutline } from '@mdi/js';
8-
import { SIZE_S, SIZE_XS } from '@tdev-components/shared/iconSizes';
9-
import CopyBadge from '@tdev-components/shared/CopyBadge';
7+
import { SIZE_S } from '@tdev-components/shared/iconSizes';
108
import CopyButton from '@tdev-components/shared/Button/CopyButton';
9+
import { useStore } from '@tdev-hooks/useStore';
10+
import { useFullscreenTargetId } from '@tdev-hooks/useFullscreenTargetId';
1111

1212
export type LogMessage = { type: 'log' | 'error'; message: string };
1313

@@ -20,7 +20,9 @@ interface Props {
2020
const Logs = observer((props: Props) => {
2121
const { messages, onClear, maxLines = 40 } = props;
2222
const ref = React.useRef<HTMLDivElement>(null);
23+
const viewStore = useStore('viewStore');
2324
const [hasHorizontalOverflow, setHasHorizontalOverflow] = React.useState(false);
25+
const targetId = useFullscreenTargetId();
2426
React.useEffect(() => {
2527
if (ref.current) {
2628
ref.current.scrollTop = ref.current.scrollHeight;
@@ -46,7 +48,13 @@ const Logs = observer((props: Props) => {
4648
/>
4749
</div>
4850
</div>
49-
<div className={clsx(styles.messages)} style={{ maxHeight: maxLines * 1.2 + 2 + 'em' }} ref={ref}>
51+
<div
52+
className={clsx(styles.messages)}
53+
style={{
54+
maxHeight: maxLines * (viewStore.isFullscreenTarget(targetId) ? 1.5 : 1) * 1.2 + 2 + 'em'
55+
}}
56+
ref={ref}
57+
>
5058
{messages.map((msg, idx) => (
5159
<pre key={idx} className={clsx(styles.message, styles[msg.type])}>
5260
{msg.message}

src/components/documents/CodeEditor/Editor/Header/Content.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,27 @@ import type { CodeType } from '@tdev-api/document';
1010
import type iCode from '@tdev-models/documents/iCode';
1111
import DownloadCode from '../../Actions/DownloadCode';
1212
import ShowRaw from '../../Actions/ShowRaw';
13+
import RequestFullscreen from '@tdev-components/shared/RequestFullscreen';
14+
import { useFullscreenTargetId } from '@tdev-hooks/useFullscreenTargetId';
1315

1416
interface Props<T extends CodeType> {
1517
code: iCode<T>;
18+
showFullscreenButton?: boolean;
1619
}
1720

1821
const Content = observer(<T extends CodeType>(props: Props<T>) => {
19-
const { code } = props;
22+
const { code, showFullscreenButton } = props;
2023
const notifyUnpersisted = code.root?.isDummy && !code.meta.slim && !code.meta.hideWarning;
24+
const targetId = useFullscreenTargetId();
2125
return (
2226
<>
2327
<div className={clsx(styles.title)}>{code.title}</div>
2428
<div className={clsx(styles.spacer)} />
29+
<RequestFullscreen
30+
targetId={targetId}
31+
adminOnly={!showFullscreenButton}
32+
className={clsx(styles.fullscreenButton)}
33+
/>
2534
{notifyUnpersisted && (
2635
<Icon
2736
path={mdiFlashTriangle}

src/components/documents/CodeEditor/Editor/Header/styles.module.scss

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
font-weight: bold;
1212
background: var(--ifm-color-emphasis-200);
1313
color: var(--ifm-color-content);
14-
font-size: calc(var(--ifm-code-font-size) * 1.2);
14+
font-size: var(--ifm-h3-font-size);
1515
.title {
1616
flex-shrink: 1;
1717
overflow-x: hidden;
@@ -31,6 +31,17 @@
3131
&.unpersisted {
3232
background: var(--ifm-color-warning-lightest);
3333
}
34+
.fullscreenButton {
35+
opacity: var(--tdev-code-editor-fullscreen-opacity);
36+
}
37+
&:hover {
38+
.fullscreenButton {
39+
--tdev-code-editor-fullscreen-opacity: 1;
40+
&:hover {
41+
--tdev-code-editor-fullscreen-opacity: 0.6;
42+
}
43+
}
44+
}
3445
}
3546

3647
[data-theme='dark'] {

src/components/documents/CodeEditor/index.tsx

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import iCode from '@tdev-models/documents/iCode';
1313
import { CodeType } from '@tdev-api/document';
1414
import { useStore } from '@tdev-hooks/useStore';
1515
import { LiveCode } from '@tdev-stores/ComponentStore';
16+
import { FullscreenContext } from '@tdev-hooks/useFullscreenTargetId';
1617

1718
export interface Props extends Omit<MetaProps, 'live_jsx' | 'live_py'> {
1819
title: string;
@@ -59,20 +60,32 @@ export interface ScriptProps<T extends CodeType> {
5960
const CodeEditorComponent = observer(<T extends CodeType>(props: ScriptProps<T>) => {
6061
const { code } = props;
6162
const { colorMode } = useCodeTheme();
63+
const viewStore = useStore('viewStore');
64+
const id = React.useId();
6265
return (
63-
<div className={clsx(styles.wrapper, 'notranslate', props.className)}>
66+
<FullscreenContext.Provider value={id}>
6467
<div
68+
id={id}
6569
className={clsx(
66-
styles.playgroundContainer,
67-
colorMode === 'light' && styles.lightTheme,
68-
code.meta.slim ? styles.containerSlim : styles.containerBig,
69-
'live-code-editor'
70+
styles.wrapper,
71+
'notranslate',
72+
props.className,
73+
viewStore.isFullscreenTarget(id) && styles.fullscreen
7074
)}
7175
>
72-
<Editor code={code} />
73-
{code.meta.hasHistory && <CodeHistory code={code} />}
76+
<div
77+
className={clsx(
78+
styles.editorContainer,
79+
colorMode === 'light' && styles.lightTheme,
80+
code.meta.slim ? styles.containerSlim : styles.containerBig,
81+
'live-code-editor'
82+
)}
83+
>
84+
<Editor code={code} />
85+
{code.meta.hasHistory && <CodeHistory code={code} />}
86+
</div>
7487
</div>
75-
</div>
88+
</FullscreenContext.Provider>
7689
);
7790
});
7891

Lines changed: 55 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,61 @@
1-
.containerBig {
2-
display: grid;
3-
flex-grow: 1;
4-
flex-shrink: 1;
5-
flex-basis: 250px;
6-
grid-template-columns: 100%;
7-
grid-template-rows: min-content 1fr min-content;
8-
gap: 0px 0px;
9-
grid-auto-flow: row;
10-
grid-template-areas: 'controls' 'editor' 'result';
11-
}
1+
.wrapper {
2+
--tdev-code-editor-fullscreen-opacity: 0;
123

13-
.containerSlim {
14-
display: grid;
15-
grid-template-columns: 1fr min-content;
16-
grid-template-rows: 1fr min-content;
17-
gap: 0px 0px;
18-
grid-auto-flow: row;
19-
grid-template-areas: 'editor controls' 'result result';
20-
.headerButton {
21-
z-index: calc(var(--ifm-z-index-fixed) - 2);
22-
height: 100%;
23-
border-radius: 0px;
24-
padding: 2px 6px;
25-
margin: 0px;
4+
&:hover {
5+
--tdev-code-editor-fullscreen-opacity: 0.2;
266
}
27-
&.lightTheme {
28-
border: 1px solid var(--ifm-color-gray-400);
7+
8+
&:last-child .editorContainer {
9+
margin-bottom: inherit;
10+
}
11+
&.fullscreen {
12+
--ifm-code-font-size: 2rem;
13+
background: var(--ifm-color-white);
14+
padding: 3em;
15+
overflow-y: auto;
16+
max-height: 95vh;
17+
.editorContainer {
18+
box-shadow: var(--ifm-global-shadow-md);
19+
}
2920
}
30-
}
3121

32-
.playgroundContainer {
33-
margin-bottom: var(--ifm-leading);
34-
border-radius: var(--ifm-global-radius);
35-
box-shadow: var(--ifm-global-shadow-lw);
36-
overflow: hidden;
37-
flex-grow: 1;
38-
flex-shrink: 1;
39-
}
22+
.containerBig {
23+
display: grid;
24+
flex-grow: 1;
25+
flex-shrink: 1;
26+
flex-basis: 250px;
27+
grid-template-columns: 100%;
28+
grid-template-rows: min-content 1fr min-content;
29+
gap: 0px 0px;
30+
grid-auto-flow: row;
31+
grid-template-areas: 'controls' 'editor' 'result';
32+
}
33+
34+
.containerSlim {
35+
display: grid;
36+
grid-template-columns: 1fr min-content;
37+
grid-template-rows: 1fr min-content;
38+
gap: 0px 0px;
39+
grid-auto-flow: row;
40+
grid-template-areas: 'editor controls' 'result result';
41+
.headerButton {
42+
z-index: calc(var(--ifm-z-index-fixed) - 2);
43+
height: 100%;
44+
border-radius: 0px;
45+
padding: 2px 6px;
46+
margin: 0px;
47+
}
48+
&.lightTheme {
49+
border: 1px solid var(--ifm-color-gray-400);
50+
}
51+
}
4052

41-
.wrapper:last-child .playgroundContainer {
42-
margin-bottom: inherit;
53+
.editorContainer {
54+
margin-bottom: var(--ifm-leading);
55+
border-radius: var(--ifm-global-radius);
56+
box-shadow: var(--ifm-global-shadow-lw);
57+
overflow: hidden;
58+
flex-grow: 1;
59+
flex-shrink: 1;
60+
}
4361
}

0 commit comments

Comments
 (0)