This commit is contained in:
Keith Cirkel 2020-02-24 16:26:48 +00:00
Родитель 08c65de090
Коммит 244bbf7f68
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E0736F11348DDD3A
9 изменённых файлов: 54 добавлений и 87 удалений

14
core/package.json Normal file
Просмотреть файл

@ -0,0 +1,14 @@
{
"name": "@catalyst/core",
"version": "1.0.0",
"description": "Helpers for creating HTML Elements as Controllers",
"license": "MIT",
"author": "Keith Cirkel (https://keithcirkel.co.uk/)",
"contributors": [
"Kristján Oddsson <koddsson@gmail.com>"
],
"main": "dist/index.esm.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
}
}

Просмотреть файл

@ -1,31 +1,27 @@
import {dasherize} from './dasherize'
import {wrap} from './wrap'
import {getMethods} from './getmethods'
/**
/*
* Bind `[data-action]` elements from the DOM to their actions.
*
*/
export function bindEvents(classObject: any) {
wrap(classObject.prototype, 'connectedCallback', function (this: HTMLElement) {
for(const el of this.querySelectorAll(`[data-action*=":${this.tagName.toLowerCase()}#"]`)) {
// Ignore nested elements
if (el.closest(this.tagName) !== this) continue
export function bind(controller: HTMLElement) {
const tag = controller.tagName.toLowerCase()
for(const el of controller.querySelectorAll(`[data-action*=":${tag}#"]`)) {
// Ignore nested elements
if (el.closest(tag) !== controller) continue
// Match the pattern of `eventName:constructor#method`.
for(const binding of (el.getAttribute('data-action')||'').split(' ')) {
const [rest, method] = binding.split('#')
const [eventName, handler] = rest.split(':')
if (handler !== this.tagName.toLowerCase()) continue
if (handler !== tag) continue
// Check the `method` is present on the prototype
const methodDescriptor = Object.getOwnPropertyDescriptor(classObject.prototype, method)
const methodDescriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(controller), method)
if (methodDescriptor && typeof methodDescriptor.value == 'function') {
el.addEventListener(eventName, (event: Event) => {
if (event.target === el) (this as any)[method](event)
if (event.target === el) (controller as any)[method](event)
})
}
}
}
})
}
}

Просмотреть файл

@ -1 +0,0 @@
export const dasherize = (str: string) => str.replace(/([a-zA-Z])(?=[A-Z])/g, '$1-').toLowerCase()

22
core/src/findtarget.ts Normal file
Просмотреть файл

@ -0,0 +1,22 @@
/**
* Create a property on the controller instance referencing the element with a
* `data-target` element of the same name.
*/
const createSelector = (receiver: Element, key: string) => `[data-target*="${receiver.tagName.toLowerCase()}.${key}"]`
export function findTarget(controller: HTMLElement, name: string) {
const tag = controller.tagName.toLowerCase()
for (const el of controller.querySelectorAll(`[data-target*="${tag}.${name}"]`)) {
if (el.closest(tag) === controller) return el
}
}
export function findTargets(controller: HTMLElement, name: string) {
const tag = controller.tagName.toLowerCase()
const targets = []
for (const el of controller.querySelectorAll(`[data-target*="${tag}.${name}"]`)) {
if (el.closest(tag) === controller) targets.push(el)
}
return targets
}

Просмотреть файл

@ -1,3 +0,0 @@
export {bindEvents} from './bind'
export {register} from './register'
export {target, targets, assertTargets} from './target'

Просмотреть файл

@ -1,15 +1,5 @@
import {bindEvents, register, target, assertTargets} from './helpers'
import {bind} from './bind'
import {register} from './register'
import {target, targets} from './target'
@register
@assertTargets
@bindEvents
class HelloController extends HTMLElement {
@target outputTarget!: HTMLElement;
@target nameTarget!: HTMLInputElement;
@target buttonTarget!: HTMLButtonElement;
greet() {
this.dataset.foo = 'foo'
this.outputTarget.textContent = `Hello, ${this.nameTarget.value}!`;
}
}
export {bind, register, target, targets}

Просмотреть файл

@ -1,4 +1,3 @@
import {dasherize} from './dasherize'
/**
* Register the controller as a custom element.
*
@ -6,9 +5,10 @@ import {dasherize} from './dasherize'
*
* Example: HelloController => hello-controller
*/
export function register(classObject: any) {
const name = dasherize(classObject.name)
export function register(classObject: Function) {
const name = classObject.name.replace(/([a-zA-Z])(?=[A-Z])/g, '$1-').toLowerCase()
if (!window.customElements.get(name)) {
// @ts-ignore
window[classObject.name] = classObject;
window.customElements.define(name, classObject);
}

Просмотреть файл

@ -1,40 +0,0 @@
import {dasherize} from './dasherize'
/**
* Create a property on the controller instance referencing the element with a
* `data-target` element of the same name.
*/
const createSelector = (receiver: Element, key: string) => `[data-target*="${receiver.tagName.toLowerCase()}.${key}"]`
export function target(proto: object, key: string) {
Object.defineProperty(
proto,
key,
{
configurable: true,
get: function() {
return this.querySelectorAll(createSelector(this, key)).find((el: Element) => el.closest(this.tagName) === this)
},
}
);
}
export function targets(proto: object, key: string) {
Object.defineProperty(
proto,
key,
{
configurable: true,
get: function() {
return this.querySelectorAll(createSelector(this, key)).filter((el: Element) => el.closest(this.tagName) === this)
},
}
);
}
export function assertTargets(_: object) {
// This is an empty stub that does nothing at runtime, but can be used by
// compilers to generate code that asserts Targets are the Element types they
// declare themselves to be, otherwise throw an Invariant Error.
}

Просмотреть файл

@ -1,11 +0,0 @@
export const wrap = (obj: any, name: string, fn: (...args: any[]) => any) => {
if (!obj[name]) {
obj[name] = fn
} else {
const oldFn = obj[name]
obj[name] = function () {
oldFn.call(this)
fn.call(this)
}
}
}