Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Editor feature documentation #798

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/chilled-students-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@keystar/ui': patch
---

Improve prose styles:
- style `kbd` elements
- basic table styles
5 changes: 5 additions & 0 deletions .changeset/fast-chicken-look.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystatic/core': patch
---

Document editor features, within footer actions.
4 changes: 3 additions & 1 deletion design-system/pkg/src/typography/Kbd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { DOMProps } from '@react-types/shared';

import { useSlotProps } from '@keystar/ui/slots';
import { BaseStyleProps, css } from '@keystar/ui/style';
import { TextProps } from '@keystar/ui/types';

import { useTextStyles } from './text';

Expand Down Expand Up @@ -50,7 +51,8 @@ export type KbdProps = {
* @default 'kbd'
*/
slot?: string;
} & DOMProps &
} & Pick<TextProps, 'trim'> &
DOMProps &
BaseStyleProps;

/** Represents text that specifies a keyboard command. */
Expand Down
42 changes: 38 additions & 4 deletions design-system/pkg/src/typography/Prose.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,20 @@ export function useProseStyleProps(props: ProseProps) {
a: {
color: tokenSchema.color.foreground.accent,
},
kbd: {
backgroundColor: tokenSchema.color.alias.backgroundIdle,
borderRadius: tokenSchema.size.radius.small,
border: `${tokenSchema.size.border.regular} solid ${tokenSchema.color.alias.borderIdle}`,
color: tokenSchema.color.foreground.neutralEmphasis,
display: 'inline',
fontSize: '0.85em',
fontFamily: tokenSchema.typography.fontFamily.code,
padding: '.2em .4em',
},

// code block
pre: {
backgroundColor: tokenSchema.color.background.surface,
backgroundColor: tokenSchema.color.alias.backgroundIdle,
borderRadius: tokenSchema.size.radius.medium,
color: tokenSchema.color.foreground.neutralEmphasis,
fontFamily: tokenSchema.typography.fontFamily.code,
Expand All @@ -137,13 +147,13 @@ export function useProseStyleProps(props: ProseProps) {
},
// inline code
'& :not(pre) > code': {
backgroundColor: tokenSchema.color.background.accent,
backgroundColor: tokenSchema.color.alias.backgroundSelected,
borderRadius: tokenSchema.size.radius.small,
color: tokenSchema.color.foreground.neutralEmphasis,
display: 'inline-block',
display: 'inline',
fontSize: '0.85em',
fontFamily: tokenSchema.typography.fontFamily.code,
paddingInline: tokenSchema.size.space.small,
padding: '.2em .4em',
},

// Headings
Expand Down Expand Up @@ -183,6 +193,30 @@ export function useProseStyleProps(props: ProseProps) {
fontWeight: tokenSchema.typography.fontWeight.semibold,
letterSpacing: '0.0125em',
},

// Tables
// ---------------------------------------------------------------------
table: {
borderCollapse: 'collapse',
borderSpacing: 0,
width: '100%',
// tableLayout: 'fixed',
},
th: {
fontWeight: tokenSchema.typography.fontWeight.semibold,
padding: '0.5rem',
textAlign: 'start',
verticalAlign: 'top',
},
':where(th:first-child)': { paddingInlineStart: 0 },
':where(th:last-child)': { paddingInlineEnd: 0 },
td: {
padding: '0.5rem',
verticalAlign: 'top',
},
':where(td:first-child)': { paddingInlineStart: 0 },
':where(td:last-child)': { paddingInlineEnd: 0 },

...getListStyles(),
}),
styleProps.className
Expand Down
218 changes: 218 additions & 0 deletions packages/keystatic/src/form/fields/markdoc/editor/editor-footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import { useCallback, useMemo } from 'react';

import { ActionButton } from '@keystar/ui/button';
import { Dialog, DialogTrigger } from '@keystar/ui/dialog';
import { Icon } from '@keystar/ui/icon';
import { imageIcon } from '@keystar/ui/icon/icons/imageIcon';
import { typeIcon } from '@keystar/ui/icon/icons/typeIcon';
import { HStack } from '@keystar/ui/layout';
import { Content, SlotProvider } from '@keystar/ui/slots';
import { Heading, Kbd, Prose, Text } from '@keystar/ui/typography';

import { getUploadedFileObject } from '../../image/ui';
import { useEditorDispatchCommand } from './editor-view';
import { readFileAsDataUrl } from './images';

Check failure on line 14 in packages/keystatic/src/form/fields/markdoc/editor/editor-footer.tsx

View workflow job for this annotation

GitHub Actions / TypeScript

Module '"./images"' has no exported member 'readFileAsDataUrl'.

export function EditorFooter() {
const runCommand = useEditorDispatchCommand();
const handleImagePress = useCallback(async () => {
const file = await getUploadedFileObject('image/*');
if (!file) return;
const src = await readFileAsDataUrl(file);
runCommand((state, dispatch) => {
if (dispatch) {
dispatch(
state.tr.replaceSelectionWith(
state.schema.nodes.image.createChecked({
src,
filename: file.name,
})
)
);
}
return true;
});
}, [runCommand]);

const slots = useMemo(
() => ({ icon: { size: 'small' }, text: { size: 'small' } }) as const,
[]
);

return (
<HStack gap="small" margin="small">
<DialogTrigger isDismissable>
<ActionButton prominence="low">
<SlotProvider slots={slots}>
<Icon src={typeIcon} />
<Text>Markdown support</Text>
</SlotProvider>
</ActionButton>
<MarkdownDialog />
</DialogTrigger>
<ActionButton prominence="low" onPress={handleImagePress}>
<SlotProvider slots={slots}>
<Icon src={imageIcon} />
<Text>Paste, drop, or click to add images</Text>
</SlotProvider>
</ActionButton>
</HStack>
);
}

function MarkdownDialog() {
return (
<Dialog size="large">
<Heading>Basic writing and formatting syntax</Heading>
<Content>
<Prose size="regular">
<p>
Markdown is a way to style text; it is intended to be as
easy-to-read and easy-to-write as is feasible.
</p>
Comment on lines +69 to +72
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels weird to say this when you're not reading markdown, there are just some markdown shortcuts

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah agreed, this is all pretty green. Switching to draft for now; we can workshop the content whenever, the concept and UI are in place.


<h3>Headings</h3>
<p>
To create a heading, add one to six <code>#</code> symbols before
your heading text. The number of <code>#</code> you use will
determine the hierarchy level and typeface size of the heading.
</p>
<pre>
<code>{`# A first-level heading\n## A second-level heading\n### A third-level heading`}</code>
</pre>

<h3>Styling text</h3>
<p>
You can indicate emphasis with bold, italic, or strikethrough text.
</p>
<table>
<thead>
<tr>
<th scope="col">Style</th>
<th scope="col">Syntax</th>
<th scope="col">Shortcut</th>
<th scope="col">Example</th>
<th scope="col">Output</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bold</td>
<td>
<code>** **</code>
</td>
<td>
<Kbd meta trim={false}>
B
</Kbd>
</td>
<td>
<code>**This is bold text**</code>
</td>
<td>
<strong>This is bold text</strong>
</td>
</tr>
<tr>
<td>Italic</td>
<td>
<code>_ _</code>
</td>
<td>
<Kbd meta trim={false}>
I
</Kbd>
</td>
<td>
<code>_This text is italicized_</code>
</td>
<td>
<em>This text is italicized</em>
</td>
</tr>
<tr>
<td>Strikethrough</td>
<td>
<code>~~ ~~</code>
</td>
<td>None</td>
<td>
<code>~~This was mistaken text~~</code>
</td>
<td>
<del>This was mistaken text</del>
</td>
</tr>
<tr>
<td>Nested&nbsp;emphasis</td>
<td>
<code>**&nbsp;**</code> and <code>_&nbsp;_</code>
</td>
<td>None</td>
<td>
<code>**This text is _extremely_ important**</code>
</td>
<td>
<strong>
This text is <em>extremely</em> important
</strong>
</td>
</tr>
</tbody>
</table>

<h3>Lists</h3>
<p>
You can make an unordered (bulleted) list by preceding one or more
lines of text with <kbd>-</kbd>, or <kbd>*</kbd>.
</p>
<pre>
<code>{`- Red\n- Green\n- Blue`}</code>
</pre>

<p>
Create an ordered (numbered) list by preceding each line with a
number.
</p>
<pre>
<code>{`1. Red\n1. Green\n1. Blue`}</code>
</pre>
Comment on lines +164 to +179
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I'd describe it like this, seems a bit weird since you'd just add e.g. the 1. on the first time and then press enter for more, you wouldn't precede every line with a number yourself

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, needs to be reconsidered.


<h3>Quoting text</h3>
<p>
You can quote text with a <kbd>{'>'}</kbd>.
</p>
<pre>
<code>{`Text that is not a quote\n\n> Text that is a quote`}</code>
</pre>

<h3>Quoting code</h3>
<p>
You can call out code or a command within a sentence with single
backticks <kbd>{'`'}</kbd>. The text within the backticks will not
be formatted.
</p>
<pre>
<code>{`Use \`npm create @keystatic@latest\` to start editing static files today.`}</code>
</pre>

<p>
To format code or text into its own distinct block, use triple
backticks <kbd>{'```'}</kbd>.
</p>
<pre>
<code>{`Keystatic projects expect an exported config:

\`\`\`
import { config } from '@keystatic/core'

export default config({
...
})
\`\`\``}</code>
</pre>
</Prose>
</Content>
</Dialog>
);
}
20 changes: 10 additions & 10 deletions packages/keystatic/src/form/fields/markdoc/editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
} from './context';
import { useEntryLayoutSplitPaneContext } from '../../../../app/entry-form';
import { useContentPanelSize } from '../../../../app/shell/context';
import { EditorFooter } from './editor-footer';
import { yCursorPluginKey, ySyncPluginKey } from 'y-prosemirror';
import * as Y from 'yjs';

Expand Down Expand Up @@ -119,16 +120,15 @@ export const Editor = forwardRef(function Editor(
})}
>
<Toolbar id={getToolbarId(id)} data-keystatic-editor="toolbar" />
<div>
<ProseMirrorEditable
{...props}
{...styleProps}
role="textbox"
aria-multiline="true"
id={getContentId(id)}
data-keystatic-editor="content"
/>
</div>
<ProseMirrorEditable
{...props}
{...styleProps}
role="textbox"
aria-multiline="true"
id={getContentId(id)}
data-keystatic-editor="content"
/>
<EditorFooter />
</Box>
<NodeViews state={value} />
<CellMenuPortal />
Expand Down
Loading