npm package: @motorcycle/mostly-dom
Description: Motorcycle.ts adapter for mostly-dom. Built on @motorcycle/dom.
Author: Tylor Steinberger
Latest version: 5.0.0

# @motorcycle/mostly-dom -- 4.1.0

Motorcycle.ts adapter for mostly-dom. Built on @motorcycle/dom.

## Get it
```sh
yarn add @motorcycle/mostly-dom
# or
npm install --save @motorcycle/mostly-dom
```

## API Documentation

All functions are curried!

#### DomSinks

<p>

Sinks type returns by a DOM component.

</p>


```typescript

export type DomSinks = { readonly view$: Stream<VNode> }

```


#### DomSource

<p>

DomSource type as defined by @motorcycle/dom

</p>


```typescript

interface DomSource {
  query(cssSelector: CssSelector): DomSource
  elements<El extends Element = Element>(): Stream<ReadonlyArray<El>>
  events<Ev extends Event = Event>(eventType: StandardEvents, options?: EventListenerOptions): Stream<Ev>
  cssSelectors(): ReadonlyArray<CssSelector>
}

```


#### DomSources

<p>

Sources type expected by a DOM component.

</p>


```typescript

export type DomSources<A = Element, B = Event> = { readonly dom: DomSource<A, B> }

```


#### Types

<p>

Virtual DOM node type from mostly-dom

</p>


```typescript

// All other types are used directly from mostly-dom
// https://github.com/TylorS167/mostly-dom

```


#### hyperscript-helpers

<p>

Functions for describing your views. 
Re-exported from [`mostly-dom`](https://github.com/TylorS167/mostly-dom)

</p>


<details>
  <summary>See an example</summary>
  
```typescript
import { VNode, div, h1, button } from '@motorcycle/mostly-dom'

function view(amount: number): VNode {
  return div([
    h1(`Clicked ${amount} times!`),
    button('Click me')
  ])
}
```

</details>

<details>
  <summary>See the code</summary>

```typescript

export * from 'mostly-dom'

export * from './isolate'
export * from './makeDomComponent'

```

</details>

<hr />


#### isolate\<Sources extends DomSources, Sinks extends DomSinks\>(component: Component\<Sources, Sinks\>, key: string, sources: Sources): Sinks

<p>

Isolates a component by adding an isolation class name to the outermost
DOM element emitted by the component’s view stream.

The isolation class name is generated by appending the given isolation `key`
to the prefix `$$isolation$$-`, e.g., given `foo` as `key` produces
`$$isolation$$-foo`.

Isolating components are useful especially when dealing with lists of a
specific component, so that events can be differentiated between the siblings.
However, isolated components are not isolated from access by an ancestor DOM
element.

Note that `isolate` is curried.

</p>


<details>
  <summary>See an example</summary>
  
```typescript
import { empty } from '@motorcycle/stream'
import { createDomSource } from '@motorcycle/dom'

const sources = createDomSource(empty())
const sinks = isolate(MyComponent, `myIsolationKey`, sources)
```

</details>

<details>
  <summary>See the code</summary>

```typescript

export const isolate: IsolatedComponent = curry3(function isolate<
  Sources extends DomSources,
  Sinks extends DomSinks
>(component: Component<Sources, Sinks>, key: string, sources: Sources): Sinks {
  const { dom } = sources
  const isolatedDom = dom.query(`.${KEY_PREFIX}${key}`)
  const sinks = component(Object.assign({}, sources, { dom: isolatedDom }))
  const isolatedSinks = Object.assign({}, sinks, { view$: isolateView(sinks.view$, key) })

  return isolatedSinks
})

const KEY_PREFIX = `__isolation__`

function isolateView(view$: Stream<VNode>, key: string) {
  const prefixedKey = KEY_PREFIX + key

  return tap(vNode => {
    const { props: { className: className = EMPTY_CLASS_NAME } } = vNode
    const needsIsolation = className.indexOf(prefixedKey) === -1

    if (needsIsolation)
      vNode.props.className = removeSuperfluousSpaces(
        join(CLASS_NAME_SEPARATOR, [className, prefixedKey])
      )
  }, view$)
}

const EMPTY_CLASS_NAME = ``
const CLASS_NAME_SEPARATOR = ` `

function removeSuperfluousSpaces(str: string): string {
  return str.replace(RE_TWO_OR_MORE_SPACES, CLASS_NAME_SEPARATOR)
}

const RE_TWO_OR_MORE_SPACES = /\s{2,}/g

export interface IsolatedComponent {
  <Sources extends DomSources, Sinks extends DomSinks>(
    component: Component<Sources, Sinks>,
    key: string,
    sources: Sources
  ): Sinks
  <Sources extends DomSources, Sinks extends DomSinks>(
    component: Component<Sources, Sinks>,
    key: string
  ): Component<Sources, Sinks>
  <Sources extends DomSources, Sinks extends DomSinks>(
    component: Component<Sources, Sinks>
  ): IsolatedComponentArity2<Sources, Sinks>
}

export interface IsolatedComponentArity2<Sources extends DomSources, Sinks extends DomSinks> {
  (key: string, sources: Sources): Sinks
  (key: string): Component<Sources, Sinks>
}

```

</details>

<hr />


#### makeDomComponent(element: Element): (sinks: DomSinks) =\> DomSources

<p>

Takes an element and returns a DOM component function.

</p>


<details>
  <summary>See an example</summary>
  
```typescript
import {
  makeDomComponent,
  DomSources,
  DomSinks,
  VNode,
  events,
  query,
  div,
  h1,
  button
} from '@motorcycle/mostly-dom'
import { run } from '@motorcycle/run'

const element = document.querySelector('#app')

if (!element) throw new Error('unable to find element')

run(Main, makeDomComponent(element))

function Main(sources: DomSources): DomSinks {
  const { dom } = sources

  const click$: Stream<Event> = events('click', query('button'))

  const amount$: Stream<number> = scan(x => x + 1, 0, click$)

  const view$: Stream<VNode> = map(view, amount$)

  return { view$ }
}

function view(amount: number) {
  return div([
    h1(`Clicked ${amount} times`),
    button(`Click me`)
  ])
}
```

</details>

<details>
  <summary>See the code</summary>

```typescript

export function makeDomComponent(element: Element): IOComponent<DomSinks, DomSources> {
  const rootVNode = elementToVNode(element)
  const wrapVNode = map(vNodeWrapper(element))
  const patch = scan(init(), rootVNode)

  return function Dom(sinks: DomSinks): DomSources {
    const { view$ } = sinks

    const elementVNode$ = patch(wrapVNode(view$))
    const element$ = hold(toElement(elementVNode$))
    const dom = createDomSource(element$)

    drain(element$)

    return { dom }
  }
}

```

</details>

<hr />