Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
8 changes: 8 additions & 0 deletions CSS_template_literal.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<LanguageInjectionConfiguration>
<injection language="CSS" injector-id="js">
<display-name>CSS template literal</display-name>
<single-file value="true" />
<place><![CDATA[taggedString("css")]]></place>
<templateTag>css</templateTag>
</injection>
</LanguageInjectionConfiguration>
68 changes: 68 additions & 0 deletions src/components/svg/svg_editor.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { css } from '../../styling/css.ts';

export const greekLetters = {
alpha: 'α',
beta: 'β',
gamma: 'γ',
delta: 'δ',
epsilon: 'ε',
zeta: 'ζ',
eta: 'η',
theta: 'θ',
} as const;
const greekLetterNames = Object.keys(greekLetters);
const greeksFirstLine = greekLetterNames.slice(0, 6);
const greeksLastLine = greekLetterNames.slice(6);

export const primes = {
prime1: '′',
prime2: '″',
prime3: '‴',
};
const primeNames = Object.keys(primes);

export const atomLabelEditCss = css`
form.react-ocl-atom-label-edit {
--box-size: 24px;

position: absolute;
z-index: 1;
line-height: 1;
font-size: 16px;
display: grid;
grid-template-columns: repeat(4, var(--box-size));
grid-template-rows: repeat(3, var(--box-size));
grid-template-areas:
'input input input input submit cancel'
'${greeksFirstLine.join(' ')}'
'${greeksLastLine.join(' ')} . ${primeNames.join(' ')}';
place-items: stretch;
gap: 0.25em;
border: 1px solid lightgray;
background-color: white;
padding: 0.25em;
box-sizing: border-box;
}

form.react-ocl-atom-label-edit button.react-ocl {
padding: 0.25em;
background-color: #efefef;
border: none;
border-radius: 5px;
font-family: sans-serif;
width: var(--box-size);
font-size: 13.3333px;
}

form.react-ocl-atom-label-edit input.react-ocl {
padding: 0.25em;
border: solid 1px lightgrey;
border-radius: 3px;
font-family: sans-serif;
font-size: 13.3333px;
}

form.react-ocl-atom-label-edit input.react-ocl:focus {
outline: auto;
}
`;
54 changes: 14 additions & 40 deletions src/components/svg/svg_editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
import { useEffect, useMemo, useReducer, useRef, useState } from 'react';

import { useRefUpToDate } from '../../hooks/use_ref_up_to_date.js';
import { InlineStylesheet } from '../../styling/inline_stylesheet.tsx';
import type { BaseEditorProps } from '../types.js';

import {
Expand All @@ -22,6 +23,7 @@ import {
import type { State } from './editor/reducer.js';
import { stateReducer } from './editor/reducer.js';
import { useHighlight } from './editor/use_highlight.js';
import { atomLabelEditCss, greekLetters, primes } from './svg_editor.css.ts';
import type { SvgRendererProps } from './svg_renderer.js';
import { SvgRenderer } from './svg_renderer.js';

Expand Down Expand Up @@ -181,26 +183,6 @@ interface AtomLabelEditFormProps {
onCancel: () => void;
}

const greekLetters = {
alpha: 'α',
beta: 'β',
gamma: 'γ',
delta: 'δ',
epsilon: 'ε',
zeta: 'ζ',
eta: 'η',
theta: 'θ',
} as const;
const greekLetterNames = Object.keys(greekLetters);
const greeksFirstLine = greekLetterNames.slice(0, 6);
const greeksLastLine = greekLetterNames.slice(6);
const primes = {
prime1: '′',
prime2: '″',
prime3: '‴',
};
const primeNames = Object.keys(primes);

function AtomLabelEditForm(props: AtomLabelEditFormProps) {
const { defaultValue, formCoords, onSubmit, onCancel } = props;

Expand Down Expand Up @@ -280,25 +262,12 @@ function AtomLabelEditForm(props: AtomLabelEditFormProps) {
ref={formRef}
onSubmit={onFormSubmit}
onKeyDown={onKeyDown}
style={{
position: 'absolute',
top: formCoords.y,
left: formCoords.x,
display: 'grid',
gridTemplateAreas: `
"input input input input submit cancel"
"${greeksFirstLine.join(' ')}"
"${greeksLastLine.join(' ')} . ${primeNames.join(' ')}"
`,
gridTemplateColumns: 'repeat(4, 1.5em)',
alignItems: 'stretch',
gap: '0.25em',
border: '1px solid lightgray',
backgroundColor: 'white',
padding: '0.25em',
}}
className="react-ocl react-ocl-atom-label-edit"
style={{ top: formCoords.y, left: formCoords.x }}
>
<InlineStylesheet>{atomLabelEditCss}</InlineStylesheet>
<input
className="react-ocl"
style={{ gridArea: 'input' }}
type="text"
name="label"
Expand All @@ -307,13 +276,16 @@ function AtomLabelEditForm(props: AtomLabelEditFormProps) {
autoFocus
ref={autoSelectText}
/>
<input
<button
className="react-ocl"
style={{ gridArea: 'submit' }}
type="submit"
value="✔️"
aria-label="Submit"
/>
>
✔️
</button>
<button
className="react-ocl"
style={{ gridArea: 'cancel' }}
type="button"
aria-label="Cancel"
Expand All @@ -324,6 +296,7 @@ function AtomLabelEditForm(props: AtomLabelEditFormProps) {

{Object.entries(greekLetters).map(([charName, greekChar]) => (
<button
className="react-ocl"
key={charName}
type="submit"
style={{ gridArea: charName }}
Expand All @@ -335,6 +308,7 @@ function AtomLabelEditForm(props: AtomLabelEditFormProps) {

{Object.entries(primes).map(([primeName, primeChar]) => (
<button
className="react-ocl"
key={primeName}
type="submit"
style={{ gridArea: primeName }}
Expand Down
12 changes: 12 additions & 0 deletions src/styling/css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/raw#building_an_identity_tag
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const css = (strings: TemplateStringsArray, ...values: any[]) =>
String.raw({ raw: strings }, ...values);

/**
* To get CSS autocomplete in css tagged template literal:
*
* WebStorm:
* Settings | Editor | Language Injections
* Click on the little import button and select `CSS_template_literal.xml` file.
*/
15 changes: 15 additions & 0 deletions src/styling/inline_stylesheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export interface InlineStylesheetProps {
children: string;
}

// eslint-disable-next-line jsdoc/require-jsdoc
export function InlineStylesheet(props: InlineStylesheetProps) {
const { children } = props;

return (
// https://react.dev/reference/react-dom/components/style#special-rendering-behavior
<style precedence="medium" href={children}>
{children}
</style>
);
}
8 changes: 8 additions & 0 deletions stories/reset/ocl_reset.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { InlineStylesheet } from '../../src/styling/inline_stylesheet.tsx';

import { resetCss } from './ocl_reset_css.ts';

// eslint-disable-next-line jsdoc/require-jsdoc
export function OclReset() {
return <InlineStylesheet>{resetCss}</InlineStylesheet>;
}
Loading
Loading