"livereload": "livereload ./public/dist/",
"permissions": "chown -R $USER:$USER bootstrap/cache storage public/uploads",
"lint": "eslint \"resources/**/*.js\" \"resources/**/*.mjs\"",
- "fix": "eslint --fix \"resources/**/*.js\" \"resources/**/*.mjs\""
+ "fix": "eslint --fix \"resources/**/*.js\" \"resources/**/*.mjs\"",
+ "ts:lint": "tsc --noEmit"
},
"devDependencies": {
"@lezer/generator": "^1.5.1",
import * as events from './services/events';
import * as httpInstance from './services/http';
import Translations from './services/translations';
-
-import * as components from './services/components';
import * as componentMap from './components';
+import {ComponentStore} from './services/components.ts';
// Url retrieval function
window.baseUrl = function baseUrl(path) {
window.trans_plural = translator.parsePlural.bind(translator);
// Load & initialise components
-components.register(componentMap);
-window.$components = components;
-components.init();
+window.$components = new ComponentStore();
+window.$components.register(componentMap);
+window.$components.init();
--- /dev/null
+declare module '*.svg' {
+ const content: string;
+ export default content;
+}
\ No newline at end of file
-declare module '*.svg' {
- const content: string;
- export default content;
-}
+import {ComponentStore} from "./services/components";
declare global {
interface Window {
- $components: {
- first: (string) => Object,
- }
+ $components: ComponentStore,
}
}
\ No newline at end of file
+++ /dev/null
-import {kebabToCamel, camelToKebab} from './text';
-
-/**
- * A mapping of active components keyed by name, with values being arrays of component
- * instances since there can be multiple components of the same type.
- * @type {Object<String, Component[]>}
- */
-const components = {};
-
-/**
- * A mapping of component class models, keyed by name.
- * @type {Object<String, Constructor<Component>>}
- */
-const componentModelMap = {};
-
-/**
- * A mapping of active component maps, keyed by the element components are assigned to.
- * @type {WeakMap<Element, Object<String, Component>>}
- */
-const elementComponentMap = new WeakMap();
-
-/**
- * Parse out the element references within the given element
- * for the given component name.
- * @param {String} name
- * @param {Element} element
- */
-function parseRefs(name, element) {
- const refs = {};
- const manyRefs = {};
-
- const prefix = `${name}@`;
- const selector = `[refs*="${prefix}"]`;
- const refElems = [...element.querySelectorAll(selector)];
- if (element.matches(selector)) {
- refElems.push(element);
- }
-
- for (const el of refElems) {
- const refNames = el.getAttribute('refs')
- .split(' ')
- .filter(str => str.startsWith(prefix))
- .map(str => str.replace(prefix, ''))
- .map(kebabToCamel);
- for (const ref of refNames) {
- refs[ref] = el;
- if (typeof manyRefs[ref] === 'undefined') {
- manyRefs[ref] = [];
- }
- manyRefs[ref].push(el);
- }
- }
- return {refs, manyRefs};
-}
-
-/**
- * Parse out the element component options.
- * @param {String} componentName
- * @param {Element} element
- * @return {Object<String, String>}
- */
-function parseOpts(componentName, element) {
- const opts = {};
- const prefix = `option:${componentName}:`;
- for (const {name, value} of element.attributes) {
- if (name.startsWith(prefix)) {
- const optName = name.replace(prefix, '');
- opts[kebabToCamel(optName)] = value || '';
- }
- }
- return opts;
-}
-
-/**
- * Initialize a component instance on the given dom element.
- * @param {String} name
- * @param {Element} element
- */
-function initComponent(name, element) {
- /** @type {Function<Component>|undefined} * */
- const ComponentModel = componentModelMap[name];
- if (ComponentModel === undefined) return;
-
- // Create our component instance
- /** @type {Component} * */
- let instance;
- try {
- instance = new ComponentModel();
- instance.$name = name;
- instance.$el = element;
- const allRefs = parseRefs(name, element);
- instance.$refs = allRefs.refs;
- instance.$manyRefs = allRefs.manyRefs;
- instance.$opts = parseOpts(name, element);
- instance.setup();
- } catch (e) {
- console.error('Failed to create component', e, name, element);
- }
-
- // Add to global listing
- if (typeof components[name] === 'undefined') {
- components[name] = [];
- }
- components[name].push(instance);
-
- // Add to element mapping
- const elComponents = elementComponentMap.get(element) || {};
- elComponents[name] = instance;
- elementComponentMap.set(element, elComponents);
-}
-
-/**
- * Initialize all components found within the given element.
- * @param {Element|Document} parentElement
- */
-export function init(parentElement = document) {
- const componentElems = parentElement.querySelectorAll('[component],[components]');
-
- for (const el of componentElems) {
- const componentNames = `${el.getAttribute('component') || ''} ${(el.getAttribute('components'))}`.toLowerCase().split(' ').filter(Boolean);
- for (const name of componentNames) {
- initComponent(name, el);
- }
- }
-}
-
-/**
- * Register the given component mapping into the component system.
- * @param {Object<String, ObjectConstructor<Component>>} mapping
- */
-export function register(mapping) {
- const keys = Object.keys(mapping);
- for (const key of keys) {
- componentModelMap[camelToKebab(key)] = mapping[key];
- }
-}
-
-/**
- * Get the first component of the given name.
- * @param {String} name
- * @returns {Component|null}
- */
-export function first(name) {
- return (components[name] || [null])[0];
-}
-
-/**
- * Get all the components of the given name.
- * @param {String} name
- * @returns {Component[]}
- */
-export function get(name) {
- return components[name] || [];
-}
-
-/**
- * Get the first component, of the given name, that's assigned to the given element.
- * @param {Element} element
- * @param {String} name
- * @returns {Component|null}
- */
-export function firstOnElement(element, name) {
- const elComponents = elementComponentMap.get(element) || {};
- return elComponents[name] || null;
-}
--- /dev/null
+import {kebabToCamel, camelToKebab} from './text';
+import {Component} from "../components/component";
+
+/**
+ * Parse out the element references within the given element
+ * for the given component name.
+ */
+function parseRefs(name: string, element: HTMLElement):
+ {refs: Record<string, HTMLElement>, manyRefs: Record<string, HTMLElement[]>} {
+ const refs: Record<string, HTMLElement> = {};
+ const manyRefs: Record<string, HTMLElement[]> = {};
+
+ const prefix = `${name}@`;
+ const selector = `[refs*="${prefix}"]`;
+ const refElems = [...element.querySelectorAll(selector)];
+ if (element.matches(selector)) {
+ refElems.push(element);
+ }
+
+ for (const el of refElems as HTMLElement[]) {
+ const refNames = (el.getAttribute('refs') || '')
+ .split(' ')
+ .filter(str => str.startsWith(prefix))
+ .map(str => str.replace(prefix, ''))
+ .map(kebabToCamel);
+ for (const ref of refNames) {
+ refs[ref] = el;
+ if (typeof manyRefs[ref] === 'undefined') {
+ manyRefs[ref] = [];
+ }
+ manyRefs[ref].push(el);
+ }
+ }
+ return {refs, manyRefs};
+}
+
+/**
+ * Parse out the element component options.
+ */
+function parseOpts(componentName: string, element: HTMLElement): Record<string, string> {
+ const opts: Record<string, string> = {};
+ const prefix = `option:${componentName}:`;
+ for (const {name, value} of element.attributes) {
+ if (name.startsWith(prefix)) {
+ const optName = name.replace(prefix, '');
+ opts[kebabToCamel(optName)] = value || '';
+ }
+ }
+ return opts;
+}
+
+export class ComponentStore {
+ /**
+ * A mapping of active components keyed by name, with values being arrays of component
+ * instances since there can be multiple components of the same type.
+ */
+ protected components: Record<string, Component[]> = {};
+
+ /**
+ * A mapping of component class models, keyed by name.
+ */
+ protected componentModelMap: Record<string, typeof Component> = {};
+
+ /**
+ * A mapping of active component maps, keyed by the element components are assigned to.
+ */
+ protected elementComponentMap: WeakMap<HTMLElement, Record<string, Component>> = new WeakMap();
+
+ /**
+ * Initialize a component instance on the given dom element.
+ */
+ protected initComponent(name: string, element: HTMLElement): void {
+ const ComponentModel = this.componentModelMap[name];
+ if (ComponentModel === undefined) return;
+
+ // Create our component instance
+ let instance: Component|null = null;
+ try {
+ instance = new ComponentModel();
+ instance.$name = name;
+ instance.$el = element;
+ const allRefs = parseRefs(name, element);
+ instance.$refs = allRefs.refs;
+ instance.$manyRefs = allRefs.manyRefs;
+ instance.$opts = parseOpts(name, element);
+ instance.setup();
+ } catch (e) {
+ console.error('Failed to create component', e, name, element);
+ }
+
+ if (!instance) {
+ return;
+ }
+
+ // Add to global listing
+ if (typeof this.components[name] === 'undefined') {
+ this.components[name] = [];
+ }
+ this.components[name].push(instance);
+
+ // Add to element mapping
+ const elComponents = this.elementComponentMap.get(element) || {};
+ elComponents[name] = instance;
+ this.elementComponentMap.set(element, elComponents);
+ }
+
+ /**
+ * Initialize all components found within the given element.
+ */
+ public init(parentElement: Document|HTMLElement = document) {
+ const componentElems = parentElement.querySelectorAll('[component],[components]');
+
+ for (const el of componentElems) {
+ const componentNames = `${el.getAttribute('component') || ''} ${(el.getAttribute('components'))}`.toLowerCase().split(' ').filter(Boolean);
+ for (const name of componentNames) {
+ this.initComponent(name, el as HTMLElement);
+ }
+ }
+ }
+
+ /**
+ * Register the given component mapping into the component system.
+ * @param {Object<String, ObjectConstructor<Component>>} mapping
+ */
+ public register(mapping: Record<string, typeof Component>) {
+ const keys = Object.keys(mapping);
+ for (const key of keys) {
+ this.componentModelMap[camelToKebab(key)] = mapping[key];
+ }
+ }
+
+ /**
+ * Get the first component of the given name.
+ */
+ public first(name: string): Component|null {
+ return (this.components[name] || [null])[0];
+ }
+
+ /**
+ * Get all the components of the given name.
+ */
+ public get(name: string): Component[] {
+ return this.components[name] || [];
+ }
+
+ /**
+ * Get the first component, of the given name, that's assigned to the given element.
+ */
+ public firstOnElement(element: HTMLElement, name: string): Component|null {
+ const elComponents = this.elementComponentMap.get(element) || {};
+ return elComponents[name] || null;
+ }
+}
/**
* Convert a kebab-case string to camelCase
- * @param {String} kebab
- * @returns {string}
*/
-export function kebabToCamel(kebab) {
- const ucFirst = word => word.slice(0, 1).toUpperCase() + word.slice(1);
+export function kebabToCamel(kebab: string): string {
+ const ucFirst = (word: string) => word.slice(0, 1).toUpperCase() + word.slice(1);
const words = kebab.split('-');
return words[0] + words.slice(1).map(ucFirst).join('');
}
/**
* Convert a camelCase string to a kebab-case string.
- * @param {String} camelStr
- * @returns {String}
*/
-export function camelToKebab(camelStr) {
+export function camelToKebab(camelStr: string): string {
return camelStr.replace(/[A-Z]/g, (str, offset) => (offset > 0 ? '-' : '') + str.toLowerCase());
}
import type {EditorConfig} from "lexical/LexicalEditor";
import {el} from "../helpers";
import {EditorDecoratorAdapter} from "../ui/framework/decorator";
+import {CodeEditor} from "../../components";
export type SerializedCodeBlockNode = Spread<{
language: string;
const language = node.getLanguage();
// @ts-ignore
- const codeEditor = window.$components.first('code-editor');
+ const codeEditor = window.$components.first('code-editor') as CodeEditor;
// TODO - Handle direction
codeEditor.open(code, language, 'ltr', (newCode: string, newLang: string) => {
editor.update(() => {
{
+ "include": ["resources/js/**/*"],
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
- // "rootDir": "./", /* Specify the root folder within your source files. */
+ "rootDir": "./resources/js/", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
"paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */