From 12ef0d8bcd1399fe5c7eea0f8971d2a53ff41a53 Mon Sep 17 00:00:00 2001 From: dominiksta Date: Mon, 24 Jul 2023 16:06:51 +0200 Subject: [PATCH] refactor: unify component registration and template creation --- .ignore | 1 + .../component/attribute-reflection.cy.ts | 7 +- .../core/cypress/component/bindings.cy.ts | 18 ++--- packages/core/cypress/component/classes.cy.ts | 2 +- packages/core/cypress/component/context.cy.ts | 16 ++--- packages/core/cypress/component/events.cy.ts | 6 +- .../core/cypress/component/generics.cy.ts | 4 +- .../core/cypress/component/lifecycle.cy.ts | 4 +- packages/core/cypress/component/props.cy.ts | 10 +-- .../core/cypress/component/reactivity.cy.ts | 7 +- .../core/cypress/component/rx/store.cy.ts | 4 +- packages/core/cypress/component/slots.cy.ts | 32 ++++----- packages/core/cypress/component/styles.cy.ts | 6 +- .../cypress/component/template-children.cy.ts | 6 +- .../component/template-references.cy.ts | 6 +- .../component/web-component-to-mvui.cy.ts | 2 +- packages/core/cypress/tsconfig.json | 1 + packages/core/reference.md | 4 +- packages/core/src/component.ts | 26 +++---- packages/core/src/define.ts | 30 -------- packages/core/src/globals.ts | 20 +++++- packages/core/src/index.ts | 1 - packages/core/src/rx/bind.ts | 2 +- packages/core/src/rx/context.ts | 10 +-- packages/core/src/rx/operators/catch-error.ts | 2 +- packages/core/tsconfig.json | 1 + packages/docs/content/_index.md | 12 ++-- .../content/docs/components/02-defining.md | 70 +++++++++++++++++++ .../components/04-props-and-attributes.md | 10 +-- .../content/docs/components/06-lifecycle.md | 17 ++--- packages/docs/content/docs/stdlib/button.md | 6 +- .../docs/content/docs/stdlib/collapsible.md | 2 +- packages/docs/content/docs/stdlib/input.md | 4 +- packages/docs/content/docs/stdlib/menu.md | 20 +++--- packages/docs/content/docs/stdlib/snackbar.md | 4 +- .../docs/static/js/app/codeview/codeview.js | 4 +- .../js/app/codeview/injected-in-iframe.js | 6 -- .../docs/static/js/app/codeview/sandbox.js | 15 +++- packages/stdlib/src/components/button.ts | 7 +- packages/stdlib/src/components/collapsible.ts | 9 ++- packages/stdlib/src/components/input.ts | 5 +- .../stdlib/src/components/menu/menu-item.ts | 25 ++++--- packages/stdlib/src/components/menu/menu.ts | 25 ++++--- packages/stdlib/src/components/snackbar.ts | 7 +- packages/stdlib/src/main.ts | 54 +++++++------- packages/stdlib/tsconfig.json | 1 + 46 files changed, 296 insertions(+), 235 deletions(-) delete mode 100644 packages/core/src/define.ts diff --git a/.ignore b/.ignore index cfacd43..aba74f7 100644 --- a/.ignore +++ b/.ignore @@ -2,3 +2,4 @@ /packages/docs/static/js/ace-builds/ /packages/docs/themes/hugo-book/ +package-lock.json diff --git a/packages/core/cypress/component/attribute-reflection.cy.ts b/packages/core/cypress/component/attribute-reflection.cy.ts index 7993f44..c51c0e4 100644 --- a/packages/core/cypress/component/attribute-reflection.cy.ts +++ b/packages/core/cypress/component/attribute-reflection.cy.ts @@ -1,11 +1,12 @@ -import { Component, rx, define, MVUI_GLOBALS } from '$thispkg'; +import { Component, rx, MVUI_GLOBALS } from '$thispkg'; import { attempt, mount, waitFrame } from '../support/helpers'; describe('attribute reflection', () => { it('basic attribute reflection', attempt(async () => { MVUI_GLOBALS.APP_DEBUG = false; - const [_, AttrReflectionTest] = define(class AttrReflectionTest extends Component { + @Component.register + class AttrReflectionTest extends Component { static useShadow = false; props = { @@ -14,7 +15,7 @@ describe('attribute reflection', () => { attrNum: new rx.Prop(5, { reflect: true, converter: Number }), } render = () => []; - }); + }; const comp = mount(AttrReflectionTest); // const [_doc, comp] = testDoc(new AttrReflectionTest()); diff --git a/packages/core/cypress/component/bindings.cy.ts b/packages/core/cypress/component/bindings.cy.ts index 6b0b44e..018fc73 100644 --- a/packages/core/cypress/component/bindings.cy.ts +++ b/packages/core/cypress/component/bindings.cy.ts @@ -1,6 +1,7 @@ import { Component, h, rx } from "$thispkg"; import { attempt, mount } from "../support/helpers"; +@Component.register class MyBoundInput extends Component { props = { value: new rx.Prop('') }; @@ -15,18 +16,17 @@ class MyBoundInput extends Component { }) ] } -MyBoundInput.register(); describe('bindings', () => { it('prop binding', attempt(async () => { + @Component.register class BindingTest extends Component { state = new rx.State('initial'); render = () => [ - MyBoundInput.new({ props: { value: rx.bind(this.state) } }), + MyBoundInput.t({ props: { value: rx.bind(this.state) } }), ] } - BindingTest.register(); const comp = mount(BindingTest); const myInput = await comp.query('app-my-bound-input'); @@ -64,6 +64,7 @@ describe('bindings', () => { })); it('field binding', attempt(async () => { + @Component.register class BindingTestField extends Component { state = new rx.State('initial'); @@ -71,7 +72,6 @@ describe('bindings', () => { h.input({ fields: { value: rx.bind(this.state) } }), ] } - BindingTestField.register(); const comp = mount(BindingTestField); const input = await comp.query('input'); @@ -97,6 +97,7 @@ describe('bindings', () => { })); it('attribute binding', attempt(async () => { + @Component.register class BindingTestAttribute extends Component { state = new rx.State('initial'); @@ -104,7 +105,6 @@ describe('bindings', () => { h.input({ attrs: { value: rx.bind(this.state) } }), ] } - BindingTestAttribute.register(); const comp = mount(BindingTestAttribute); const input = await comp.query('input'); @@ -131,6 +131,7 @@ describe('bindings', () => { it('type coercion/serialization', attempt(async () => { + @Component.register class BindingsTestSerialization extends Component { noCoerce = new rx.State('0'); coerce = new rx.State(0); @@ -151,7 +152,6 @@ describe('bindings', () => { ] } } - BindingsTestSerialization.register(); const comp = mount(BindingsTestSerialization); let inputs: HTMLInputElement[] = []; @@ -192,17 +192,17 @@ describe('bindings', () => { })); it('two bindings', async () => { + @Component.register class BindingTestTwo extends Component { // #state = new State('nothing'); state = new rx.State('initial'); render = () => [ - MyBoundInput.new({ props: { value: rx.bind(this.state) } }), - MyBoundInput.new({ props: { value: rx.bind(this.state) } }), + MyBoundInput.t({ props: { value: rx.bind(this.state) } }), + MyBoundInput.t({ props: { value: rx.bind(this.state) } }), h.input({ fields: { value: rx.bind(this.state) } }), ] } - BindingTestTwo.register(); const comp = mount(BindingTestTwo); const myInputs = await comp.queryAll('app-my-bound-input'); diff --git a/packages/core/cypress/component/classes.cy.ts b/packages/core/cypress/component/classes.cy.ts index b4e9378..5d34969 100644 --- a/packages/core/cypress/component/classes.cy.ts +++ b/packages/core/cypress/component/classes.cy.ts @@ -6,6 +6,7 @@ describe('setting css classes', () => { const klass3 = new rx.State(false); + @Component.register class ClassListTest extends Component { render() { return [ @@ -19,7 +20,6 @@ describe('setting css classes', () => { ] } } - ClassListTest.register(); const comp = mount(ClassListTest); const el = await comp.query('div'); diff --git a/packages/core/cypress/component/context.cy.ts b/packages/core/cypress/component/context.cy.ts index aed7c3b..f93497a 100644 --- a/packages/core/cypress/component/context.cy.ts +++ b/packages/core/cypress/component/context.cy.ts @@ -1,8 +1,9 @@ -import { Component, rx, h, define } from "$thispkg"; +import { Component, rx, h } from "$thispkg"; import { attempt, mount } from "../support/helpers"; const selectCtx = new rx.Context(() => new rx.State('val1')); +@Component.register class MySelect extends Component { render() { const ctx = this.provideContext(selectCtx); @@ -16,8 +17,8 @@ class MySelect extends Component { ] } } -const [ mySelect ] = define(MySelect); +@Component.register class MySelectItem extends Component { props = { value: new rx.Prop(''), @@ -31,24 +32,23 @@ class MySelectItem extends Component { ] } } -const [ mySelectItem ] = define(MySelectItem); describe('context', () => { it('kind works', attempt(async () => { + @Component.register class ContextTest extends Component { render() { return [ - mySelect([ - mySelectItem({ props: { value: 'val1' } }), - mySelectItem({ props: { value: 'val2' } }), - mySelectItem({ props: { value: 'val3' } }), + MySelect.t([ + MySelectItem.t({ props: { value: 'val1' } }), + MySelectItem.t({ props: { value: 'val2' } }), + MySelectItem.t({ props: { value: 'val3' } }), ]) ]; } } - define(ContextTest); const comp = mount(ContextTest); const currVal = await (await comp.query('app-my-select')).query( diff --git a/packages/core/cypress/component/events.cy.ts b/packages/core/cypress/component/events.cy.ts index 19ec228..dc04029 100644 --- a/packages/core/cypress/component/events.cy.ts +++ b/packages/core/cypress/component/events.cy.ts @@ -1,6 +1,7 @@ import { Component, h, rx } from "$thispkg"; import { mount } from "../support/helpers"; +@Component.register export class EventEmitter extends Component<{ events: { 'customClick': MouseEvent, @@ -39,9 +40,9 @@ export class EventEmitter extends Component<{ ]; } -EventEmitter.register(); +@Component.register export class EventReceiver extends Component { state = new rx.State(new CustomEvent('')); @@ -50,7 +51,7 @@ export class EventReceiver extends Component { h.fieldset([ h.legend('Event Receiver'), h.p({ attrs: { id: 'state' } }, this.state), - EventEmitter.new({ + EventEmitter.t({ events: { // we put the click event in here additionally to test the types customClick: (e) => this.state.next(e), @@ -63,7 +64,6 @@ export class EventReceiver extends Component { }; } -EventReceiver.register(); describe('custom events', () => { it('kinda works', async () => { diff --git a/packages/core/cypress/component/generics.cy.ts b/packages/core/cypress/component/generics.cy.ts index 63c566f..47bbfff 100644 --- a/packages/core/cypress/component/generics.cy.ts +++ b/packages/core/cypress/component/generics.cy.ts @@ -20,11 +20,11 @@ describe('generic components', () => { const state1 = new rx.State(0); const state2 = new rx.State('hi'); return [ - (GenericComp).new({ + (GenericComp).t({ props: { value: state1 }, events: { change: e => state1.next(e) } }), - (GenericComp).new({ + (GenericComp).t({ props: { value: state2 }, events: { change: e => state2.next(e) } }) diff --git a/packages/core/cypress/component/lifecycle.cy.ts b/packages/core/cypress/component/lifecycle.cy.ts index a62ea80..4227751 100644 --- a/packages/core/cypress/component/lifecycle.cy.ts +++ b/packages/core/cypress/component/lifecycle.cy.ts @@ -1,8 +1,9 @@ -import { Component, h, rx, define } from "$thispkg"; +import { Component, h, rx } from "$thispkg"; import { attempt } from "../support/helpers"; const lifecycle = new rx.State('initial'); +@Component.register class LifecycleTestComponent extends Component { stickyCounter = new rx.State(100); @@ -28,7 +29,6 @@ class LifecycleTestComponent extends Component { ]; } } -define(LifecycleTestComponent); describe('lifecycle', () => { it('general lifecycle', attempt(async () => { diff --git a/packages/core/cypress/component/props.cy.ts b/packages/core/cypress/component/props.cy.ts index e7fbe0b..c554132 100644 --- a/packages/core/cypress/component/props.cy.ts +++ b/packages/core/cypress/component/props.cy.ts @@ -1,6 +1,7 @@ import { Component, rx, h } from "$thispkg"; import { attempt, mount } from "../support/helpers"; +@Component.register class DumbComponent extends Component { props = { value: new rx.Prop('', { reflect: true }) }; @@ -12,8 +13,8 @@ class DumbComponent extends Component { ]) ] } -DumbComponent.register(); +@Component.register class SmartComponent extends Component { private state = new rx.State('reactive value'); @@ -24,15 +25,14 @@ class SmartComponent extends Component { h.button({ events: { click: () => this.state.next('second reactive value') }}, 'Change reactive value'), - DumbComponent.new({ props: { value: 'test' }}), - DumbComponent.new({ props: { value: this.state }}), - DumbComponent.new( + DumbComponent.t({ props: { value: 'test' }}), + DumbComponent.t({ props: { value: this.state }}), + DumbComponent.t( { attrs: { value: this.state.map(v => v + ' from attribute') }} ), ]) ] } -SmartComponent.register(); describe('props', (() => { it('basic props', attempt(async () => { diff --git a/packages/core/cypress/component/reactivity.cy.ts b/packages/core/cypress/component/reactivity.cy.ts index 162f325..acc6a6c 100644 --- a/packages/core/cypress/component/reactivity.cy.ts +++ b/packages/core/cypress/component/reactivity.cy.ts @@ -2,6 +2,7 @@ import { Component, rx, h } from "$thispkg"; import { attempt, mount } from "../support/helpers"; describe('reactive templates', () => { + @Component.register class CounterComponent extends Component { private count = new rx.State(0); private multiplier = new rx.State(1); @@ -30,7 +31,6 @@ describe('reactive templates', () => { ]) ]; } - CounterComponent.register(); it('basic reactivity (counter)', attempt(async () => { const comp = mount(CounterComponent); @@ -49,7 +49,7 @@ describe('reactive templates', () => { btnIncCount.click(); expect(state.innerText).to.be.eq('4 * 3 = 12'); })); - + @Component.register class ReactiveList extends Component { private list = new rx.State(['item 1', 'item 2']); private counter = new rx.State(0); @@ -86,7 +86,6 @@ describe('reactive templates', () => { ]) ]; } - ReactiveList.register(); it('reactive list', attempt(async () => { const comp = mount(ReactiveList); @@ -114,6 +113,7 @@ describe('reactive templates', () => { check(3, 5); })); + @Component.register class EditableList extends Component { private editableList = new rx.State([ { name: 'name1', value: 'val1' }, @@ -165,7 +165,6 @@ describe('reactive templates', () => { ), ] } - EditableList.register(); it('editable list', attempt(async () => { diff --git a/packages/core/cypress/component/rx/store.cy.ts b/packages/core/cypress/component/rx/store.cy.ts index 6e6779d..d7b53f9 100644 --- a/packages/core/cypress/component/rx/store.cy.ts +++ b/packages/core/cypress/component/rx/store.cy.ts @@ -202,6 +202,7 @@ describe('Stores', () => { it('attaching to component lifecycle', async () => { + @Component.register class StoreComponent extends Component { render() { // console.log('render'); @@ -243,7 +244,6 @@ describe('Stores', () => { ] } } - StoreComponent.register(); const comp = mount(StoreComponent); const addEffect = await comp.query('#add-effect'); @@ -264,6 +264,7 @@ describe('Stores', () => { it('partial bindings in component', async () => { let bindVal = ''; + @Component.register class PartialBindingsStoreComponent extends Component { render() { // console.log('render'); @@ -284,7 +285,6 @@ describe('Stores', () => { ] } } - PartialBindingsStoreComponent.register(); const comp = mount(PartialBindingsStoreComponent); const input = await comp.query('input'); diff --git a/packages/core/cypress/component/slots.cy.ts b/packages/core/cypress/component/slots.cy.ts index e83608c..215dd5c 100644 --- a/packages/core/cypress/component/slots.cy.ts +++ b/packages/core/cypress/component/slots.cy.ts @@ -1,6 +1,7 @@ -import { Component, h, define } from "$thispkg"; +import { Component, h } from "$thispkg"; import { attempt, mount } from "../support/helpers"; +@Component.register class MyLayout extends Component<{ slots: { 'after-footer': any, @@ -16,13 +17,13 @@ class MyLayout extends Component<{ h.slot({ attrs: { id: 'only-divs', name: "only-divs" } }), ] } -const [myLayout] = define(MyLayout); +@Component.register export class SlotsTest extends Component { render = () => [ h.fieldset([ h.legend('Slots'), - myLayout({ + MyLayout.t({ slots: { 'after-footer': 'After Footer', 'only-divs': h.div() } }, [ h.div('Content Children'), @@ -31,7 +32,6 @@ export class SlotsTest extends Component { ]) ] } -SlotsTest.register(); describe('Slots', function() { it('kinda work', attempt(async () => { @@ -51,14 +51,14 @@ describe('Slots', function() { })) it('multiple children in default slot', attempt(async () => { + @Component.register class TestSlotDefaultMultipleMixed extends Component { render() { return [ - myLayout([h.div('yes'), 'very much']), + MyLayout.t([h.div('yes'), 'very much']), ] } } - TestSlotDefaultMultipleMixed.register(); const comp = mount(TestSlotDefaultMultipleMixed); const layout = await comp.query('app-my-layout'); @@ -71,14 +71,14 @@ describe('Slots', function() { })) it('multiple string children in default slot', attempt(async () => { + @Component.register class TestSlotDefaultMultipleString extends Component { render() { return [ - myLayout(['yes', 'very much']), + MyLayout.t(['yes', 'very much']), ] } } - TestSlotDefaultMultipleString.register(); const comp = mount(TestSlotDefaultMultipleString); const layout = await comp.query('app-my-layout'); @@ -91,14 +91,14 @@ describe('Slots', function() { })) it('multiple element children in default slot', attempt(async () => { + @Component.register class TestSlotDefaultMultipleElement extends Component { render() { return [ - myLayout([h.div('yes'), h.span('very much')]), + MyLayout.t([h.div('yes'), h.span('very much')]), ] } } - TestSlotDefaultMultipleElement.register(); const comp = mount(TestSlotDefaultMultipleElement); const layout = await comp.query('app-my-layout'); @@ -111,16 +111,16 @@ describe('Slots', function() { })) it('multiple children in named slot', attempt(async () => { + @Component.register class TestSlotNamedMultipleMixed extends Component { render() { return [ - myLayout({ + MyLayout.t({ slots: { 'after-footer': [h.div('yes'), 'very much'] } }), ] } } - TestSlotNamedMultipleMixed.register(); const comp = mount(TestSlotNamedMultipleMixed); const layout = await comp.query('app-my-layout'); @@ -133,16 +133,16 @@ describe('Slots', function() { })) it('multiple string only children in named slot', attempt(async () => { + @Component.register class TestSlotNamedMultipleString extends Component { render() { return [ - myLayout({ + MyLayout.t({ slots: { 'after-footer': ['yes', 'very much'] } }), ] } } - TestSlotNamedMultipleString.register(); const comp = mount(TestSlotNamedMultipleString); const layout = await comp.query('app-my-layout'); @@ -155,16 +155,16 @@ describe('Slots', function() { })) it('multiple element only children in named slot', attempt(async () => { + @Component.register class TestSlotNamedMultipleElement extends Component { render() { return [ - myLayout({ + MyLayout.t({ slots: { 'after-footer': [h.div('yes'), h.span('very much')] } }), ] } } - TestSlotNamedMultipleElement.register(); const comp = mount(TestSlotNamedMultipleElement); const layout = await comp.query('app-my-layout'); diff --git a/packages/core/cypress/component/styles.cy.ts b/packages/core/cypress/component/styles.cy.ts index c103d04..febe78c 100644 --- a/packages/core/cypress/component/styles.cy.ts +++ b/packages/core/cypress/component/styles.cy.ts @@ -8,6 +8,7 @@ const SOME_SHARED_STYLES = style.sheet({ } }); +@Component.register class StyledComponent extends Component { static styles = [ @@ -45,7 +46,6 @@ class StyledComponent extends Component { ]) ] } -StyledComponent.register(); describe('styling', () => { it('basic styling', attempt(async () => { @@ -65,10 +65,11 @@ describe('styling', () => { })); it('style overrides', async () => { + @Component.register class StyleOverridingComponent extends Component { render() { return [ - StyledComponent.new({ + StyledComponent.t({ styleOverrides: style.sheet({ 'button': { // HACK this should ideally work without !important @@ -79,7 +80,6 @@ describe('styling', () => { ] } } - StyleOverridingComponent.register(); const comp = mount(StyleOverridingComponent); diff --git a/packages/core/cypress/component/template-children.cy.ts b/packages/core/cypress/component/template-children.cy.ts index 100be8b..ca20923 100644 --- a/packages/core/cypress/component/template-children.cy.ts +++ b/packages/core/cypress/component/template-children.cy.ts @@ -5,6 +5,7 @@ describe('template children', () => { it('HTM(Div)LElement', attempt(() => { + @Component.register class TemplateChildrenHTMLElementTest extends Component { render() { const div1 = document.createElement('div'); @@ -20,7 +21,6 @@ describe('template children', () => { ] } } - TemplateChildrenHTMLElementTest.register(); const comp = mount(TemplateChildrenHTMLElementTest); @@ -32,13 +32,14 @@ describe('template children', () => { it('custom', attempt(async () => { + @Component.register class TemplateChildrenCustomTestChild extends Component { render() { return [ h.slot() ] } } - TemplateChildrenCustomTestChild.register(); + @Component.register class TemplateChildrenCustomTest extends Component { render() { return [ @@ -65,7 +66,6 @@ describe('template children', () => { ] } } - TemplateChildrenCustomTest.register(); const comp = mount(TemplateChildrenCustomTest); diff --git a/packages/core/cypress/component/template-references.cy.ts b/packages/core/cypress/component/template-references.cy.ts index d9ab579..2f155a4 100644 --- a/packages/core/cypress/component/template-references.cy.ts +++ b/packages/core/cypress/component/template-references.cy.ts @@ -2,6 +2,7 @@ import { Component, h } from "$thispkg"; import { sleep } from "$thispkg/util/time"; import { attempt, mount, waitFrame } from "../support/helpers"; +@Component.register class TemplateReferencesTest1 extends Component { private paragraphEl = this.query('.myClass'); @@ -29,8 +30,8 @@ class TemplateReferencesTest1 extends Component { ]; } } -TemplateReferencesTest1.register(); +@Component.register class TemplateReferencesTest2 extends Component { render = () => [ @@ -50,7 +51,6 @@ class TemplateReferencesTest2 extends Component { ]) ] } -TemplateReferencesTest2.register(); describe('Template References', function() { it('queries', attempt(async () => { @@ -71,6 +71,7 @@ describe('Template References', function() { })); it('ref field in template element creator', attempt(async () => { + @Component.register class TemplateReferencesTestRef extends Component { render() { @@ -94,7 +95,6 @@ describe('Template References', function() { ] } } - TemplateReferencesTestRef.register(); const comp = mount(TemplateReferencesTestRef); await waitFrame(); diff --git a/packages/core/cypress/component/web-component-to-mvui.cy.ts b/packages/core/cypress/component/web-component-to-mvui.cy.ts index a8a9d72..907f885 100644 --- a/packages/core/cypress/component/web-component-to-mvui.cy.ts +++ b/packages/core/cypress/component/web-component-to-mvui.cy.ts @@ -52,6 +52,7 @@ describe('web component to mvui', () => { let state = 'initial'; + @Component.register class ComponentUsingWrapper extends Component { render = () => [ h.fieldset([ @@ -65,7 +66,6 @@ describe('web component to mvui', () => { ]), ] } - ComponentUsingWrapper.register(); const comp = mount(ComponentUsingWrapper); const wrapped = await comp.query('#wrapped-comp'); diff --git a/packages/core/cypress/tsconfig.json b/packages/core/cypress/tsconfig.json index 6b26245..6a05882 100644 --- a/packages/core/cypress/tsconfig.json +++ b/packages/core/cypress/tsconfig.json @@ -5,6 +5,7 @@ "module": "esnext", "target": "es2015", "moduleResolution": "node", + "experimentalDecorators": true, "lib": ["esnext", "dom"], "types": ["cypress", "node"], "baseUrl": ".", diff --git a/packages/core/reference.md b/packages/core/reference.md index 34c777a..7fa0c08 100644 --- a/packages/core/reference.md +++ b/packages/core/reference.md @@ -102,11 +102,11 @@ class UserComp extends Component { const state1 = new rx.State(0); const state2 = new rx.State('hi'); return [ - (GenericComp).new({ + (GenericComp).t({ props: { value: state1 }, events: { change: e => state1.next(e.detail) } }), - (GenericComp).new({ + (GenericComp).t({ props: { value: state2 }, events: { change: e => state2.next(e.detail) } }) diff --git a/packages/core/src/component.ts b/packages/core/src/component.ts index 386f228..346b8a1 100644 --- a/packages/core/src/component.ts +++ b/packages/core/src/component.ts @@ -221,25 +221,28 @@ export default abstract class Component< this.lifecycleState = "created"; } - static register() { - if (!this.tagNameSuffix) - this.tagNameSuffix = camelToDash(this.name).substring(1); + static register(constructor?: Function) { + const cls = constructor ? (constructor as typeof Component) : this; + console.log(this, constructor); + + if (!cls.tagNameSuffix) + cls.tagNameSuffix = camelToDash(cls.name).substring(1); // fix bundled names starting with `_` - if (this.tagNameSuffix.startsWith("-")) - this.tagNameSuffix = this.tagNameSuffix.substring(1); + if (cls.tagNameSuffix.startsWith("-")) + cls.tagNameSuffix = cls.tagNameSuffix.substring(1); let prefix; - if (this.tagNameLibrary) { - prefix = MVUI_GLOBALS.PREFIXES.get(this.tagNameLibrary) ?? - this.tagNameLibrary; + if (cls.tagNameLibrary) { + prefix = MVUI_GLOBALS.PREFIXES.get(cls.tagNameLibrary) ?? + cls.tagNameLibrary; } else { prefix = MVUI_GLOBALS.PREFIXES.get('default'); } customElements.define( - `${prefix}-${this.tagNameSuffix}`, - this as any + `${prefix}-${cls.tagNameSuffix}`, + cls as any ); } @@ -343,9 +346,8 @@ export default abstract class Component< */ [GENERIC_TYPE_HIDE]?: ParamSpec; - // TODO: maybe remove now that `define` is a thing? /** Get a new {@link TemplateElement} for use in a {@link render} method. */ - static new< + static t< T extends Component, E extends _ParamSpec = T extends Component ? I : never >( diff --git a/packages/core/src/define.ts b/packages/core/src/define.ts deleted file mode 100644 index c682349..0000000 --- a/packages/core/src/define.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as rx from "./rx"; -import Component, { ComponentTemplateElement } from "./component"; -import { TemplateElement, TemplateElementChildren } from "./template-element"; - -/** - * TODO - */ -export default function define< - ClassT extends { new(): Component }, ->( - cls: ClassT -): [ ComponentTemplateElementCreator>, ClassT ] { - (cls as any).register(); - - const templateElementCreator: ComponentTemplateElementCreator> = ( - childrenOrParams, children, - ) => new TemplateElement>( - () => new (cls as any)(), - childrenOrParams, children - ) as ComponentTemplateElement>; - - return [templateElementCreator, cls]; -} - - -type ComponentTemplateElementCreator = ( - childrenOrParams?: TemplateElementChildren | - ComponentTemplateElement['params'], - children?: TemplateElementChildren, -) => ComponentTemplateElement diff --git a/packages/core/src/globals.ts b/packages/core/src/globals.ts index babcd30..4660a74 100644 --- a/packages/core/src/globals.ts +++ b/packages/core/src/globals.ts @@ -1,3 +1,6 @@ +// typedef +// ---------------------------------------------------------------------- + export type MvuiConfig = { /** Wether the app should run in debug mode. Currently the only effect of setting this to @@ -19,6 +22,9 @@ export type MvuiConfig = { PREFIXES: Map, } +// defaults +// ---------------------------------------------------------------------- + const globals: MvuiConfig = { APP_DEBUG: false, STYLE_SHEET_NONCE: 'mvui-component', @@ -27,7 +33,19 @@ const globals: MvuiConfig = { globals.PREFIXES.set('default', 'app'); -(window as any).__MVUI_GLOBALS = globals; +// set up +// ---------------------------------------------------------------------- + +function maybeSetGlobal() { + const w = (window as any); + if (!('__MVUI_GLOBALS' in w)) w.__MVUI_GLOBALS = globals; +} + +maybeSetGlobal(); + +// export +// ---------------------------------------------------------------------- + /** A global configuration object. You can change its properties by importing or alternatively using the `__MVUI_GLOBALS` window scoped object. diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 20fbdf5..c0d5945 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,6 +1,5 @@ export { default as Component, ComponentTemplateElement } from './component'; export { MVUI_GLOBALS, MvuiConfig } from './globals'; -export { default as define } from './define'; export * as rx from './rx'; export { default as h } from './html'; export { http, RequestOptions } from "./http"; diff --git a/packages/core/src/rx/bind.ts b/packages/core/src/rx/bind.ts index 092c333..38028c2 100644 --- a/packages/core/src/rx/bind.ts +++ b/packages/core/src/rx/bind.ts @@ -23,7 +23,7 @@ export type Binding = { marker: typeof BIND_MARKER, value: State }; #state = new rx.State('initial'); render = () => [ - MyBoundInput.new({ props: { value: rx.bind(this.#state) }}), + MyBoundInput.t({ props: { value: rx.bind(this.#state) }}), h.input({ fields: { value: rx.bind(this.#state) }}), h.div(this.#state), h.button({ events: { click: _ => this.#state.next('intial') }}, 'reset') diff --git a/packages/core/src/rx/context.ts b/packages/core/src/rx/context.ts index 261981a..1f305a1 100644 --- a/packages/core/src/rx/context.ts +++ b/packages/core/src/rx/context.ts @@ -14,6 +14,7 @@ import type Component from "../component"; ```typescript const myCtx = new rx.Context(() => new rx.State(0)); + @Component.register class CtxProvider extends Component { render() { const ctx = this.provideContext(myCtx); @@ -23,8 +24,8 @@ import type Component from "../component"; ] } } - const [ctxprovider] = define(CtxProvider); + @Component.register class CtxConsumer extends Component { props = { value: new rx.Prop('') } @@ -35,14 +36,13 @@ import type Component from "../component"; ] } } - const [ctxconsumer] = define(CtxConsumer); class Main extends Component { render() { return [ - ctxprovider([ - ctxconsumer({ props: { value: 'val1' }), - ctxconsumer({ props: { value: 'val2' }), + CtxProvider.t([ + CtxConsumer.t({ props: { value: 'val1' }), + CtxConsumer.t({ props: { value: 'val2' }), ]) ] } diff --git a/packages/core/src/rx/operators/catch-error.ts b/packages/core/src/rx/operators/catch-error.ts index c12d433..b7291b0 100644 --- a/packages/core/src/rx/operators/catch-error.ts +++ b/packages/core/src/rx/operators/catch-error.ts @@ -37,7 +37,7 @@ export default function catchError( 'Please use the retry operator instead. \n\n' + 'Example: \n\n' + "Don't: `.pipe(rx.catchError((_, caught) => caught))` \n" + - "Do: `.pipe(rx.retriy())` \n" + "Do: `.pipe(rx.retry())` \n" ) } else { unsubSelected = selected.subscribe(observer); diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 63e332d..b0f4205 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -4,6 +4,7 @@ "sourceMap": true, "strict": true, "moduleResolution": "node", + "experimentalDecorators": true, "composite": true, // "noEmit": true, "lib": [ diff --git a/packages/docs/content/_index.md b/packages/docs/content/_index.md index 97734db..141f1f8 100644 --- a/packages/docs/content/_index.md +++ b/packages/docs/content/_index.md @@ -13,16 +13,16 @@ bookToC: false ```typescript import { Component, rx, h } from "@mvui/core"; +@Component.register export default class CounterComponent extends Component { render() { const count = new rx.State(0); - // this will get properly torn down on unmount - this.onRemoved(rx.interval(1000).subscribe(_ => { - count.next(v => v + 1); - })); return [ - // synchronous memoized state derivation - h.span(count.derive(v => `You are looking at Mvui for ${v}s`)), + h.button( + { events: { click: () => count.next(v => v + 1) } }, + 'Click Me' + ), + h.span(count.derive(v => ` Count: ${v}`)), ]; } } diff --git a/packages/docs/content/docs/components/02-defining.md b/packages/docs/content/docs/components/02-defining.md index 688b99c..eb58934 100644 --- a/packages/docs/content/docs/components/02-defining.md +++ b/packages/docs/content/docs/components/02-defining.md @@ -5,3 +5,73 @@ weight: 02 --- # Defining + +All web components, including mvui components, must be registered to the "custom elements +registry". This assigns them a fixed, unique tag name (like ``). + +You *could* go the default `customElements.define('my-component', MyComponent)` way, but +mvui provides some convenience features which will make life easier. + +```typescript +import { Component, h } from "@mvui/core"; + +@Component.register +export default class MyComponent extends Component { + render() { + return [ h.div('Hello World!') ]; + } +} + +// If you want to not use decorators (for example in vanilla JS), this call is +// equivalent to the `@Component.register` decorator: +MyComponent.register(); +``` + +But wait, where did we specify the tag name? In this case, we didn't, and mvui will assume +that what you wanted was `''`. You can customize the tag name by +specifying both a *prefix* and a *suffix*: + +```typescript +import { Component, h } from "@mvui/core"; + +@Component.register +export default class MyComponent extends Component { + static tagNameSuffix = 'fancy-component-name'; + static tagNameLibrary = 'myapp'; + + render() { + return [ h.div('Hello World!') ]; + } +} +``` + +This will register the component as '``'. + +## Advanced: Avoiding naming conflicts + +You may have noticed that the property we used to specify the prefix was not called +`tagNamePrefix` but `tagNameLibrary`. The reason is that you can change the tag name +prefixes of other mvui libraries to avoid naming conflicts. + +Let's pretend you have are writing library called `toaster`, which you use to control your +actual toaster. But you want to use an mvui component library also called `toaster`, that +displays toast notifications on screen. You can avoid conflicts in your tag names like so: + +```typescript +// in some global startup file: +import { MVUI_GLOBALS } from "@mvui/core"; + +// This code has to run *before you load the 'toaster'* library and your own +// components! +// It also assumes that the author of the 'toaster' library has set +// `static tagNameLibrary = 'toaster'` for all their components and that you +// have set `static tagNameLibrary = 'toaster-control'` for all your components. + +MVUI_GLOBALS.PREFIXES.set('toaster', 'toaster-notify'); +MVUI_GLOBALS.PREFIXES.set('toaster-control', 'toaster'); +``` + +Other web component frameworks typically do not allow you to do this and will just assign +a static tag name on import. In that case, you just have to live with that tag name and +name your components differently. There are technically some really nasty hacks to edit +the customElements registry, but for now there is no reliable way of doing so. diff --git a/packages/docs/content/docs/components/04-props-and-attributes.md b/packages/docs/content/docs/components/04-props-and-attributes.md index f293ab5..502c8e6 100644 --- a/packages/docs/content/docs/components/04-props-and-attributes.md +++ b/packages/docs/content/docs/components/04-props-and-attributes.md @@ -24,8 +24,9 @@ your component to be used outside of Mvui. {{}} ```typescript -import { Component, rx, h, define } from "@mvui/core"; +import { Component, rx, h } from "@mvui/core"; +@Component.register class MyButton extends Component { props = { kind: new rx.Prop<'primary' | 'default'>( @@ -45,13 +46,12 @@ class MyButton extends Component { } } -const [ myButton ] = define(MyButton); - +@Component.register export default class Main extends Component { render() { return [ - myButton(), - myButton({props: { kind: 'primary' }}), + MyButton.t(), + MyButton.t({props: { kind: 'primary' }}), h.span(' <-- inspect element to see the attribute'), ] } diff --git a/packages/docs/content/docs/components/06-lifecycle.md b/packages/docs/content/docs/components/06-lifecycle.md index 65a891e..b15e264 100644 --- a/packages/docs/content/docs/components/06-lifecycle.md +++ b/packages/docs/content/docs/components/06-lifecycle.md @@ -26,10 +26,11 @@ because you are expected to define them in the `render()` method. {{}} ```typescript -import { Component, rx, h, define } from '@mvui/core'; +import { Component, rx, h } from '@mvui/core'; const lifecycle = new rx.State(['initial']); +@Component.register class LifecycleTest extends Component { render() { lifecycle.next(v => [ ...v, 'added' ]); @@ -45,8 +46,7 @@ class LifecycleTest extends Component { } } -const [ lifecycleTest ] = define(LifecycleTest); - +@Component.register export default class Wrapper extends Component { render() { const displayChild = new rx.State(false); @@ -57,7 +57,7 @@ export default class Wrapper extends Component { }, 'Toggle Child Mount'), h.div( displayChild.ifelse({ - if: lifecycleTest(), + if: LifecycleTest.t(), else: undefined, }) ) @@ -76,8 +76,9 @@ for your sanity as a non-expert in reactive programming). {{}} ```typescript -import { Component, rx, h, define } from '@mvui/core'; +import { Component, rx, h } from '@mvui/core'; +@Component.register export default class Wrapper extends Component { render() { // imagine this being a websocket connection or something similar @@ -103,8 +104,9 @@ to be reset on unmount, you can define it as a class field. {{}} ```typescript -import { Component, rx, h, define } from '@mvui/core'; +import { Component, rx, h } from '@mvui/core'; +@Component.register class LifecycleTest extends Component { stickyState = new rx.State(100); @@ -130,8 +132,7 @@ class LifecycleTest extends Component { } } -LifecycleTest.register(); - +@Component.register export default class Wrapper extends Component { render() { const displayChild = new rx.State(false); diff --git a/packages/docs/content/docs/stdlib/button.md b/packages/docs/content/docs/stdlib/button.md index 915c760..4b2987a 100644 --- a/packages/docs/content/docs/stdlib/button.md +++ b/packages/docs/content/docs/stdlib/button.md @@ -14,9 +14,9 @@ import * as std from "@mvui/stdlib"; export default class Example extends Component { render() { return [ - std.button('Default'), - std.button({ props: { kind: 'primary' }}, 'Primary'), - std.button({ props: { kind: 'accent' }}, 'Accent'), + std.Button.t('Default'), + std.Button.t({ props: { kind: 'primary' }}, 'Primary'), + std.Button.t({ props: { kind: 'accent' }}, 'Accent'), ]; } } diff --git a/packages/docs/content/docs/stdlib/collapsible.md b/packages/docs/content/docs/stdlib/collapsible.md index 72e5484..0ac30b8 100644 --- a/packages/docs/content/docs/stdlib/collapsible.md +++ b/packages/docs/content/docs/stdlib/collapsible.md @@ -13,7 +13,7 @@ import * as std from "@mvui/stdlib"; export default class Example extends Component { render() { return [ - std.collapsible({ + std.Collapsible.t({ slots: { header: h.span('Header') }, }, 'Content'), ]; diff --git a/packages/docs/content/docs/stdlib/input.md b/packages/docs/content/docs/stdlib/input.md index c4e91a1..6254ac3 100644 --- a/packages/docs/content/docs/stdlib/input.md +++ b/packages/docs/content/docs/stdlib/input.md @@ -14,8 +14,8 @@ export default class Example extends Component { render() { const value = new rx.State('hi'); return [ - std.input({props: { value: rx.bind(value) }}), - std.input({props: { value: rx.bind(value) }}), + std.Input.t({props: { value: rx.bind(value) }}), + std.Input.t({props: { value: rx.bind(value) }}), h.span(value), ]; } diff --git a/packages/docs/content/docs/stdlib/menu.md b/packages/docs/content/docs/stdlib/menu.md index 6175436..7e6c860 100644 --- a/packages/docs/content/docs/stdlib/menu.md +++ b/packages/docs/content/docs/stdlib/menu.md @@ -13,18 +13,18 @@ import * as std from "@mvui/stdlib"; export default class Example extends Component { render() { return [ - std.menu([ - std.menuitem('Menu 1 Item 1'), - std.menuitem('Menu 1 Item 2'), - std.menu({ props: { text: 'Submenu 1' }}, [ - std.menuitem('Submenu 1 Item 1'), - std.menuitem('Submenu 1 Item 2'), - std.menu({ props: { text: 'SubSubmenu 1' } }, [ - std.menuitem('SubSubmenu 1 Item 1'), - std.menuitem('SubSubmenu 1 Item 2'), + std.Menu.t([ + std.MenuItem.t('Menu 1 Item 1'), + std.MenuItem.t('Menu 1 Item 2'), + std.Menu.t({ props: { text: 'Submenu 1' }}, [ + std.MenuItem.t('Submenu 1 Item 1'), + std.MenuItem.t('Submenu 1 Item 2'), + std.Menu.t({ props: { text: 'SubSubmenu 1' } }, [ + std.MenuItem.t('SubSubmenu 1 Item 1'), + std.MenuItem.t('SubSubmenu 1 Item 2'), ]), ]), - std.menuitem('Menu 1 Item 3'), + std.MenuItem.t('Menu 1 Item 3'), ]), ]; } diff --git a/packages/docs/content/docs/stdlib/snackbar.md b/packages/docs/content/docs/stdlib/snackbar.md index dd303c9..954b015 100644 --- a/packages/docs/content/docs/stdlib/snackbar.md +++ b/packages/docs/content/docs/stdlib/snackbar.md @@ -13,8 +13,8 @@ import * as std from "@mvui/stdlib"; export default class Example extends Component { render() { return [ - std.snackbar(), // can be anywhere - std.button( + std.Snackbar.t(), // can be anywhere + std.Button.t( { events: { click: _ => std.openSnackbar('hi') }}, 'Trigger Snackbar' ) diff --git a/packages/docs/static/js/app/codeview/codeview.js b/packages/docs/static/js/app/codeview/codeview.js index 0b62124..f80f139 100644 --- a/packages/docs/static/js/app/codeview/codeview.js +++ b/packages/docs/static/js/app/codeview/codeview.js @@ -1,6 +1,6 @@ import { Component, h, rx, style } from '@mvui/core'; import { theme } from '@mvui/stdlib'; -import { sandbox } from './sandbox.js'; +import { Sandbox } from './sandbox.js'; import '/js/ace-builds/src-noconflict/ace.js'; @@ -85,7 +85,7 @@ export default class Codeview extends Component { h.div({ attrs: { id: 'editor' }}), ]), h.div({ attrs: { id: 'sep' }}), - sandbox({ + Sandbox.t({ props: { code: editorChange, height: this.props.outputHeight.derive(h => h === '' ? '80px' : h), diff --git a/packages/docs/static/js/app/codeview/injected-in-iframe.js b/packages/docs/static/js/app/codeview/injected-in-iframe.js index 4144154..62d6860 100644 --- a/packages/docs/static/js/app/codeview/injected-in-iframe.js +++ b/packages/docs/static/js/app/codeview/injected-in-iframe.js @@ -31,12 +31,6 @@ async function displayComponent() { try { const module = await evalEsm(code); - if (module.default.prototype instanceof Component) { - module.default.register(); - } else { - customElements.define('playground-component', module.default); - } - document.body.innerHTML = ''; setDefaultStyles(); document.body.appendChild(new module.default()); diff --git a/packages/docs/static/js/app/codeview/sandbox.js b/packages/docs/static/js/app/codeview/sandbox.js index 14a4e68..3ecafb1 100644 --- a/packages/docs/static/js/app/codeview/sandbox.js +++ b/packages/docs/static/js/app/codeview/sandbox.js @@ -1,4 +1,4 @@ -import { Component, h, rx, define, style } from '@mvui/core'; +import { Component, h, rx, style } from '@mvui/core'; // import 'https://unpkg.com/@babel/standalone/babel.min.js'; // TODO import Babel from 'https://esm.sh/@babel/standalone@7.21.4/babel.min.js'; @@ -69,7 +69,16 @@ export class Sandbox extends Component { try { code = Babel.transform( - code, { filename: 'hi.ts', presets: ["typescript"] } + code, { + filename: 'hi.ts', + presets: ["typescript"], + plugins: [ + [ + Babel.availablePlugins["proposal-decorators"], + { version: "2023-01" }, + ], + ], + } ).code; } catch (e) { win.displayError(e); @@ -123,4 +132,4 @@ export class Sandbox extends Component { } -export const [ sandbox ] = define(Sandbox); +Sandbox.register(); diff --git a/packages/stdlib/src/components/button.ts b/packages/stdlib/src/components/button.ts index 07e477e..c729529 100644 --- a/packages/stdlib/src/components/button.ts +++ b/packages/stdlib/src/components/button.ts @@ -1,4 +1,4 @@ -import { Component, define, h, rx, style } from "@mvui/core"; +import { Component, h, rx, style } from "@mvui/core"; import { theme } from "theme"; /** @@ -10,7 +10,7 @@ import { theme } from "theme"; class Example extends Component { render = () => [ - std.button('Click Me!'), + std.Button.t('Click Me!'), ] } ``` @@ -28,6 +28,7 @@ import { theme } from "theme"; @slot {any} default */ +@Component.register export class Button extends Component<{ events: { click: MouseEvent @@ -100,5 +101,3 @@ export class Button extends Component<{ ] } } - -export const [button] = define(Button); diff --git a/packages/stdlib/src/components/collapsible.ts b/packages/stdlib/src/components/collapsible.ts index e2176a0..0ff7e32 100644 --- a/packages/stdlib/src/components/collapsible.ts +++ b/packages/stdlib/src/components/collapsible.ts @@ -1,5 +1,5 @@ -import { Component, define, h, rx, style } from "@mvui/core"; -import { button } from "./button"; +import { Component, h, rx, style } from "@mvui/core"; +import { Button } from "./button"; import { theme } from '../theme'; /** @@ -22,6 +22,7 @@ import { theme } from '../theme'; This will be shown as the clickable header that will toggle wether the content is collapsed. */ +@Component.register export class Collapsible extends Component { static tagNameLibrary = 'std'; @@ -43,7 +44,7 @@ export class Collapsible extends Component { h.div( { attrs: { id: 'wrapper' } }, [ - button({ + Button.t({ attrs: { id: 'header' }, events: { click: _ => collapsed.next(!collapsed.value) }, }, [ @@ -114,5 +115,3 @@ export class Collapsible extends Component { } }); } - -export const [ collapsible ] = define(Collapsible); diff --git a/packages/stdlib/src/components/input.ts b/packages/stdlib/src/components/input.ts index a472262..33637b8 100644 --- a/packages/stdlib/src/components/input.ts +++ b/packages/stdlib/src/components/input.ts @@ -1,4 +1,4 @@ -import { Component, rx, h, style, define } from "@mvui/core"; +import { Component, rx, h, style } from "@mvui/core"; import { theme } from "theme"; /** @@ -13,6 +13,7 @@ import { theme } from "theme"; * * TODO: maybe make onlyEmitOnBlur an option for rx.bind ? */ +@Component.register export class Input extends Component { static tagNameLibrary = 'std'; @@ -57,5 +58,3 @@ export class Input extends Component { }}) ] } - -export const [ input ] = define(Input); diff --git a/packages/stdlib/src/components/menu/menu-item.ts b/packages/stdlib/src/components/menu/menu-item.ts index 0a737b2..901eab7 100644 --- a/packages/stdlib/src/components/menu/menu-item.ts +++ b/packages/stdlib/src/components/menu/menu-item.ts @@ -1,4 +1,4 @@ -import { Component, define, h, style } from "@mvui/core"; +import { Component, h, style } from "@mvui/core"; import { menuContext, STYLE_MENU_ITEM } from './menu'; /** @@ -10,18 +10,18 @@ import { menuContext, STYLE_MENU_ITEM } from './menu'; // ... render() { return [ - std.menu([ - std.menuitem('Menu 1 Item 1'), - std.menuitem('Menu 1 Item 2'), - std.menu({ props: { text: 'Submenu 1' }}, [ - std.menuitem('Submenu 1 Item 1'), - std.menu({ props: { text: 'SubSubmenu 1' } }, [ - std.menuitem('SubSubmenu 1 Item 1'), - std.menuitem('SubSubmenu 1 Item 2'), + std.Menu.t([ + std.MenuItem.t('Menu 1 Item 1'), + std.MenuItem.t('Menu 1 Item 2'), + std.Menu.t({ props: { text: 'Submenu 1' }}, [ + std.MenuItem.t('Submenu 1 Item 1'), + std.Menu.t({ props: { text: 'SubSubmenu 1' } }, [ + std.MenuItem.t('SubSubmenu 1 Item 1'), + std.MenuItem.t('SubSubmenu 1 Item 2'), ]), - std.menuitem('Submenu 1 Item 2'), + std.MenuItem.t('Submenu 1 Item 2'), ]), - std.menuitem('Menu 1 Item 3'), + std.MenuItem.t('Menu 1 Item 3'), ]), ] } @@ -33,6 +33,7 @@ import { menuContext, STYLE_MENU_ITEM } from './menu'; @slot {any} default */ +@Component.register export class MenuItem extends Component<{ slots: { right: HTMLDivElement @@ -68,5 +69,3 @@ export class MenuItem extends Component<{ } }); }; - -export const [ menuitem ] = define(MenuItem); diff --git a/packages/stdlib/src/components/menu/menu.ts b/packages/stdlib/src/components/menu/menu.ts index 158d18b..031a5f0 100644 --- a/packages/stdlib/src/components/menu/menu.ts +++ b/packages/stdlib/src/components/menu/menu.ts @@ -1,4 +1,4 @@ -import { Component, define, h, rx, style } from "@mvui/core"; +import { Component, h, rx, style } from "@mvui/core"; import { theme } from "theme"; export const menuContext = new rx.Context(() => new rx.State<{ @@ -51,18 +51,18 @@ export const STYLE_MENU_ITEM = { // ... render() { return [ - std.menu([ - std.menuitem('Menu 1 Item 1'), - std.menuitem('Menu 1 Item 2'), - std.menu({ props: { text: 'Submenu 1' }}, [ - std.menuitem('Submenu 1 Item 1'), - std.menu({ props: { text: 'SubSubmenu 1' } }, [ - std.menuitem('SubSubmenu 1 Item 1'), - std.menuitem('SubSubmenu 1 Item 2'), + std.Menu.t([ + std.MenuItem.t('Menu 1 Item 1'), + std.MenuItem.t('Menu 1 Item 2'), + std.Menu.t({ props: { text: 'Submenu 1' }}, [ + std.MenuItem.t('Submenu 1 Item 1'), + std.Menu.t({ props: { text: 'SubSubmenu 1' } }, [ + std.MenuItem.t('SubSubmenu 1 Item 1'), + std.MenuItem.t('SubSubmenu 1 Item 2'), ]), - std.menuitem('Submenu 1 Item 2'), + std.MenuItem.t('Submenu 1 Item 2'), ]), - std.menuitem('Menu 1 Item 3'), + std.MenuItem.t('Menu 1 Item 3'), ]), ] } @@ -77,6 +77,7 @@ export const STYLE_MENU_ITEM = { @slot {MenuItem} default */ +@Component.register export class Menu extends Component { static tagNameLibrary = 'std'; @@ -162,5 +163,3 @@ export class Menu extends Component { }); }; - -export const [menu] = define(Menu); diff --git a/packages/stdlib/src/components/snackbar.ts b/packages/stdlib/src/components/snackbar.ts index 39bb740..58ee592 100644 --- a/packages/stdlib/src/components/snackbar.ts +++ b/packages/stdlib/src/components/snackbar.ts @@ -1,4 +1,4 @@ -import { Component, define, h, rx, style } from "@mvui/core"; +import { Component, h, rx, style } from "@mvui/core"; import { TemplateElementChild } from "@mvui/core"; import { theme } from "theme"; @@ -28,7 +28,7 @@ export function openSnackbar( class Main extends Component { render = () => [ // put this anywhere, position really does not matter - std.snackbar(), + std.Snackbar.t(), ] } @@ -39,6 +39,7 @@ export function openSnackbar( @class Snackbar */ +@Component.register export class Snackbar extends Component { render() { @@ -81,5 +82,3 @@ export class Snackbar extends Component { }, }); }; - -export const [snackbar] = define(Snackbar); diff --git a/packages/stdlib/src/main.ts b/packages/stdlib/src/main.ts index 074207f..3cbdaff 100644 --- a/packages/stdlib/src/main.ts +++ b/packages/stdlib/src/main.ts @@ -1,4 +1,4 @@ -import { Component, define, h, MVUI_GLOBALS, rx, style } from "@mvui/core"; +import { Component, h, MVUI_GLOBALS, rx, style } from "@mvui/core"; import { theme, darkTheme, lightTheme, MVUI_STDLIB_THEME_NAME } from "theme"; import * as std from "./index"; @@ -10,6 +10,7 @@ style.currentTheme$.subscribe(theme => { MVUI_GLOBALS.APP_DEBUG = false; +@Component.register class Main extends Component { #state = new rx.State('initial'); @@ -30,44 +31,44 @@ class Main extends Component { render = () => [ h.h1('Test Page'), - std.snackbar(), - std.collapsible([ + std.Snackbar.t(), + std.Collapsible.t([ h.span({attrs: { slot: 'header' }}, 'Buttons'), - std.button('Default'), - std.button({ props: { kind: 'primary' } }, 'Primary'), - std.button({ props: { kind: 'accent' } }, 'Accent'), + std.Button.t('Default'), + std.Button.t({ props: { kind: 'primary' } }, 'Primary'), + std.Button.t({ props: { kind: 'accent' } }, 'Accent'), ]), - std.collapsible([ + std.Collapsible.t([ h.span({ attrs: { slot: 'header' } }, 'Menus'), - std.menu({ props: { text: 'Menu 1 (should not display)' }}, [ - std.menuitem({ slots: { right: [h.div('')] }}, 'Menu 1 Item 1'), - std.menuitem('Menu 1 Item 2'), - std.menu({ props: { text: 'Submenu 1' }}, [ - std.menuitem('Submenu 1 Item 1'), - std.menu({ props: { text: 'SubSubmenu 1' } }, [ - std.menuitem('SubSubmenu 1 Item 1'), - std.menuitem('SubSubmenu 1 Item 2'), + std.Menu.t({ props: { text: 'Menu 1 (should not display)' }}, [ + std.MenuItem.t({ slots: { right: [h.div('')] }}, 'Menu 1 Item 1'), + std.MenuItem.t('Menu 1 Item 2'), + std.Menu.t({ props: { text: 'Submenu 1' }}, [ + std.MenuItem.t('Submenu 1 Item 1'), + std.Menu.t({ props: { text: 'SubSubmenu 1' } }, [ + std.MenuItem.t('SubSubmenu 1 Item 1'), + std.MenuItem.t('SubSubmenu 1 Item 2'), ]), - std.menuitem('Submenu 1 Item 2'), + std.MenuItem.t('Submenu 1 Item 2'), ]), - std.menuitem('Menu 1 Item 3'), + std.MenuItem.t('Menu 1 Item 3'), ]), h.br(), ]), - std.collapsible([ + std.Collapsible.t([ h.span({ attrs: { slot: 'header' } }, 'Snackbar'), - std.button( + std.Button.t( { events: { click: _ => std.openSnackbar('Text 1') } }, 'Trigger with Text 1' ), - std.button( + std.Button.t( { events: { click: _ => std.openSnackbar('Text 2') } }, 'Trigger with Text 2' ), - std.button( + std.Button.t( { events: { - click: _ => std.openSnackbar(std.collapsible([ + click: _ => std.openSnackbar(std.Collapsible.t([ h.span({ attrs: { slot: 'header' } }, 'header'), h.div('content') ]), 5000) @@ -76,7 +77,7 @@ class Main extends Component { 'Trigger with Custom Elements' ), ]), - std.collapsible([ + std.Collapsible.t([ h.span({ attrs: { slot: 'header' } }, 'Bindings Test'), h.p([ 'Cras placerat accumsan nulla. Nullam tempus.', @@ -84,8 +85,8 @@ class Main extends Component { 'Nam vestibulum accumsan nisl. Nullam tristique diam non turpis. ', 'Nullam tristique diam non turpis. ', ]), - std.input({ props: { value: rx.bind(this.#state) } }), - std.input({ + std.Input.t({ props: { value: rx.bind(this.#state) } }), + std.Input.t({ styleOverrides: style.sheet({ 'input': { fontFamily: 'monospace', @@ -95,10 +96,9 @@ class Main extends Component { value: rx.bind(this.#state), } }), - std.button({ events: { click: _ => this.#state.next('intial') } }, 'reset'), + std.Button.t({ events: { click: _ => this.#state.next('intial') } }, 'reset'), ]), ] } -Main.register(); document.body.appendChild(new Main()); diff --git a/packages/stdlib/tsconfig.json b/packages/stdlib/tsconfig.json index f218742..3eccf05 100644 --- a/packages/stdlib/tsconfig.json +++ b/packages/stdlib/tsconfig.json @@ -4,6 +4,7 @@ "sourceMap": true, "strict": true, "moduleResolution": "node", + "experimentalDecorators": true, "lib": [ "esnext", "dom"