Skip to content

Commit

Permalink
wip: menu. Added per container and per item custom classes
Browse files Browse the repository at this point in the history
  • Loading branch information
arnog committed Nov 19, 2023
1 parent 4452e2a commit eaf14d8
Show file tree
Hide file tree
Showing 11 changed files with 320 additions and 169 deletions.
26 changes: 25 additions & 1 deletion css/mathfield.less
Original file line number Diff line number Diff line change
Expand Up @@ -366,4 +366,28 @@ with this style */
.ML__incorrectPromptBox {
outline: 1px solid var(--_incorrect-color);
box-shadow: 0 0 5px var(--_incorrect-color);
}
}

.menu-container-variant {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
column-count: 3;
column-gap: 8px;
padding: 8px;
margin: 8px;
}

.menu-container-variant [part=menu-item] {
font-size: 2rem;
text-align: center;
margin: 0;
}

.menu-container-insert-matrix [part=menu-item] {
font-size: 2rem;
line-height: 1.2;
text-align: center;
}

2 changes: 1 addition & 1 deletion src/addons/auto-render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export type AutoRenderOptionsPrivate = AutoRenderOptions & {
renderToSpeakableText?: (text: string) => string;

/** A function to convert MathJSON to a LaTeX string */
serializeToLatex?: (json: any) => string;
serializeToLatex?: (json: unknown) => string;

ignoreClassPattern?: RegExp;
processClassPattern?: RegExp;
Expand Down
4 changes: 2 additions & 2 deletions src/core/box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ export class Box implements BoxInterface {
if (this.hasExplicitWidth) {
// console.assert(cssProps.width === undefined);
if (cssProps.width === undefined)
cssProps.width = `${Math.round(this._width * 100) / 100}em`;
cssProps.width = `${Math.ceil(this._width * 100) / 100}em`;
// cssProps['height'] = `${Math.round(this.height * 100) / 100}em`;
}
const styles = Object.keys(cssProps).map((x) => `${x}:${cssProps[x]}`);
Expand All @@ -470,7 +470,7 @@ export class Box implements BoxInterface {
this.scale !== 1.0 &&
(body.length > 0 || svgMarkup.length > 0)
)
styles.push(`font-size: ${Math.round(this.scale * 10000) / 100}%`);
styles.push(`font-size: ${Math.ceil(this.scale * 10000) / 100}%`);

if (this.htmlStyle) {
const entries = this.htmlStyle.split(';');
Expand Down
2 changes: 1 addition & 1 deletion src/core/skip-box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export class SkipBox extends Box {
}
toMarkup(): string {
return `<span style="display:inline-block;width:${
Math.round(this.width * 100) / 100
Math.ceil(this.width * 100) / 100
}em"></span>`;
}
}
Expand Down
68 changes: 62 additions & 6 deletions src/editor/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,57 @@ function getVariantSubmenu(mf: _Mathfield): MenuItem[] {
visible: () => getSelection(mf).length === 1,
onMenuSelect: () => mf.insert('\\mathbf{#@}', { selectionMode: 'item' }),
},
{
label: () => convertLatexToMarkup(`\\vec{${getSelection(mf)}}`),
visible: () => getSelection(mf).length === 1,
onMenuSelect: () => mf.insert('\\vec{#@}', { selectionMode: 'item' }),
},
{
label: () =>
convertLatexToMarkup(`\\overrightarrow{${getSelection(mf)}}`),
visible: () => getSelection(mf).length > 0,
onMenuSelect: () =>
mf.insert('\\overrightarrow{#@}', { selectionMode: 'item' }),
},
{
label: () => convertLatexToMarkup(`\\overleftarrow{${getSelection(mf)}}`),
visible: () => getSelection(mf).length > 0,
onMenuSelect: () =>
mf.insert('\\overleftarrow{#@}', { selectionMode: 'item' }),
},
{
label: () => convertLatexToMarkup(`\\dot{${getSelection(mf)}}`),
visible: () => getSelection(mf).length === 1,
onMenuSelect: () => mf.insert('\\dot{#@}', { selectionMode: 'item' }),
},
{
label: () => convertLatexToMarkup(`\\ddot{${getSelection(mf)}}`),
visible: () => getSelection(mf).length === 1,
onMenuSelect: () => mf.insert('\\ddot{#@}', { selectionMode: 'item' }),
},
{
label: () => convertLatexToMarkup(`\\bar{${getSelection(mf)}}`),
visible: () => getSelection(mf).length === 1,
onMenuSelect: () => mf.insert('\\bar{#@}', { selectionMode: 'item' }),
},
{
label: () => convertLatexToMarkup(`\\overline{${getSelection(mf)}}`),
visible: () => getSelection(mf).length > 0,
onMenuSelect: () =>
mf.insert('\\overline{#@}', { selectionMode: 'item' }),
},
{
label: () => convertLatexToMarkup(`\\overgroup{${getSelection(mf)}}`),
visible: () => getSelection(mf).length > 0,
onMenuSelect: () =>
mf.insert('\\overgroup{#@}', { selectionMode: 'item' }),
},
{
label: () => convertLatexToMarkup(`\\overbrace{${getSelection(mf)}}`),
visible: () => getSelection(mf).length > 0,
onMenuSelect: () =>
mf.insert('\\overbrace{#@}', { selectionMode: 'item' }),
},
];
}

Expand All @@ -227,11 +278,6 @@ export function getDefaultMenuItems(mf: _Mathfield): MenuItem[] {
// {
// type: 'divider',
// },
{
label: 'Insert Text',
onMenuSelect: () => mf.executeCommand(['switchMode', 'text']),
visible: () => mf.model.mode === 'math',
},
{
label: 'Switch to Math Mode',
onMenuSelect: () => mf.executeCommand(['switchMode', 'math']),
Expand Down Expand Up @@ -279,6 +325,7 @@ export function getDefaultMenuItems(mf: _Mathfield): MenuItem[] {

{
label: 'Insert Matrix',
containerClass: 'menu-container-insert-matrix',
submenu: [
{
label: '(⋱)',
Expand Down Expand Up @@ -309,11 +356,20 @@ export function getDefaultMenuItems(mf: _Mathfield): MenuItem[] {
},
],
},
{
label: 'Insert Text',
onMenuSelect: () => mf.executeCommand(['switchMode', 'text']),
visible: () => mf.model.mode === 'math',
},

{
type: 'divider',
},
{ label: 'Variant', submenu: getVariantSubmenu(mf) },
{
label: 'Variant',
containerClass: 'menu-container-variant',
submenu: getVariantSubmenu(mf),
},
{
type: 'divider',
},
Expand Down
8 changes: 4 additions & 4 deletions src/ui/geometry/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,10 @@ export function fitInViewport(

width = Math.min(left! + width, window.innerWidth - 8) - left!;

element.style.left = `${Math.round(left!).toString()}px`;
element.style.top = `${Math.round(top!).toString()}px`;
element.style.height = `${Math.round(height).toString()}px`;
element.style.width = `${Math.round(width).toString()}px`;
element.style.left = `${Math.ceil(left!).toString()}px`;
element.style.top = `${Math.ceil(top!).toString()}px`;
element.style.height = `${Math.ceil(height).toString()}px`;
element.style.width = `${Math.ceil(width).toString()}px`;
}

export function distance(
Expand Down
16 changes: 12 additions & 4 deletions src/ui/menu/menu-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { KeyboardModifiers } from 'ui/events/types';
import { keyboardModifiersFromEvent } from 'ui/events/utils';
import { MenuList } from './menu-list';
import {
DynamicPredicate,
DynamicBoolean,
DynamicString,
MenuInterface,
MenuItemInterface,
Expand Down Expand Up @@ -30,6 +30,8 @@ export class _MenuItem<T> implements MenuItemInterface {
_enabled: boolean;
_visible: boolean;

_class?: string;

id?: string;
data?: T;

Expand Down Expand Up @@ -61,6 +63,8 @@ export class _MenuItem<T> implements MenuItemInterface {
this.checked =
evalToBoolean(template, template.checked, modifiers) ?? false;

this._class = template.class;

this.id = template.id;
this._label = evalToString(template, template.label, modifiers);
this.ariaLabel = evalToString(template, template.ariaLabel, modifiers);
Expand All @@ -72,7 +76,10 @@ export class _MenuItem<T> implements MenuItemInterface {

if (Array.isArray(template.submenu)) {
this._type = 'submenu';
this.submenu = new MenuList(template.submenu, { parentMenu });
this.submenu = new MenuList(template.submenu, {
parentMenu,
containerClass: template.containerClass,
});
this.submenu.updateMenu(modifiers);
} else if (template.type === undefined && template.checked !== undefined)
this._type = 'checkbox';
Expand Down Expand Up @@ -108,6 +115,7 @@ export class _MenuItem<T> implements MenuItemInterface {

if (this.type === 'divider') {
const li = document.createElement('li');
if (this._class) li.className = this._class;
li.setAttribute('part', 'menu-divider');
li.setAttribute('role', 'divider');
return li;
Expand All @@ -122,6 +130,7 @@ export class _MenuItem<T> implements MenuItemInterface {
return null;

const li = document.createElement('li');
if (this._class) li.className = this._class;
li.setAttribute('part', 'menu-item');
li.setAttribute('tabindex', '-1');
if (this.type === 'radio') li.setAttribute('role', 'menuitemradio');
Expand All @@ -144,7 +153,6 @@ export class _MenuItem<T> implements MenuItemInterface {

if (!this.enabled) li.setAttribute('aria-disabled', 'true');
else {
li.removeAttribute('aria-disabled');
li.addEventListener('pointerenter', this);
li.addEventListener('pointerleave', this);
li.addEventListener('pointerup', this);
Expand Down Expand Up @@ -337,7 +345,7 @@ function speed(dx: number, dy: number, dt: number): number {

function evalToBoolean(
item: MenuItem,
value: DynamicPredicate | undefined,
value: DynamicBoolean | undefined,
modifiers?: KeyboardModifiers
): boolean | undefined {
if (typeof value === 'boolean') return value;
Expand Down
57 changes: 27 additions & 30 deletions src/ui/menu/menu-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import {
MenuItemInterface,
MenuItem,
RootMenuInterface,
MenuSelectEvent,
} from './types';
import { _MenuItem } from './menu-item';

/**
* A collection of menu items.
*
* Can be a main menu, or a submenu.
* Can be a main menu or a submenu.
*
*/
export class MenuList implements MenuInterface {
Expand All @@ -28,20 +29,24 @@ export class MenuList implements MenuInterface {
/*
* The menu items template are preserved so that the actual menu items
* can be recalculated, for example if the keyboard modifiers change.
* (when isDynamic is true)
* (when Menu.isDynamic is true)
*/
private _menuItemsDescriptions: MenuItem[];

private _containerClass?: string;

constructor(
menuItems: MenuItem[],
options?: {
parentMenu?: MenuInterface;
containerClass?: string;
}
) {
this.parentMenu = options?.parentMenu ?? null;

this._menuItemsDescriptions = [...menuItems];
this.isSubmenuOpen = false;
this._containerClass = options?.containerClass;
}

handleEvent(event: Event): void {
Expand All @@ -50,7 +55,7 @@ export class MenuList implements MenuInterface {
// Scroll wheel: adjust scroll position
this._element.scrollBy(0, ev.deltaY);

event.preventDefault();
// event.preventDefault();
event.stopPropagation();
}
}
Expand Down Expand Up @@ -261,8 +266,10 @@ export class MenuList implements MenuInterface {
result =
candidates.find(
(x) =>
MenuList.collator.compare(text, x.label.substr(i, text.length)) ===
0
MenuList.collator.compare(
text,
x.label.substring(i, text.length)
) === 0
) ?? null;
i++;
}
Expand All @@ -271,34 +278,32 @@ export class MenuList implements MenuInterface {
}

makeElement(): HTMLElement {
const ul = document.createElement('ul');
ul.classList.add('ui-menu-container');
ul.setAttribute('part', 'ui-menu-container');
ul.setAttribute('tabindex', '-1');
ul.setAttribute('role', 'menu');
ul.setAttribute('aria-orientation', 'vertical');
ul.addEventListener('wheel', this, { passive: true });

// Remove all items
ul.textContent = '';
const menu = document.createElement('menu');
menu.setAttribute('role', 'menu');
menu.setAttribute('tabindex', '-1');
menu.setAttribute('aria-orientation', 'vertical');
menu.setAttribute('part', 'ui-menu-container');
if (this._containerClass) menu.classList.add(this._containerClass);
menu.classList.add('ui-menu-container');
menu.addEventListener('wheel', this, { passive: true });

// Remove consecutive dividers
let wasDivider = false;
let wasDivider = true; // Avoid divider as first item
for (const item of this._menuItems) {
// Avoid consecutive dividers
if (item.type === 'divider') {
// Avoid consecutive dividers
if (wasDivider) item.visible = false;
wasDivider = true;
} else if (item.visible) wasDivider = false;
}

// Add back all necessary items (after they've been updated if applicable)
// Add all visible items
for (const { element, visible } of this._menuItems)
if (element && visible) ul.append(element);
if (element && visible) menu.append(element);

ul.querySelector('li:first-of-type')?.setAttribute('tabindex', '0');
menu.querySelector('li:first-of-type')?.setAttribute('tabindex', '0');

return ul;
return menu;
}

/**
Expand Down Expand Up @@ -429,20 +434,12 @@ export function evalToString(
return undefined;
}

export type MenuSelectEvent = {
keyboardModifiers?: KeyboardModifiers;
id?: string;
label?: string;
data?: any;
element?: HTMLElement;
};

declare global {
/**
* Map the custom event names to types
* @internal
*/
export interface DocumentEventMap {
['select']: CustomEvent<MenuSelectEvent>;
['menu-select']: CustomEvent<MenuSelectEvent>;
}
}
Loading

0 comments on commit eaf14d8

Please sign in to comment.