Skip to content

Commit

Permalink
Typescriptify Phase II 🐝 (#737)
Browse files Browse the repository at this point in the history
* Typescriptify attributable + MarkupSection 🐝

* Atom + AtomNode 🐥

* CardNode + Markup 🦏

* Image + ListItem + ListSection 🦀🦀

* LifecycleCallbacks + PostNodeBuilder + fixy fix 🐷

* POST 🦋

* RenderNode + Fixy 🐠
  • Loading branch information
ZeeJab authored Jul 22, 2020
1 parent adabc3e commit 44fc8ea
Show file tree
Hide file tree
Showing 33 changed files with 656 additions and 570 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ module.exports = {
"no-continue": "off",
"no-div-regex": "error",
"no-duplicate-imports": "off",
"no-dupe-class-members": "off",
"no-else-return": "off",
"no-empty-function": "off",
"no-eq-null": "error",
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@
"@rollup/plugin-commonjs": "^11.1.0",
"@rollup/plugin-node-resolve": "^7.1.3",
"@rollup/plugin-typescript": "^4.1.2",
"@typescript-eslint/eslint-plugin": "^3.0.2",
"@typescript-eslint/parser": "^3.0.2",
"@typescript-eslint/eslint-plugin": "^3.6.1",
"@typescript-eslint/parser": "^3.6.1",
"conventional-changelog-cli": "^2.0.34",
"eslint": "^7.1.0",
"eslint": "^7.4.0",
"eslint-config-prettier": "^6.11.0",
"jquery": "^3.5.1",
"jsdoc": "^3.6.4",
Expand All @@ -78,6 +78,6 @@
},
"volta": {
"node": "12.14.1",
"yarn": "1.22.1"
"yarn": "1.22.4"
}
}
6 changes: 3 additions & 3 deletions src/js/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import Environment from '../utils/environment'
import PostNodeBuilder from '../models/post-node-builder'
import { DEFAULT_TEXT_INPUT_HANDLERS } from './text-input-handlers'
import { DEFAULT_KEY_COMMANDS, buildKeyCommand, findKeyCommands, validateKeyCommand } from './key-commands'
import { CARD_MODES } from '../models/card'
import { CardMode } from '../models/card'
import assert from '../utils/assert'
import MutationHandler from 'mobiledoc-kit/editor/mutation-handler'
import EditHistory from 'mobiledoc-kit/editor/edit-history'
Expand Down Expand Up @@ -690,7 +690,7 @@ class Editor {
* @public
*/
editCard(cardSection) {
this._setCardMode(cardSection, CARD_MODES.EDIT)
this._setCardMode(cardSection, CardMode.EDIT)
}

/**
Expand All @@ -702,7 +702,7 @@ class Editor {
* @public
*/
displayCard(cardSection) {
this._setCardMode(cardSection, CARD_MODES.DISPLAY)
this._setCardMode(cardSection, CardMode.DISPLAY)
}

/**
Expand Down
28 changes: 0 additions & 28 deletions src/js/models/_attributable.js

This file was deleted.

49 changes: 49 additions & 0 deletions src/js/models/_attributable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { entries } from '../utils/object-utils'
import { contains } from '../utils/array-utils'

export const VALID_ATTRIBUTES = ['data-md-text-align']

export interface Attributable {
attributes: { [key: string]: string }
hasAttribute: (key: string) => boolean
setAttribute: (key: string, value: string) => void
removeAttribute: (key: string) => void
getAttribute: (key: string) => string
eachAttribute: (cb: (key: string, value: string) => void) => void
}

type AbstractConstructor<T> = Function & { prototype: T }
type Constructor<T> = new (...args: any[]) => T

/*
* A "mixin" to add section attribute support
* to markup and list sections.
*/
export function attributable<T extends unknown>(Base: AbstractConstructor<T>): Constructor<T & Attributable> {
return class extends (Base as any) {
attributes: { [key: string]: string } = {}

hasAttribute(key: string) {
return key in this.attributes
}

setAttribute(key: string, value: string) {
if (!contains(VALID_ATTRIBUTES, key)) {
throw new Error(`Invalid attribute "${key}" was passed. Constrain attributes to the spec-compliant whitelist.`)
}
this.attributes[key] = value
}

removeAttribute(key: string) {
delete this.attributes[key]
}

getAttribute(key: string) {
return this.attributes[key]
}

eachAttribute(cb: (key: string | number, value: string) => void) {
entries(this.attributes).forEach(([k, v]) => cb(k, v))
}
} as Constructor<T & Attributable>
}
29 changes: 21 additions & 8 deletions src/js/models/_markerable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ import assert from '../utils/assert'
import Position from '../utils/cursor/position'
import Section from './_section'
import Marker from './marker'
import { tagNameable } from './_tag-nameable'
import { Type } from './types'

export default abstract class Markerable extends Section {
tagName: string
type MarkerableType = Type.LIST_ITEM | Type.MARKUP_SECTION

export default abstract class Markerable extends tagNameable(Section) {
type: MarkerableType
markers: LinkedList<Marker>
builder: any

constructor(type: string, tagName: string, markers = []) {
constructor(type: MarkerableType, tagName: string, markers: Marker[] = []) {
super(type)
this.type = type
this.isMarkerable = true
this.tagName = tagName
this.markers = new LinkedList({
Expand All @@ -27,13 +31,13 @@ export default abstract class Markerable extends Section {
markers.forEach(m => this.markers.append(m))
}

canJoin(other: Section) {
canJoin(other: Markerable) {
return other.isMarkerable && other.type === this.type && other.tagName === this.tagName
}

clone() {
clone(): this {
const newMarkers = this.markers.map(m => m.clone())
return this.builder.createMarkerableSection(this.type, this.tagName, newMarkers)
return this.builder.createMarkerableSection(this.type, this.tagName, newMarkers) as any as this
}

get isBlank() {
Expand Down Expand Up @@ -87,7 +91,12 @@ export default abstract class Markerable extends Section {
// all markers before the marker/offset split go in beforeSection, and all
// after the marker/offset split go in afterSection
// @return {Array} [beforeSection, afterSection], two new sections
_redistributeMarkers(beforeSection: Markerable, afterSection: Markerable, marker: Marker, offset = 0) {
_redistributeMarkers(
beforeSection: Markerable,
afterSection: Markerable,
marker: Marker,
offset = 0
): [Section, Section] {
let currentSection = beforeSection
forEach(this.markers, m => {
if (m === marker) {
Expand Down Expand Up @@ -280,3 +289,7 @@ export default abstract class Markerable extends Section {
return { beforeMarker, afterMarker }
}
}

export function isMarkerable(section: Section): section is Markerable {
return section.isMarkerable
}
65 changes: 24 additions & 41 deletions src/js/models/_section.ts
Original file line number Diff line number Diff line change
@@ -1,67 +1,58 @@
import { normalizeTagName } from '../utils/dom-utils'
import LinkedItem from '../utils/linked-item'
import assert from '../utils/assert'
import Position from '../utils/cursor/position'
import LinkedList from '../utils/linked-list'
import Range from '../utils/cursor/range'
import Marker from './marker'
import RenderNode from './render-node'
import Post from './post'
import { isListSection } from './is-list-section'
import PostNodeBuilder from './post-node-builder'
import { Type } from './types'

export default abstract class Section extends LinkedItem<Section> {
type: string
isSection: boolean
isMarkerable: boolean
isNested: boolean
isLeafSection: boolean
export default class Section extends LinkedItem {
type: Type

isSection = true
isMarkerable = false
isNested = false
isListItem = false
isListSection = false
isLeafSection = true
isCardSection = false

post?: Post | null
parent: Section | null = null
renderNode: RenderNode | null = null
_tagName: string | null = null

constructor(type: string) {
parent: Section | null = null
builder!: PostNodeBuilder

constructor(type: Type) {
super()
assert('Cannot create section without type', !!type)
this.type = type
this.isSection = true
this.isMarkerable = false
this.isNested = false
this.isLeafSection = true
}

set tagName(val: string) {
let normalizedTagName = normalizeTagName(val)
assert(`Cannot set section tagName to ${val}`, this.isValidTagName(normalizedTagName))
this._tagName = normalizedTagName
}

get tagName() {
return this._tagName as string
get isBlank() {
return false
}

get length() {
return 0
}

abstract get isBlank(): boolean
abstract isValidTagName(_normalizedTagName: string): boolean
abstract clone(): Section
abstract canJoin(otherSection: Section): boolean
abstract textUntil(position: Position): string

/**
* @return {Position} The position at the start of this section
* @public
*/
headPosition() {
headPosition(): Position {
return this.toPosition(0)
}

/**
* @return {Position} The position at the end of this section
* @public
*/
tailPosition() {
tailPosition(): Position {
return this.toPosition(this.length)
}

Expand All @@ -70,7 +61,7 @@ export default abstract class Section extends LinkedItem<Section> {
* @return {Position} The position in this section at the given offset
* @public
*/
toPosition(offset: number) {
toPosition(offset: number): Position {
assert('Must pass number to `toPosition`', typeof offset === 'number')
assert('Cannot call `toPosition` with offset > length', offset <= this.length)

Expand All @@ -81,7 +72,7 @@ export default abstract class Section extends LinkedItem<Section> {
* @return {Range} A range from this section's head to tail positions
* @public
*/
toRange() {
toRange(): Range {
return this.headPosition().toRange(this.tailPosition())
}

Expand Down Expand Up @@ -136,11 +127,3 @@ export default abstract class Section extends LinkedItem<Section> {
return null
}
}

interface ListSection {
items: LinkedList<Section>
}

function isListSection(item: any): item is ListSection {
return 'items' in item && item.items
}
30 changes: 30 additions & 0 deletions src/js/models/_tag-nameable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { normalizeTagName } from '../utils/dom-utils'
import assert from '../utils/assert'
import Section from './_section'

type Constructor<T = {}> = new (...args: any[]) => T

export interface TagNameable {
tagName: string
isValidTagName(normalizedTagName: string): boolean
}

export function tagNameable(Base: Constructor<Section>) {
abstract class TagNameable extends Base {
_tagName: string | null = null

set tagName(val: string) {
let normalizedTagName = normalizeTagName(val)
assert(`Cannot set section tagName to ${val}`, this.isValidTagName(normalizedTagName))
this._tagName = normalizedTagName
}

get tagName() {
return this._tagName as string
}

abstract isValidTagName(normalizedTagName: string): boolean
}

return TagNameable
}
Loading

0 comments on commit 44fc8ea

Please sign in to comment.