Skip to content

Commit

Permalink
Merge pull request #90 from editor-js/ol-customisation
Browse files Browse the repository at this point in the history
feat(OrderedList UI): support start property
  • Loading branch information
e11sy authored Oct 23, 2024
2 parents 047bb73 + 7289432 commit 307277c
Show file tree
Hide file tree
Showing 15 changed files with 246 additions and 29 deletions.
2 changes: 2 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@
{
type: 'List',
data: {
style: 'ordered',
start: 2,
items: [
{
content: "Canon",
Expand Down
3 changes: 2 additions & 1 deletion src/ListRenderer/ChecklistRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { IconCheck } from '@codexteam/icons';
import type { ChecklistItemMeta } from '../types/ItemMeta';
import type { NestedListConfig } from '../types/ListParams';
import * as Dom from '@editorjs/dom';
import { DefaultListCssClasses, CssPrefix } from './ListRenderer';
import { DefaultListCssClasses } from './ListRenderer';
import type { ListCssClasses, ListRendererInterface } from './ListRenderer';
import { CssPrefix } from '../styles/CssPrefix';

/**
* Interface that represents all list used only in unordered list rendering
Expand Down
6 changes: 1 addition & 5 deletions src/ListRenderer/ListRenderer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
/**
* Default css prefix for list
*/
export const CssPrefix = 'cdx-list';

import { CssPrefix } from '../styles/CssPrefix';
/**
* CSS classes for the List Tool
*/
Expand Down
3 changes: 2 additions & 1 deletion src/ListRenderer/OrderedListRenderer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { OrderedListItemMeta } from '../types/ItemMeta';
import type { NestedListConfig } from '../types/ListParams';
import * as Dom from '@editorjs/dom';
import { DefaultListCssClasses, CssPrefix } from './ListRenderer';
import { DefaultListCssClasses } from './ListRenderer';
import type { ListCssClasses, ListRendererInterface } from './ListRenderer';
import { CssPrefix } from '../styles/CssPrefix';

/**
* Interface that represents all list used only in unordered list rendering
Expand Down
3 changes: 2 additions & 1 deletion src/ListRenderer/UnorderedListRenderer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { UnorderedListItemMeta } from '../types/ItemMeta';
import type { NestedListConfig } from '../types/ListParams';
import * as Dom from '@editorjs/dom';
import { DefaultListCssClasses, CssPrefix } from './ListRenderer';
import { DefaultListCssClasses } from './ListRenderer';
import type { ListCssClasses, ListRendererInterface } from './ListRenderer';
import { CssPrefix } from '../styles/CssPrefix';

/**
* Interface that represents all list used only in unordered list rendering
Expand Down
4 changes: 2 additions & 2 deletions src/ListRenderer/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CheckListRenderer } from './ChecklistRenderer';
import { OrderedListRenderer } from './OrderedListRenderer';
import { UnorderedListRenderer } from './UnorderedListRenderer';
import { DefaultListCssClasses, CssPrefix } from './ListRenderer';
import { DefaultListCssClasses } from './ListRenderer';

export { CheckListRenderer, OrderedListRenderer, UnorderedListRenderer, DefaultListCssClasses, CssPrefix };
export { CheckListRenderer, OrderedListRenderer, UnorderedListRenderer, DefaultListCssClasses };
34 changes: 32 additions & 2 deletions src/ListTabulator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@ export default class ListTabulator<Renderer extends ListRenderer> {
);
}

/**
* Set start property value from initial data
*/
if (this.data.start !== undefined) {
this.changeStartWith(this.data.start);
}

return this.listWrapper;
}

Expand Down Expand Up @@ -188,10 +195,21 @@ export default class ListTabulator<Renderer extends ListRenderer> {
});
};

return {
const composedListItems = listWrapper ? getItems(listWrapper) : [];

let dataToSave: ListData = {
style: this.data.style,
items: listWrapper ? getItems(listWrapper) : [],
items: composedListItems,
};

if (this.data.style === 'ordered') {
dataToSave = {
start: this.data.start,
...dataToSave,
};
}

return dataToSave;
}

/**
Expand Down Expand Up @@ -354,6 +372,16 @@ export default class ListTabulator<Renderer extends ListRenderer> {
return data;
}

/**
* Changes ordered list start property value
* @param index - new value of the start property
*/
public changeStartWith(index: number): void {
this.listWrapper!.style.setProperty('counter-reset', `item ${index - 1}`);

this.data.start = index;
}

/**
* Handles Enter keypress
* @param event - keydown
Expand Down Expand Up @@ -595,6 +623,8 @@ export default class ListTabulator<Renderer extends ListRenderer> {

const newListContent = this.save(newListWrapper);

newListContent.start = this.data.style == 'ordered' ? 1 : undefined;

/**
* Get current list block index
*/
Expand Down
78 changes: 63 additions & 15 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { API, BlockAPI, PasteConfig, ToolboxConfig } from '@editorjs/editorjs';
import type {
BlockToolConstructorOptions,
ToolConfig,
TunesMenuConfig
MenuConfigItem,
ToolConfig
} from '@editorjs/editorjs/types/tools';
import { IconListBulleted, IconListNumbered, IconChecklist } from '@codexteam/icons';
import type { NestedListConfig, ListData, ListDataStyle, ListItem } from './types/ListParams';
Expand All @@ -13,7 +13,9 @@ import type { ListRenderer } from './types/ListRenderer';
/**
* Build styles
*/
import './../styles/index.pcss';
import './styles/list.pcss';
import './styles/input.pcss';
import { renderToolboxInput } from './utils/renderToolboxInput';

/**
* Constructor Params for Nested List Tool, use to pass initial data and settings
Expand Down Expand Up @@ -254,35 +256,81 @@ export default class NestedList {
* Creates Block Tune allowing to change the list style
* @returns array of tune configs
*/
public renderSettings(): TunesMenuConfig {
const tunes = [
public renderSettings(): MenuConfigItem[] {
const defaultTunes: MenuConfigItem[] = [
{
name: 'unordered' as const,
label: this.api.i18n.t('Unordered'),
icon: IconListBulleted,
closeOnActivate: true,
onActivate: () => {
this.listStyle = 'unordered';
},
},
{
name: 'ordered' as const,
label: this.api.i18n.t('Ordered'),
icon: IconListNumbered,
closeOnActivate: true,
onActivate: () => {
this.listStyle = 'ordered';
},
},
{
name: 'checklist' as const,
label: this.api.i18n.t('Checklist'),
icon: IconChecklist,
closeOnActivate: true,
onActivate: () => {
this.listStyle = 'checklist';
},
},
];

return tunes.map(tune => ({
name: tune.name,
icon: tune.icon,
label: tune.label,
isActive: this.data.style === tune.name,
closeOnActivate: true,
onActivate: () => {
this.listStyle = tune.name;
},
}));
if (this.listStyle === 'ordered') {
const startWithElement = renderToolboxInput(
(index: number) => this.changeStartWith(index),
{
value: String(this.data.start ?? 1),
placeholder: '',
attributes: {
type: 'number',
step: '1',
required: 'true',
},
});

const unorderedListTunes: MenuConfigItem[] = [
{
name: 'start with' as const,
label: this.api.i18n.t('Start with'),
children: {
items: [
{
name: 'start with input',
element: startWithElement,
// @ts-expect-error ts(2820) can not use PopoverItem enum from editor.js types
type: 'html',
},
],
},
},
];

defaultTunes.push(...unorderedListTunes);
}

return defaultTunes;
}

/**
* Changes ordered list start property value
* @param index - new value of the start property
*/
private changeStartWith(index: number): void {
this.list?.changeStartWith(index);

this.data.start = index;
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/styles/CssPrefix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* Default css prefix for list
*/
export const CssPrefix = 'cdx-list';
31 changes: 31 additions & 0 deletions src/styles/input.pcss
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.cdx-list-start-with-field {
background: #F8F8F8;
border: 1px solid rgba(226,226,229,0.20);
border-radius: 6px;
padding: 2px;
display: grid;
grid-template-columns: auto auto 1fr;
grid-template-rows: auto;

&__input {
font-size: 14px;
outline: none;
font-weight: 500;
font-family: inherit;
border: 0;
background: transparent;
margin: 0;
padding: 0;
line-height: 22px;
min-width: calc(100% - var(--toolbox-buttons-size) - var(--icon-margin-right));

&--invalid {
background: red;
}

&::placeholder {
color: var(--grayText);
font-weight: 500;
}
}
}
File renamed without changes.
7 changes: 6 additions & 1 deletion src/types/ItemMeta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ export interface ChecklistItemMeta extends ItemMeta {
/**
* Meta information of ordered list item
*/
export interface OrderedListItemMeta extends ItemMeta {};
export interface OrderedListItemMeta extends ItemMeta {
/**
* If passed, ordered list counters will start with this index
*/
start?: number;
};

/**
* Meta information of unordered list item
Expand Down
4 changes: 4 additions & 0 deletions src/types/ListParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export interface ListData {
* list of first-level elements
*/
items: ListItem[];
/**
* Start property used only in ordered list
*/
start?: number;
}

/**
Expand Down
94 changes: 94 additions & 0 deletions src/utils/renderToolboxInput.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import * as Dom from '@editorjs/dom';
import { CssPrefix } from '../styles/CssPrefix';

/**
* Options used in input rendering
*/
interface InputOptions {
/**
* Placeholder, that will be displayed in input
*/
placeholder: string;
/**
* Input will be rendered with this value inside
*/
value?: string;
/**
* Html attributes, that would be added to the input element
*/
attributes?: {
[key: string]: string;
};
}

const css = {
wrapper: `${CssPrefix}-start-with-field`,
input: `${CssPrefix}-start-with-field__input`,
inputInvalid: `${CssPrefix}-start-with-field__input--invalid`,
};

/**
* Method that renders html element for popover start with item
* @param inputCallback - callback method that could change nested list attributes on input
* @param inputOptions - options used in input rendering
* @param inputOptions.value - input will be rendered with this value inside
* @param inputOptions.placeholder - placeholder, that will be displayed in input
* @param inputOptions.attributes - html attributes, that would be added to the input element
* @returns - rendered html element
*/
export function renderToolboxInput(inputCallback: (index: number) => void,
{ value, placeholder, attributes }: InputOptions): HTMLElement {
const startWithElementWrapper = Dom.make('div', css.wrapper);

const input = Dom.make('input', css.input, {
placeholder,
/**
* Used to prevent focusing on the input by Tab key
* (Popover in the Toolbar lays below the blocks,
* so Tab in the last block will focus this hidden input if this property is not set)
*/
tabIndex: -1,
/**
* Value of the start property, if it is not specified, then it is set to one
*/
value,
}) as HTMLInputElement;

/**
* Add passed attributes to the input
*/
for (const attribute in attributes) {
input.setAttribute(attribute, attributes[attribute]);
}

startWithElementWrapper.appendChild(input);

input.addEventListener('input', () => {
const validInput = input.checkValidity();

/**
* If input is invalid and classlist does not contain invalid class, add it
*/
if (!validInput && !input.classList.contains(css.inputInvalid)) {
input.classList.add(css.inputInvalid);
}

/**
* If input is valid and classlist contains invalid class, remove it
*/
if (validInput && input.classList.contains(css.inputInvalid)) {
input.classList.remove(css.inputInvalid);
}

/**
* If input is invalid, than do not change start with attribute
*/
if (!validInput) {
return;
}

inputCallback(Number(input.value));
});

return startWithElementWrapper;
}
Loading

0 comments on commit 307277c

Please sign in to comment.