Skip to content
Merged
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
92 changes: 81 additions & 11 deletions resources/dist/mail-editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ odden-mail-editor {

.ome-button,
.ome-icon-button,
.ome-palette-item,
.ome-structure-select,
.ome-handle {
background: var(--ome-surface);
Expand Down Expand Up @@ -136,7 +135,6 @@ odden-mail-editor {

.ome-button:hover:not(:disabled),
.ome-icon-button:hover:not(:disabled),
.ome-palette-item:hover,
.ome-structure-select:hover {
background: #f8fafc;
border-color: #cbd5e1;
Expand All @@ -154,27 +152,99 @@ odden-mail-editor {

/* Palette */

.ome-palette {
.ome-tabs {
background: var(--ome-canvas);
border-radius: 8px;
display: grid;
gap: 2px;
grid-template-columns: 1fr 1fr;
margin-bottom: 12px;
padding: 2px;
}

.ome-tab {
background: transparent;
border: 0;
border-radius: 6px;
color: var(--ome-muted);
cursor: pointer;
font: inherit;
font-weight: 600;
min-height: 30px;
}

.ome-tab[aria-selected='true'] {
background: var(--ome-surface);
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.12);
color: var(--ome-ink);
}

.ome-pane[hidden] {
display: none;
}

.ome-search {
margin-bottom: 12px;
width: 100%;
}

.ome-group-heading {
color: var(--ome-muted);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.06em;
margin: 14px 0 6px;
text-transform: uppercase;
}

.ome-palette-group:first-child .ome-group-heading {
margin-top: 0;
}

.ome-tiles {
display: grid;
gap: 6px;
grid-template-columns: 1fr;
grid-template-columns: repeat(2, minmax(0, 1fr));
}

.ome-palette-item {
.ome-tile {
align-items: center;
background: var(--ome-surface);
border: 1px solid var(--ome-line);
border-radius: 8px;
color: var(--ome-ink);
cursor: grab;
display: flex;
gap: 8px;
min-height: 34px;
padding: 4px 10px;
text-align: left;
flex-direction: column;
font: inherit;
font-size: 12px;
gap: 6px;
justify-content: center;
min-height: 72px;
padding: 10px 6px;
text-align: center;
touch-action: none;
user-select: none;
}

.ome-palette-glyph {
.ome-tile:hover {
background: var(--ome-accent-soft);
border-color: var(--ome-accent);
}

.ome-tile-icon {
color: var(--ome-accent);
font-weight: 700;
display: inline-flex;
}

.ome-tile-icon svg {
height: 24px;
width: 24px;
}

.ome-tile-label {
line-height: 1.25;
overflow-wrap: anywhere;
}

/* Structure list */
Expand Down
201 changes: 177 additions & 24 deletions resources/dist/mail-editor.js

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions resources/js/editor/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,28 @@ export function clear(element) {
element.removeChild(element.firstChild);
}
}

const SVG_NS = 'http://www.w3.org/2000/svg';

/** A decorative 24x24 outline icon from path data (the `d` of each path). */
export function icon(paths) {
const svg = document.createElementNS(SVG_NS, 'svg');

svg.setAttribute('viewBox', '0 0 24 24');
svg.setAttribute('fill', 'none');
svg.setAttribute('stroke', 'currentColor');
svg.setAttribute('stroke-width', '1.5');
svg.setAttribute('stroke-linecap', 'round');
svg.setAttribute('stroke-linejoin', 'round');
svg.setAttribute('aria-hidden', 'true');
svg.setAttribute('focusable', 'false');

for (const d of paths) {
const path = document.createElementNS(SVG_NS, 'path');

path.setAttribute('d', d);
svg.append(path);
}

return svg;
}
92 changes: 81 additions & 11 deletions resources/js/editor/editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ odden-mail-editor {

.ome-button,
.ome-icon-button,
.ome-palette-item,
.ome-structure-select,
.ome-handle {
background: var(--ome-surface);
Expand Down Expand Up @@ -136,7 +135,6 @@ odden-mail-editor {

.ome-button:hover:not(:disabled),
.ome-icon-button:hover:not(:disabled),
.ome-palette-item:hover,
.ome-structure-select:hover {
background: #f8fafc;
border-color: #cbd5e1;
Expand All @@ -154,27 +152,99 @@ odden-mail-editor {

/* Palette */

.ome-palette {
.ome-tabs {
background: var(--ome-canvas);
border-radius: 8px;
display: grid;
gap: 2px;
grid-template-columns: 1fr 1fr;
margin-bottom: 12px;
padding: 2px;
}

.ome-tab {
background: transparent;
border: 0;
border-radius: 6px;
color: var(--ome-muted);
cursor: pointer;
font: inherit;
font-weight: 600;
min-height: 30px;
}

.ome-tab[aria-selected='true'] {
background: var(--ome-surface);
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.12);
color: var(--ome-ink);
}

.ome-pane[hidden] {
display: none;
}

.ome-search {
margin-bottom: 12px;
width: 100%;
}

.ome-group-heading {
color: var(--ome-muted);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.06em;
margin: 14px 0 6px;
text-transform: uppercase;
}

.ome-palette-group:first-child .ome-group-heading {
margin-top: 0;
}

.ome-tiles {
display: grid;
gap: 6px;
grid-template-columns: 1fr;
grid-template-columns: repeat(2, minmax(0, 1fr));
}

.ome-palette-item {
.ome-tile {
align-items: center;
background: var(--ome-surface);
border: 1px solid var(--ome-line);
border-radius: 8px;
color: var(--ome-ink);
cursor: grab;
display: flex;
gap: 8px;
min-height: 34px;
padding: 4px 10px;
text-align: left;
flex-direction: column;
font: inherit;
font-size: 12px;
gap: 6px;
justify-content: center;
min-height: 72px;
padding: 10px 6px;
text-align: center;
touch-action: none;
user-select: none;
}

.ome-palette-glyph {
.ome-tile:hover {
background: var(--ome-accent-soft);
border-color: var(--ome-accent);
}

.ome-tile-icon {
color: var(--ome-accent);
font-weight: 700;
display: inline-flex;
}

.ome-tile-icon svg {
height: 24px;
width: 24px;
}

.ome-tile-label {
line-height: 1.25;
overflow-wrap: anywhere;
}

/* Structure list */
Expand Down
38 changes: 38 additions & 0 deletions resources/js/editor/icons.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions resources/js/editor/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,34 @@ export function dropLineY(rects, index) {

return index >= rects.length ? rects[rects.length - 1].bottom : rects[index].top;
}

/**
* The blocks of the palette for a search text, in groups: `[{key, label, blocks: [slotSchema]}]`. A block matches when
* every word of the query is found in its label, short label, description or group name. Empty groups are left out.
* A schema from a server that sends no groups lands in one group with no heading.
*/
export function groupBlocks(schemas, groups, query = '') {
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
const labelOf = new Map((groups ?? []).map((group) => [group.key, group.label]));
const matches = (slotSchema) => {
const text = [slotSchema.label, slotSchema.short_label, slotSchema.description, labelOf.get(slotSchema.group), slotSchema.type.replaceAll('_', ' ')].filter(Boolean).join(' ').toLowerCase();

return words.every((word) => text.includes(word));
};
const found = schemas.filter(matches);

if (!groups || groups.length === 0) {
return found.length > 0 ? [{ key: '', label: '', blocks: found }] : [];
}

const known = new Set(groups.map((group) => group.key));
const result = groups.map((group) => ({ ...group, blocks: found.filter((s) => s.group === group.key) }));
// A block whose group the server did not list is still shown, after the others.
const rest = found.filter((s) => !known.has(s.group));

if (rest.length > 0) {
result.push({ key: '', label: 'Other', blocks: rest });
}

return result.filter((group) => group.blocks.length > 0);
}
Loading
Loading