Skip to content

Commit

Permalink
feat(EditorView): change default selection styles
Browse files Browse the repository at this point in the history
  • Loading branch information
whiteformed committed Dec 10, 2024
1 parent 89c9881 commit 0ae5e41
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 33 deletions.
24 changes: 24 additions & 0 deletions demo/stories/css-variables/CSSVariables.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,30 @@ export const Story: StoryObj<typeof component> = {
control: {type: 'text'},
description: 'Editor contents padding',
},
'--g-selection-bg-color': {
control: {type: 'text'},
description: 'Editor selection bg color',
},
'--g-selection-border': {
control: {type: 'text'},
description: 'Editor selection border',
},
'--g-selection-border-radius': {
control: {type: 'text'},
description: 'Editor selection border radius',
},
'--g-selection-outline': {
control: {type: 'text'},
description: 'Editor selection outline',
},
'--g-selection-background': {
control: {type: 'text'},
description: 'Editor selection background',
},
'--g-selection-box-shadow': {
control: {type: 'text'},
description: 'Editor selection box-shadow',
},
},
};
Story.storyName = 'Custom CSS Variables';
Expand Down
9 changes: 9 additions & 0 deletions docs/how-to-customize-the-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## How to customize the editor
You can use CSS variables to make editor contents fit your own needs

### Elements styles
| **Variable** | **Description** | **CSS Property Type** | **Default** |
| :---: | :---: | :---: | :---: |
| `--g-md-toolbar-padding` | Toolbar padding | padding | 0px |
Expand All @@ -11,3 +12,11 @@ You can use CSS variables to make editor contents fit your own needs
| `--g-md-toolbar-sticky-offset` | Toolbar offset in sticky mode | top | 0px |
| `--g-md-toolbar-sticky-border` | Toolbar border in sticky mode | border | 1px solid var(--g-color-line-generic-solid) |
| `--g-md-editor-padding` | Editor contents padding | padding | 0px |

### Selection styles
| **Variable** | **Description** | **CSS Property Type** | **Default** |
| `--g-selection-border` | Editor selection border | border | none |
| `--g-selection-border-radius` | Editor selection border radius | border-radius | 6px |
| `--g-selection-outline` | Editor selection outline | outline | none |
| `--g-selection-background` | Editor selection background | background | #e6e6e6 |
| `--g-selection-box-shadow` | Editor selection box shadow | box-shadow | none |
1 change: 1 addition & 0 deletions src/extensions/base/BaseSchema/BaseSchemaSpecs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const BaseSchemaSpecs: ExtensionAuto<BaseSchemaSpecsOptions> = (builder,
0,
];
},
selectable: true,
placeholder: opts.paragraphPlaceholder
? {
content: opts.paragraphPlaceholder,
Expand Down
28 changes: 0 additions & 28 deletions src/extensions/base/BaseStyles/index.scss

This file was deleted.

2 changes: 0 additions & 2 deletions src/extensions/base/BaseStyles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import {Plugin} from 'prosemirror-state';

import type {ExtensionAuto} from '../../../core';

import './index.scss';

export const BaseStyles: ExtensionAuto = (builder) => {
builder.addPlugin(
() =>
Expand Down
104 changes: 102 additions & 2 deletions src/extensions/behavior/Selection/selection.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,103 @@
.g-md-editor.ProseMirror-focused .pm-node-selected {
box-shadow: var(--g-color-text-info) 0 0 0 1px;
$active-node-default-indent-top: calc(-1 * var(--g-spacing-1));
$active-node-default-indent-bottom: calc(-1 * var(--g-spacing-1));
$active-node-default-indent-left: calc(-1 * var(--g-spacing-2));
$active-node-default-indent-right: calc(-1 * var(--g-spacing-2));

$active-node-selector: '.pm-node-selected';
$basic-elements: h1, h2, h3, h4, h5, h6, p, ul, ol, span, pre, '*[data-html]', '.g-md-checkbox', '.g-md-table-wrapper';
$yfm-elements: 'div[class^="yfm-"]';

$default-selection-border: none;
$default-selection-border-radius: var(--g-border-radius-m);
$default-selection-outline: none;
// TODO: CHANGE TO TOKEN
$default-selection-background: #e6e6e6;
$default-selection-box-shadow: none;

$default-li-marker-width: var(--g-spacing-4);

@mixin node-props {
position: relative;

border: $default-selection-border;
border-radius: $default-selection-border-radius;
outline: $default-selection-outline;
background: var(--g-selection-background, $default-selection-background);
box-shadow: $default-selection-box-shadow;
}

@mixin selection-props {
border: var(--g-selection-border, $default-selection-border);
border-radius: var(--g-selection-border-radius, $default-selection-border-radius);
outline: var(--g-selection-outline, $default-selection-outline);
background: var(--g-selection-background, $default-selection-background);
box-shadow: var(--g-selection-box-shadow, $default-selection-box-shadow);
}

[class].g-md-editor {
@each $basic-element in $basic-elements {
& #{$basic-element}#{$active-node-selector} {
@include node-props;

&::after {
position: absolute;
z-index: -1;
inset: $active-node-default-indent-top
$active-node-default-indent-right
$active-node-default-indent-bottom
$active-node-default-indent-left;

content: '';

@include selection-props;
}

& img {
mix-blend-mode: multiply;
}
}

}

& #{$yfm-elements}#{$active-node-selector} {
position: relative;

&::after {
position: absolute;
z-index: -1;
inset: $active-node-default-indent-top
$active-node-default-indent-right
$active-node-default-indent-bottom
$active-node-default-indent-left;

content: '';

@include selection-props;
}
}

& li#{$active-node-selector} {
@include node-props;

&::marker {
@include selection-props;
}

&::after {
position: absolute;
z-index: -1;
inset: $active-node-default-indent-top $active-node-default-indent-right
$active-node-default-indent-bottom
calc(
$active-node-default-indent-left - max(
var(--li-marker-width, 0),
$default-li-marker-width
)
);

content: '';

@include selection-props;
}
}
}
5 changes: 4 additions & 1 deletion src/extensions/behavior/Selection/selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,18 +99,21 @@ const getTopLevelNodesFromSelection = (selection: Selection, doc: Node) => {
const nodes: {node: Node; pos: number}[] = [];
if (selection.from !== selection.to) {
const {from, to} = selection;

doc.nodesBetween(from, to, (node, pos) => {
const withinSelection = from <= pos && pos + node.nodeSize <= to;

if (

Check failure on line 106 in src/extensions/behavior/Selection/selection.ts

View workflow job for this annotation

GitHub Actions / Verify Files

Replace `⏎················node·&&⏎················!node.isText·&&⏎················node.type.spec.selectable·&&⏎················withinSelection⏎············` with `node·&&·!node.isText·&&·node.type.spec.selectable·&&·withinSelection`
node &&
node.type.name !== 'paragraph' &&
!node.isText &&
node.type.spec.selectable &&
withinSelection
) {
nodes.push({node, pos});

return false;
}

return true;
});
}
Expand Down
21 changes: 21 additions & 0 deletions src/extensions/markdown/Lists/ListsSpecs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,27 @@ export const ListsSpecs: ExtensionAuto = (builder) => {
spec: schemaSpecs[ListNode.ListItem],
toMd: serializerTokens[ListNode.ListItem],
fromMd: {tokenSpec: parserTokens[ListNode.ListItem]},
// @ts-expect-error
view: () => (node, view, getPos) => {
return {
update: (node_) => {
node = node_;

const pos = getPos();
if (pos === undefined) return false;

const dom = view.domAtPos(pos + 1).node as HTMLElement;

const markerWidth = Math.max(
Math.floor(parseFloat(getComputedStyle(dom, '::marker').width)),
);

dom.style.setProperty('--li-marker-width', `${markerWidth}px`);

return true;
},
};
},
}))
.addNode(ListNode.BulletList, () => ({
spec: schemaSpecs[ListNode.BulletList],
Expand Down

0 comments on commit 0ae5e41

Please sign in to comment.