diff --git a/src/editor/index.ts b/src/editor/index.ts index 84e4b2d..354f460 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -1,5 +1,4 @@ export { default as Delimiter } from './delimiter' export { default as Heading } from './heading' -export { default as List } from './list' export { default as Charges } from './charges' export { default as DnDStats } from './dnd-stats' diff --git a/src/editor/list.ts b/src/editor/list.ts deleted file mode 100644 index 88cecf4..0000000 --- a/src/editor/list.ts +++ /dev/null @@ -1,254 +0,0 @@ -import { - ContentBlock, - ContentBlockArgs, - ContentBlockConfig, - ContentBlockData, - ConversionConfig -} from './content-block' - -import icon from '../assets/editor/list.svg.txt' -import iconUL from '../assets/editor/list_unordered.svg.txt' -import iconOL from '../assets/editor/list_ordered.svg.txt' - -const title = 'List' - -enum ListStyle { - Ordered = 'OL', - Unordered = 'UL' -} -const icons = { - [ListStyle.Ordered]: iconOL, - [ListStyle.Unordered]: iconUL -} - -interface ListConfig extends ContentBlockConfig { - placeholder?: string; - styles: ListStyle[]; - defaultStyle: ListStyle; -} - -interface ListData extends ContentBlockData { - style: ListStyle; - items: string[]; -} - -interface ListConversionConfig extends ConversionConfig { - import: (str: string) => ListData; - export: (data: ListData) => string; -} - -class List extends ContentBlock { - static _supportedTags = ['UL', 'OL', 'LI'] - static _toolboxConfig = { icon, title } - - protected _config: ListConfig - private defaultStyle: ListStyle - private currentStyle: ListStyle - - constructor (args: ContentBlockArgs) { - super(args) - this._config = args.config as ListConfig - - if (this._config.styles === undefined) { - this._config.styles = [ListStyle.Unordered, ListStyle.Ordered] - } - - if (this._config.defaultStyle === undefined) { - this._config.defaultStyle = ListStyle.Unordered - } - if (this._config.styles.indexOf(this._config.defaultStyle) === -1) { - console.warn('(ง\'̀-\'́)ง List Tool: the default style specified was not found in available styles') - } - this.defaultStyle = this._config.defaultStyle - this.currentStyle = this.defaultStyle - - this.data = { - style: this.currentStyle, - items: (args.data as ListData).items - } - - this._settingButtons = this._config.styles.map(listStyle => { - return { - name: listStyle, - icon: icons[listStyle] || icon, - action: (name: string) => this.setStyle(name as ListStyle), - isActive: (name: string): boolean => name === this.currentStyle - } - }) - } - - public get data (): ListData { - // first render - if (!this._element) return this._data as ListData - - const items = this.queryItems() - const data = this._data as ListData - data.items = Array.from(items).map(item => item.innerText) - return data - } - - public set data (data: ListData) { - const currentData = this._data as ListData - - if (data.style === undefined) data.style = currentData.style || this.defaultStyle - if (data.items === undefined) data.items = currentData.items || [] - - this._data = data - this.currentStyle = data.style - - const newList = this._render() - if (this._element.parentNode) { - this._element.parentNode.replaceChild(newList, this._element) - } - this._element = newList - } - - private setStyle (style: ListStyle) { - this.data = { style, items: this.data.items || [] } - } - - protected _render (): HTMLElement { - const el = document.createElement(this.currentStyle) - el.classList.add('cdx-list', this._CSS.block) - el.contentEditable = 'true' - el.dataset.placeholder = this._config.placeholder || '' - - const items = this.data.items - - if (!items || !items.length) { - const li = document.createElement('LI') - li.innerText = '' - li.classList.add('cdx-list__item') - el.appendChild(li) - } else { - items.forEach(item => { - const li = document.createElement('LI') - li.innerText = item - li.classList.add('cdx-list__item') - el.appendChild(li) - }) - } - - el.addEventListener('keydown', event => { - // on pressing Enter - if (event.keyCode === 13) return this.getOutOfList(event) - // on pressing Backspace - if (event.keyCode === 8) return this.backspace(event) - }, false) - - return el - } - - private queryItems (): NodeListOf { - return this._element.querySelectorAll('.cdx-list__item') - } - - private get currentItem (): Element | undefined { - const selection = window.getSelection() - if (selection === null) return - - let currentNode = selection.anchorNode - if (currentNode === null) return - - if (currentNode.nodeType !== Node.ELEMENT_NODE) { - currentNode = currentNode.parentNode - } - - return (currentNode as HTMLElement).closest('.cdx-list__item') || undefined - } - - // leave list by pressing enter on an empty list item - private getOutOfList (event: KeyboardEvent) { - const items = this.queryItems() - if (items.length < 2) return - - const lastItem = items[items.length - 1] - const currentItem = this.currentItem - - // prevent generation of new li if last li is empty - if (currentItem === lastItem && !lastItem.innerText.trim().length) { - if (!currentItem.parentElement) return // somethings really wrong - currentItem.parentElement.removeChild(currentItem) - this.api.blocks.insertNewBlock() - event.preventDefault() - event.stopPropagation() - } - } - - public backspace (event: KeyboardEvent) { - const items = this.queryItems() - const firstItem = items[0] - - if (firstItem === undefined) return - if (items.length < 2 && !firstItem.innerText.trim()) event.preventDefault() - } - - public selectItem (event: KeyboardEvent) { - event.preventDefault() - - const selection = window.getSelection() - if (selection === null) return // no selection, no problem - - const currentNode = selection.anchorNode?.parentNode - if (currentNode === null) return - - const currentItem = (currentNode as Element).closest('.cdx-list__item') - if (currentItem === null) return - - const range = new Range() - - range.selectNodeContents(currentItem) - selection.removeAllRanges() - selection.addRange(range) - } - - protected pasteHandler (element: HTMLUListElement | HTMLOListElement | HTMLLIElement): ListData { - const tag = element.tagName - const style = tag === ListStyle.Ordered ? ListStyle.Ordered : ListStyle.Unordered - const data: ListData = { style, items: [] } - - if (tag === 'LI') { // does this really happen? - data.items.push(element.innerText) - } else { - const items = Array.from(element.querySelectorAll('LI')) - data.items = items.map(item => (item as HTMLElement).innerText).filter(item => !!item.trim()) - } - - return data - } - - // allow to use native enter behavior - static get enableLineBreaks (): boolean { - return true - } - - static get sanitize () { - return { - style: {}, - items: { - br: true - } - } - } - - static get conversionConfig (): ListConversionConfig { - return { - export: data => { - return data.items.join(' * ') - }, - import: str => { - return { - items: [str], - style: ListStyle.Unordered - } - } - } - } - - public save (): ListData { - console.log('saving list', this.data) - return this.data - } -} - -export default List