From 472418abb392260d4c8eec15bb6f0bd64b286056 Mon Sep 17 00:00:00 2001 From: Zecyel Date: Thu, 21 Sep 2023 21:31:03 +0800 Subject: [PATCH 01/14] =?UTF-8?q?Upd:=20=E7=94=A8=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E9=87=8D=E6=96=B0=E5=AE=9E=E7=8E=B0AInput?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/base.ts | 17 +++++++++++++++++ src/component/input.ts | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 src/component/base.ts create mode 100644 src/component/input.ts diff --git a/src/component/base.ts b/src/component/base.ts new file mode 100644 index 0000000..22c9243 --- /dev/null +++ b/src/component/base.ts @@ -0,0 +1,17 @@ +abstract class ComponentModel { + abstract update(...args: any[]): any[] +} + +abstract class ComponentBase, MsgType> { + el: HTMLDivElement + + constructor(public value: Model) {} + + public patch(...args: any[]): any[] { + return this.value.update(...args) + } + + public abstract mount(): void + + protected abstract update(msg: MsgType): void +} diff --git a/src/component/input.ts b/src/component/input.ts new file mode 100644 index 0000000..9d7d42d --- /dev/null +++ b/src/component/input.ts @@ -0,0 +1,35 @@ +class AInputModel { + val: string + place_holder: string + + onchange: () => void = () => {} +} + +class AInput extends ComponentBase { + + label_el: HTMLLabelElement + input_el: HTMLInputElement + + constructor(val: Partial) { + super(val) + } + + mount(): void { + this.el = document.createElement('div') + this.el.classList.add('mdui-textfield') + this.el.classList.add('mdui-textfield-floating-label') + + this.label_el = document.createElement('label') + this.label_el.classList.add('mdui-textfield-label') + + + this.input_el = document.createElement('input') + this.input_el.classList.add('mdui-textfield-input') + + // this.input_el.addEventListener('change', ) + } + + update(key: string, val: any) { + + } +} \ No newline at end of file From 63418f2a29efbc8ee5c46a735252ec522d82456a Mon Sep 17 00:00:00 2001 From: Zecyel Date: Fri, 22 Sep 2023 10:22:40 +0800 Subject: [PATCH 02/14] =?UTF-8?q?Fix:=20=E8=B0=83=E8=AF=95=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E4=BA=86AInput?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 +- src/component/base.ts | 21 ++++++++++---- src/component/input.ts | 65 ++++++++++++++++++++++++++++++++++-------- src/index.ts | 7 ++--- tsconfig.json | 3 +- 5 files changed, 75 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index 72a1fb9..63a2d33 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "_KermanX" ], "scripts": { - "build": "tsc" + "build": "tsc", + "publish": "npm publish --access=public" }, "dependencies": { "mdui": "^1.0.2" diff --git a/src/component/base.ts b/src/component/base.ts index 22c9243..5a86f02 100644 --- a/src/component/base.ts +++ b/src/component/base.ts @@ -1,17 +1,28 @@ abstract class ComponentModel { - abstract update(...args: any[]): any[] + abstract update(...args: any[]): MsgType[] } abstract class ComponentBase, MsgType> { el: HTMLDivElement - constructor(public value: Model) {} + constructor(public model: Model) { + this.el = document.createElement('div') + } - public patch(...args: any[]): any[] { - return this.value.update(...args) + public patch(...args: any[]): void { + let upd = this.model.update(...args) + for (let i of upd) { + this.update(i) + } } - public abstract mount(): void + public abstract mount(el: HTMLDivElement): void protected abstract update(msg: MsgType): void } + + +export { + ComponentModel, + ComponentBase +} \ No newline at end of file diff --git a/src/component/input.ts b/src/component/input.ts index 9d7d42d..eade884 100644 --- a/src/component/input.ts +++ b/src/component/input.ts @@ -1,35 +1,76 @@ -class AInputModel { - val: string - place_holder: string +import mdui from 'mdui' +import { ComponentBase, ComponentModel } from './base' +type AInputMsgType = 'val' | 'place_holder' + +class AInputModel extends ComponentModel { + val: string = '' + place_holder: string = '' onchange: () => void = () => {} + + constructor (p: any) { + super() + this.update(p) + } + + update(p: any): AInputMsgType[] { + let ret = [] as AInputMsgType[] + if (p.val !== undefined) { + this.val = p.val + ret.push('val') + } + + if (p.place_holder !== undefined) { + this.place_holder = p.place_holder + ret.push('place_holder') + } + + return ret + } } -class AInput extends ComponentBase { +class AInput extends ComponentBase { label_el: HTMLLabelElement input_el: HTMLInputElement - constructor(val: Partial) { - super(val) + constructor(model: AInputModel) { + super(model) } - mount(): void { - this.el = document.createElement('div') + mount(el: HTMLDivElement): void { this.el.classList.add('mdui-textfield') this.el.classList.add('mdui-textfield-floating-label') this.label_el = document.createElement('label') this.label_el.classList.add('mdui-textfield-label') - + this.update('place_holder') + this.el.appendChild(this.label_el) this.input_el = document.createElement('input') this.input_el.classList.add('mdui-textfield-input') + this.update('val') + this.el.appendChild(this.input_el) - // this.input_el.addEventListener('change', ) + this.input_el.addEventListener('input', (_: Event) => { + this.model.val = this.input_el.value + console.log(this.input_el.value) + }) + el.appendChild(this.el) } - update(key: string, val: any) { - + update(msg: AInputMsgType): void { + if (msg === 'val') { + this.input_el.value = this.model.val + mdui.updateTextFields(this.input_el) + } + if (msg === 'place_holder') + this.label_el.innerText = this.model.place_holder + mdui.mutation() } +} + +export { + AInputModel, + AInput } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 6e9c7d2..711dfd9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,2 @@ -import { AutoForm } from "./autoform"; - -export { - AutoForm -} \ No newline at end of file +export * from './component/base' +export * from './component/input' diff --git a/tsconfig.json b/tsconfig.json index 0a10013..9fa4950 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,7 @@ "module": "ESNext", "lib": ["ES2020", "DOM", "DOM.Iterable"], "skipLibCheck": true, + "strictPropertyInitialization": false, // "moduleResolution": "NodeNext", "composite": true, @@ -20,7 +21,7 @@ "outDir": "dist" }, "include": [ - "src/*" + "src/**/*" ], "exclude": ["node_modules"] } From ac54b14c1ae050309fa0d8e8ef11757e3769c51e Mon Sep 17 00:00:00 2001 From: Zecyel Date: Fri, 22 Sep 2023 12:07:53 +0800 Subject: [PATCH 03/14] =?UTF-8?q?Fix:=20=E8=A1=A5=E5=85=85=E4=BA=86AInput?= =?UTF-8?q?=E7=9A=84onchange=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/input.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/component/input.ts b/src/component/input.ts index eade884..ccedcf1 100644 --- a/src/component/input.ts +++ b/src/component/input.ts @@ -25,6 +25,10 @@ class AInputModel extends ComponentModel { ret.push('place_holder') } + if (p.onchange !== undefined) { + this.onchange = p.onchange + } + return ret } } @@ -54,7 +58,7 @@ class AInput extends ComponentBase { this.input_el.addEventListener('input', (_: Event) => { this.model.val = this.input_el.value - console.log(this.input_el.value) + this.model.onchange() }) el.appendChild(this.el) } From bc704afbfdc338a01dd51f5801ab6297fbd730a6 Mon Sep 17 00:00:00 2001 From: Zecyel Date: Sun, 24 Sep 2023 22:31:48 +0800 Subject: [PATCH 04/14] =?UTF-8?q?Upd:=20=E6=96=B0=E5=A2=9E=E4=BA=86AButton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/button.ts | 63 +++++++++++++++++++++++++++++++++++++++++ src/index.ts | 1 + 2 files changed, 64 insertions(+) create mode 100644 src/component/button.ts diff --git a/src/component/button.ts b/src/component/button.ts new file mode 100644 index 0000000..050a1b5 --- /dev/null +++ b/src/component/button.ts @@ -0,0 +1,63 @@ +import mdui from "mdui"; +import { ComponentBase, ComponentModel } from ".."; + +type AButtonMsgType = 'caption' + +class AButtonModel extends ComponentModel { + caption: string = '' + onclick: () => void = () => {} + + constructor (p: any) { + super() + this.update(p) + } + + update(p: any): AButtonMsgType[] { + let ret = [] as AButtonMsgType[] + if (p.caption !== undefined) { + this.caption = p.caption + ret.push('caption') + } + + if (p.onclick !== undefined) { + this.onclick = p.onclick + } + return ret + } +} + +class AButton extends ComponentBase { + + button_el: HTMLButtonElement + + constructor(model: AButtonModel) { + super(model) + } + + mount(el: HTMLDivElement): void { + this.button_el = document.createElement('button') + this.button_el.classList.add('mdui-btn') + this.button_el.classList.add('mdui-btn-raised') + this.button_el.classList.add('mdui-ripple') + this.update('caption') + this.el.appendChild(this.button_el) + + this.button_el.addEventListener('click', (_) => { + this.model.onclick() + }) + + el.appendChild(this.el) + } + + update(msg: AButtonMsgType): void { + if (msg === 'caption') { + this.button_el.innerText = this.model.caption + } + mdui.mutation() + } +} + +export { + AButtonModel, + AButton +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 711dfd9..268b53c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,3 @@ export * from './component/base' export * from './component/input' +export * from './component/button' From 4e64a6e05d0eb446788dca5e55aecd24ee29d69a Mon Sep 17 00:00:00 2001 From: Zecyel Date: Wed, 27 Sep 2023 09:03:32 +0800 Subject: [PATCH 05/14] =?UTF-8?q?Upd:=20=E5=AE=9E=E7=8E=B0=E4=BA=86?= =?UTF-8?q?=E4=B8=8D=E9=9C=80=E6=89=8B=E5=8A=A8=E7=BC=96=E8=AF=91=E5=90=8E?= =?UTF-8?q?=E7=9A=84=E9=87=8D=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 63a2d33..2241c23 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,7 @@ "devDependencies": { "typescript": "^5.0.2" }, - "main": "./dist/src/index.js", - "types": "./dist/src/index.d.ts", + "main": "./src/index.ts", "files": [ "dist/src", "README.md", @@ -29,8 +28,7 @@ "exports": { ".": { "import": { - "types": "./dist/src/index.d.ts", - "default": "./dist/src/index.js" + "default": "./src/index.ts" } } } From f22bdc54a36eb915c1c052469584dca9e8f613f8 Mon Sep 17 00:00:00 2001 From: Zecyel Date: Wed, 27 Sep 2023 09:04:17 +0800 Subject: [PATCH 06/14] =?UTF-8?q?Upd:=20=E5=AE=9E=E7=8E=B0=E4=BA=86button?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/button.ts | 63 +++++++++++++++++++++++++++++++++++++++++ src/index.ts | 1 + 2 files changed, 64 insertions(+) create mode 100644 src/component/button.ts diff --git a/src/component/button.ts b/src/component/button.ts new file mode 100644 index 0000000..43df2ba --- /dev/null +++ b/src/component/button.ts @@ -0,0 +1,63 @@ +import mdui from "mdui" +import { ComponentBase, ComponentModel } from ".." + +type AButtonMsgType = 'caption' + +class AButtonModel extends ComponentModel { + caption: string = '' + onclick: () => void = () => {} + + constructor (p: any) { + super() + this.update(p) + } + + update(p: any): AButtonMsgType[] { + let ret = [] as AButtonMsgType[] + if (p.caption !== undefined) { + this.caption = p.caption + ret.push('caption') + } + + if (p.onclick !== undefined) { + this.onclick = p.onclick + } + return ret + } +} + +class AButton extends ComponentBase { + + button_el: HTMLButtonElement + + constructor(model: AButtonModel) { + super(model) + } + + mount(el: HTMLDivElement): void { + this.button_el = document.createElement('button') + this.button_el.classList.add('mdui-btn') + this.button_el.classList.add('mdui-btn-raised') + this.button_el.classList.add('mdui-ripple') + this.update('caption') + this.el.appendChild(this.button_el) + + this.button_el.addEventListener('click', (_) => { + this.model.onclick() + }) + + el.appendChild(this.el) + } + + update(msg: AButtonMsgType): void { + if (msg === 'caption') { + this.button_el.innerText = this.model.caption + } + mdui.mutation() + } +} + +export { + AButtonModel, + AButton +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 711dfd9..268b53c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,3 @@ export * from './component/base' export * from './component/input' +export * from './component/button' From 00fa44003ccd2215d6751cc375cc2818644fbaac Mon Sep 17 00:00:00 2001 From: Zecyel Date: Thu, 28 Sep 2023 10:41:17 +0800 Subject: [PATCH 07/14] =?UTF-8?q?Upd:=20=E5=AE=9E=E7=8E=B0=E4=BA=86Select?= =?UTF-8?q?=E5=85=83=E7=B4=A0=E7=9A=84=E5=8A=A8=E6=80=81=E5=8F=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/button.ts | 4 --- src/component/input.ts | 4 --- src/component/select.ts | 75 +++++++++++++++++++++++++++++++++++++++++ src/index.ts | 1 + 4 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 src/component/select.ts diff --git a/src/component/button.ts b/src/component/button.ts index 43df2ba..66f2cd7 100644 --- a/src/component/button.ts +++ b/src/component/button.ts @@ -30,10 +30,6 @@ class AButton extends ComponentBase { button_el: HTMLButtonElement - constructor(model: AButtonModel) { - super(model) - } - mount(el: HTMLDivElement): void { this.button_el = document.createElement('button') this.button_el.classList.add('mdui-btn') diff --git a/src/component/input.ts b/src/component/input.ts index ccedcf1..d0c0e10 100644 --- a/src/component/input.ts +++ b/src/component/input.ts @@ -38,10 +38,6 @@ class AInput extends ComponentBase { label_el: HTMLLabelElement input_el: HTMLInputElement - constructor(model: AInputModel) { - super(model) - } - mount(el: HTMLDivElement): void { this.el.classList.add('mdui-textfield') this.el.classList.add('mdui-textfield-floating-label') diff --git a/src/component/select.ts b/src/component/select.ts new file mode 100644 index 0000000..658c43a --- /dev/null +++ b/src/component/select.ts @@ -0,0 +1,75 @@ + +import mdui from "mdui" +import { ComponentBase, ComponentModel } from ".." + +type ASelectMsgType = 'options' + +class ASelectModel extends ComponentModel { + options: string[] = [] + value: string = '' + onchange: () => void = () => {} // after change + + constructor (p: any) { + super() + this.update(p) + } + + update(p: any): ASelectMsgType[] { + let ret = [] as ASelectMsgType[] + if (p.options !== undefined) { + this.options = (p.options as Array).filter(() => true) + ret.push('options') + } + + return ret + } +} + +class ASelect extends ComponentBase { + + select_el: HTMLSelectElement + option_el: HTMLOptionElement[] = [] + mdui_select_obj: Select + + mount(el: HTMLDivElement): void { + this.select_el = document.createElement('select') + // this.select_el.classList.add('mdui-select') + // this.select_el.setAttribute('mdui-select', `{position: 'bottom'}`) + // I don't know why but it works. + this.mdui_select_obj = new mdui.Select(this.select_el) + this.select_el.addEventListener('change', () => { + this.model.value = this.select_el.value + this.model.onchange() + }) + this.el.appendChild(this.select_el) + this.update('options') + // mdui.mutation() + + el.appendChild(this.el) + } + + update(msg: ASelectMsgType): void { + if (msg === 'options') { + for (let i of this.option_el) + this.select_el.removeChild(i) + this.option_el = this.model.options.map((v, indx) => { + let option_el = document.createElement('option') + option_el.value = indx.toString() + option_el.innerText = v + this.select_el.appendChild(option_el) + return option_el + }) + if (parseInt(this.model.value) < this.model.options.length) { + this.select_el.value = this.model.value + } else { + this.select_el.value = '0' + } + this.mdui_select_obj.handleUpdate() + } + } +} + +export { + ASelectModel, + ASelect +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 268b53c..0401f41 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ export * from './component/base' export * from './component/input' export * from './component/button' +export * from './component/select' From e698747f0589bdc3784aae0809c0ca31f4f92710 Mon Sep 17 00:00:00 2001 From: Zecyel Date: Thu, 28 Sep 2023 14:17:58 +0800 Subject: [PATCH 08/14] =?UTF-8?q?Upd:=20=E5=AE=9E=E7=8E=B0=E4=BA=86select?= =?UTF-8?q?=E7=9A=84onchange=E7=9B=91=E5=90=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/select.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/component/select.ts b/src/component/select.ts index 658c43a..9056159 100644 --- a/src/component/select.ts +++ b/src/component/select.ts @@ -20,7 +20,9 @@ class ASelectModel extends ComponentModel { this.options = (p.options as Array).filter(() => true) ret.push('options') } - + if (p.onchange !== undefined) { + this.onchange = p.onchange + } return ret } } @@ -29,7 +31,7 @@ class ASelect extends ComponentBase { select_el: HTMLSelectElement option_el: HTMLOptionElement[] = [] - mdui_select_obj: Select + mdui_select_obj: any mount(el: HTMLDivElement): void { this.select_el = document.createElement('select') From 09ce971f73c8fe14ff68d9017e19695cd3e1d68a Mon Sep 17 00:00:00 2001 From: Zecyel Date: Sat, 7 Oct 2023 13:49:26 +0800 Subject: [PATCH 09/14] =?UTF-8?q?Upd:=20=E5=AE=9E=E7=8E=B0=E4=BA=86Checkbo?= =?UTF-8?q?x?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/checkbox.ts | 85 +++++++++++++++++++++++++++++++++++++++ src/index.ts | 1 + 2 files changed, 86 insertions(+) create mode 100644 src/component/checkbox.ts diff --git a/src/component/checkbox.ts b/src/component/checkbox.ts new file mode 100644 index 0000000..45b155f --- /dev/null +++ b/src/component/checkbox.ts @@ -0,0 +1,85 @@ +import mdui from "mdui" +import { ComponentBase, ComponentModel } from ".." + +type ACheckboxMsgType = 'value' | 'label' + +type ACheckboxStatus = 'checked' | 'unchecked' // | 'partial' + +class ACheckboxModel extends ComponentModel { + value: ACheckboxStatus = 'unchecked' + label: string = '' + onchange: () => void = () => {} + + constructor (p: any) { + super() + this.update(p) + } + + update(p: any): ACheckboxMsgType[] { // I know its better to have a callback func + let ret = [] as ACheckboxMsgType[] + if (p.value !== undefined) { + this.value = p.value + ret.push('value') + } + if (p.label !== undefined) { + this.label = p.label + ret.push('label') + } + if (p.onchange !== undefined) { + this.onchange = p.onchange + } + return ret + } +} + +class ACheckbox extends ComponentBase { + + label_el: HTMLLabelElement + input_el: HTMLInputElement + i_el: HTMLElement + + mount(el: HTMLDivElement): void { + this.label_el = document.createElement('label') + this.label_el.classList.add('mdui-checkbox') + // this.label_el.textContent = this.model.label + + this.input_el = document.createElement('input') + this.input_el.type = 'checkbox' + this.label_el.appendChild(this.input_el) + + this.i_el = document.createElement('i') + this.i_el.classList.add('mdui-checkbox-icon') + this.label_el.appendChild(this.i_el) + + this.label_el.appendChild(new Text(this.model.label)) + this.update('value') + this.input_el.addEventListener('change', () => { + this.model.value = this.input_el.checked ? 'checked': 'unchecked' + this.update('value') + this.model.onchange() + } ) + this.el.appendChild(this.label_el) + + el.appendChild(this.el) + } + + update(msg: ACheckboxMsgType): void { + if (msg === 'label') { + // this.label_el.textContent = this.model.label + // this.label_el.appendChild(new Text(this.model.label)) + (this.label_el.lastChild as Text).textContent = this.model.label + // this.label_el.appendChild(this.input_el) + // this.label_el.appendChild(this.i_el) + return + } + if (msg === 'value') { + this.input_el.checked = this.model.value === 'checked' + } + mdui.mutation() + } +} + +export { + ACheckboxModel, + ACheckbox +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 0401f41..e97989f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,3 +2,4 @@ export * from './component/base' export * from './component/input' export * from './component/button' export * from './component/select' +export * from './component/checkbox' \ No newline at end of file From 12d2b508666778eae8ba642f7e9063fc22a047dd Mon Sep 17 00:00:00 2001 From: Zecyel Date: Sat, 7 Oct 2023 14:07:06 +0800 Subject: [PATCH 10/14] =?UTF-8?q?Upd:=20=E5=AE=9E=E7=8E=B0=E4=BA=86switch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/switch.ts | 70 +++++++++++++++++++++++++++++++++++++++++ src/index.ts | 3 +- 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/component/switch.ts diff --git a/src/component/switch.ts b/src/component/switch.ts new file mode 100644 index 0000000..300f410 --- /dev/null +++ b/src/component/switch.ts @@ -0,0 +1,70 @@ +import mdui from "mdui" +import { ComponentBase, ComponentModel } from ".." + +type ASwitchMsgType = 'value' + +type ASwitchStatus = 'off' | 'on' + +class ASwitchModel extends ComponentModel { + value: ASwitchStatus = 'off' + onchange: () => void = () => {} + + constructor (p: any) { + super() + this.update(p) + } + + update(p: any): ASwitchMsgType[] { + let ret = [] as ASwitchMsgType[] + if (p.value !== undefined) { + this.value = p.value + ret.push('value') + } + if (p.onchange !== undefined) { + this.onchange = p.onchange + } + return ret + } +} + +class ASwitch extends ComponentBase { + + label_el: HTMLLabelElement + input_el: HTMLInputElement + i_el: HTMLElement + + mount(el: HTMLDivElement): void { + this.label_el = document.createElement('label') + this.label_el.classList.add('mdui-switch') + + this.input_el = document.createElement('input') + this.input_el.type = 'checkbox' + this.label_el.appendChild(this.input_el) + + this.i_el = document.createElement('i') + this.i_el.classList.add('mdui-switch-icon') + this.label_el.appendChild(this.i_el) + + this.update('value') + this.input_el.addEventListener('change', () => { + this.model.value = this.input_el.checked ? 'on': 'off' + this.update('value') + this.model.onchange() + } ) + this.el.appendChild(this.label_el) + + el.appendChild(this.el) + } + + update(msg: ASwitchMsgType): void { + if (msg === 'value') { + this.input_el.checked = this.model.value === 'on' + } + mdui.mutation() + } +} + +export { + ASwitchModel, + ASwitch +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index e97989f..3096015 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,4 +2,5 @@ export * from './component/base' export * from './component/input' export * from './component/button' export * from './component/select' -export * from './component/checkbox' \ No newline at end of file +export * from './component/checkbox' +export * from './component/switch' \ No newline at end of file From 23099bdc8d08d4dd2005d88ce1a18c35802289f2 Mon Sep 17 00:00:00 2001 From: Zecyel Date: Sat, 7 Oct 2023 23:57:09 +0800 Subject: [PATCH 11/14] =?UTF-8?q?Upd:=20=E5=B0=86update=E6=94=B9=E6=88=90?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/base.ts | 11 +++++------ src/component/button.ts | 19 ++++++------------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/component/base.ts b/src/component/base.ts index 5a86f02..7779297 100644 --- a/src/component/base.ts +++ b/src/component/base.ts @@ -1,19 +1,18 @@ abstract class ComponentModel { - abstract update(...args: any[]): MsgType[] + abstract update(o: any, forward: (msg: MsgType) => void): void } abstract class ComponentBase, MsgType> { el: HTMLDivElement + // model: Model constructor(public model: Model) { this.el = document.createElement('div') + // this.model = new componentModelCtor() } - public patch(...args: any[]): void { - let upd = this.model.update(...args) - for (let i of upd) { - this.update(i) - } + public patch(o: any): void { + this.model.update(o, this.update.bind(this)) } public abstract mount(el: HTMLDivElement): void diff --git a/src/component/button.ts b/src/component/button.ts index 66f2cd7..593401a 100644 --- a/src/component/button.ts +++ b/src/component/button.ts @@ -7,22 +7,15 @@ class AButtonModel extends ComponentModel { caption: string = '' onclick: () => void = () => {} - constructor (p: any) { - super() - this.update(p) - } - - update(p: any): AButtonMsgType[] { - let ret = [] as AButtonMsgType[] - if (p.caption !== undefined) { - this.caption = p.caption - ret.push('caption') + update(o: any, forward: (msg: AButtonMsgType) => void): void { + if (o.caption !== undefined) { + this.caption = o.caption + forward('caption') } - if (p.onclick !== undefined) { - this.onclick = p.onclick + if (o.onclick !== undefined) { + this.onclick = o.onclick } - return ret } } From 2c519950df859cbbf374459e134ec6bbb5856d02 Mon Sep 17 00:00:00 2001 From: Zecyel Date: Sun, 8 Oct 2023 10:04:00 +0800 Subject: [PATCH 12/14] =?UTF-8?q?Upd:=20=E5=AE=9E=E7=8E=B0=E4=BA=86?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=E5=BC=8F=E6=9B=B4=E6=96=B0=E7=9A=84=E5=85=A8?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E8=A6=86=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/base.ts | 6 +++--- src/component/button.ts | 10 +++++----- src/component/checkbox.ts | 25 +++++++++---------------- src/component/input.ts | 28 +++++++++------------------- src/component/select.ts | 19 ++++++------------- src/component/switch.ts | 19 ++++++------------- 6 files changed, 38 insertions(+), 69 deletions(-) diff --git a/src/component/base.ts b/src/component/base.ts index 7779297..03d0d1f 100644 --- a/src/component/base.ts +++ b/src/component/base.ts @@ -1,5 +1,5 @@ abstract class ComponentModel { - abstract update(o: any, forward: (msg: MsgType) => void): void + abstract update(payload: any, forward: (msg: MsgType) => void): void } abstract class ComponentBase, MsgType> { @@ -11,8 +11,8 @@ abstract class ComponentBase, MsgType> { // this.model = new componentModelCtor() } - public patch(o: any): void { - this.model.update(o, this.update.bind(this)) + public patch(payload: any): void { + this.model.update(payload, this.update.bind(this)) } public abstract mount(el: HTMLDivElement): void diff --git a/src/component/button.ts b/src/component/button.ts index 593401a..b3e0a24 100644 --- a/src/component/button.ts +++ b/src/component/button.ts @@ -7,14 +7,14 @@ class AButtonModel extends ComponentModel { caption: string = '' onclick: () => void = () => {} - update(o: any, forward: (msg: AButtonMsgType) => void): void { - if (o.caption !== undefined) { - this.caption = o.caption + update(payload: any, forward: (msg: AButtonMsgType) => void): void { + if (payload.caption !== undefined) { + this.caption = payload.caption forward('caption') } - if (o.onclick !== undefined) { - this.onclick = o.onclick + if (payload.onclick !== undefined) { + this.onclick = payload.onclick } } } diff --git a/src/component/checkbox.ts b/src/component/checkbox.ts index 45b155f..1ecb089 100644 --- a/src/component/checkbox.ts +++ b/src/component/checkbox.ts @@ -10,25 +10,18 @@ class ACheckboxModel extends ComponentModel { label: string = '' onchange: () => void = () => {} - constructor (p: any) { - super() - this.update(p) - } - - update(p: any): ACheckboxMsgType[] { // I know its better to have a callback func - let ret = [] as ACheckboxMsgType[] - if (p.value !== undefined) { - this.value = p.value - ret.push('value') + update(payload: any, forward: (msg: ACheckboxMsgType) => void): void { + if (payload.value !== undefined) { + this.value = payload.value + forward('value') } - if (p.label !== undefined) { - this.label = p.label - ret.push('label') + if (payload.label !== undefined) { + this.label = payload.label + forward('label') } - if (p.onchange !== undefined) { - this.onchange = p.onchange + if (payload.onchange !== undefined) { + this.onchange = payload.onchange } - return ret } } diff --git a/src/component/input.ts b/src/component/input.ts index d0c0e10..f2189f5 100644 --- a/src/component/input.ts +++ b/src/component/input.ts @@ -8,28 +8,18 @@ class AInputModel extends ComponentModel { place_holder: string = '' onchange: () => void = () => {} - constructor (p: any) { - super() - this.update(p) - } - - update(p: any): AInputMsgType[] { - let ret = [] as AInputMsgType[] - if (p.val !== undefined) { - this.val = p.val - ret.push('val') + update(payload: any, forward: (msg: AInputMsgType) => void): void { + if (payload.val !== undefined) { + this.val = payload.val + forward('val') } - - if (p.place_holder !== undefined) { - this.place_holder = p.place_holder - ret.push('place_holder') + if (payload.place_holder !== undefined) { + this.place_holder = payload.place_holder + forward('place_holder') } - - if (p.onchange !== undefined) { - this.onchange = p.onchange + if (payload.onchange !== undefined) { + this.onchange = payload.onchange } - - return ret } } diff --git a/src/component/select.ts b/src/component/select.ts index 9056159..522493e 100644 --- a/src/component/select.ts +++ b/src/component/select.ts @@ -9,21 +9,14 @@ class ASelectModel extends ComponentModel { value: string = '' onchange: () => void = () => {} // after change - constructor (p: any) { - super() - this.update(p) - } - - update(p: any): ASelectMsgType[] { - let ret = [] as ASelectMsgType[] - if (p.options !== undefined) { - this.options = (p.options as Array).filter(() => true) - ret.push('options') + update(payload: any, forward: (msg: ASelectMsgType) => void): void { + if (payload.options !== undefined) { + this.options = (payload.options as Array).filter(() => true) + forward('options') } - if (p.onchange !== undefined) { - this.onchange = p.onchange + if (payload.onchange !== undefined) { + this.onchange = payload.onchange } - return ret } } diff --git a/src/component/switch.ts b/src/component/switch.ts index 300f410..f8ae498 100644 --- a/src/component/switch.ts +++ b/src/component/switch.ts @@ -9,21 +9,14 @@ class ASwitchModel extends ComponentModel { value: ASwitchStatus = 'off' onchange: () => void = () => {} - constructor (p: any) { - super() - this.update(p) - } - - update(p: any): ASwitchMsgType[] { - let ret = [] as ASwitchMsgType[] - if (p.value !== undefined) { - this.value = p.value - ret.push('value') + update(payload: any, forward: (msg: ASwitchMsgType) => void): void { + if (payload.value !== undefined) { + this.value = payload.value + forward('value') } - if (p.onchange !== undefined) { - this.onchange = p.onchange + if (payload.onchange !== undefined) { + this.onchange = payload.onchange } - return ret } } From af191b67efe9f54432bc4daf9b793e0480a8593c Mon Sep 17 00:00:00 2001 From: Zecyel Date: Sun, 8 Oct 2023 10:17:34 +0800 Subject: [PATCH 13/14] =?UTF-8?q?Upd:=20=E5=88=A0=E9=99=A4=E4=BA=86v0.0.1?= =?UTF-8?q?=E7=9A=84=E9=83=A8=E5=88=86=E6=97=A0=E7=94=A8=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 17 +++--- doc/api.md | 88 ------------------------------- doc/template.md | 48 ----------------- src/autoform.ts | 29 ---------- src/inject.ts | 113 --------------------------------------- src/types.ts | 137 ------------------------------------------------ src/value.ts | 36 ------------- 7 files changed, 9 insertions(+), 459 deletions(-) delete mode 100644 doc/api.md delete mode 100644 doc/template.md delete mode 100644 src/autoform.ts delete mode 100644 src/inject.ts delete mode 100644 src/types.ts delete mode 100644 src/value.ts diff --git a/README.md b/README.md index b4a8e54..bcf635c 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,17 @@ npm i @quasi-dev/autoform ## Demo ```typescript -import { AutoForm } from '@quasi-dev/autoform' -const af = new AutoForm({ - type: 'input', - hint: 'Input a string.' +import { AButton, AButtonModel } from '@quasi-dev/autoform' + +let el = document.getElementById('root') as HTMLDivElement + +let btn = new AButton(new AButtonModel()) +btn.mount(el) +btn.patch({ + caption: '123' }) -af.init(document.getElementById('root') as HTMLDivElement) ``` ## 文档 -[Api](./doc/api.md) - -[JSON模板格式](./doc/template.md) +暂缺 diff --git a/doc/api.md b/doc/api.md deleted file mode 100644 index 8428c35..0000000 --- a/doc/api.md +++ /dev/null @@ -1,88 +0,0 @@ -# Api - -目前 `@quasi-dev/autoform` 中只有 `AutoForm` 这一个对象 - -## 构造函数 - -创建 `AutoForm` 对象时需要一个 `json` 格式的模板,用于指定表单的内容及组成。 - -示例代码: - -```ts -const af = new AutoForm({ - type: 'input', - hint: 'Input a string.' -}) -``` - -完整的模板格式参阅[JSON模板格式](./doc/template.md) - -## 显示 - -`AutoForm` 对象创建后,可以通过 `.init(el)` 方法绑定到页面中的一个元素上,要求 `el` 必须是 `HTMLDivElement`。 - -示例代码: - -```ts -af.init(document.getElementById('form') as HTMLDivElement) -``` - -## 获取表单数据 - -通过调用 `.value()` 方法可以获取表单数据,返回值是 `json` 格式的数据。 - -返回值类型可由下表确定: - -| 表单结构 | 返回值类型 | 说明 | -|:---:|:---:|:---:| -| 单行输入框 | `string` | 用户输入的内容 | -| 下拉选择框 | `string` | 选项的文本 | -| 复选框 | `boolean` | 是否选中 | -| 表单 | `json` | 子表单数据 | - -例如,当 `template` 为如下内容时: - -```ts -{ - type: 'form', - child: { - input1: { - type: 'input', - }, - input2: { - type: 'input' - }, - checkbox: { - type: 'checkbox', - label: 'click me' - }, - subform: { - type: 'form', - child: { - input3: { - type: 'input' - } - sel1: { - type: 'select', - option: ['option1', 'option2', 'option3'] - } - } - } -} -``` - -一个可能的返回值为: - -```ts -{ - input1: 'input1 value', - input2: 'input2 value', - checkbox: true, - subform: { - input3: 'input3 value', - sel1: 'option2' - } -} -``` - -暂时还不支持设置表单数据的功能 diff --git a/doc/template.md b/doc/template.md deleted file mode 100644 index 6387964..0000000 --- a/doc/template.md +++ /dev/null @@ -1,48 +0,0 @@ -# JSON模板格式 - -目前支持的控件有以下几个: - -* [单行输入框](#单行输入框) -* [下拉选择框](#下拉选择框) -* [复选框](#复选框) -* [表单](#表单) - -## 单行输入框 - -| 参数 | 类型 | 是否可选 | 说明 | -|:---:|:---:|:---:|:---:| -| caption | string | yes | [caption字段作用说明](#caption-字段作用说明) | -| type | 'input' | no | 标识字段 | -| default | string | yes | 默认值,尚未实现 | -| hint | string | yes | 提示文本 | -| validate | string => boolean | yes | 校验函数,尚未实现 | - -## 下拉选择框 - -| 参数 | 类型 | 是否可选 | 说明 | -|:---:|:---:|:---:|:---:| -| caption | string | yes | [caption字段作用说明](#caption-字段作用说明) | -| type | 'select' | no | 标识字段 | -| option | string[] | no | 选项列表 | - -## 复选框 - -| 参数 | 类型 | 是否可选 | 说明 | -|:---:|:---:|:---:|:---:| -| caption | string | yes | [caption字段作用说明](#caption-字段作用说明) | -| type | 'checkbox' | no | 标识字段 | -| label | string | yes | 选项文本 | - -## 表单 - -| 参数 | 类型 | 是否可选 | 说明 | -|:---:|:---:|:---:|:---:| -| caption | string | yes | [caption字段作用说明](#caption-字段作用说明) | -| type | 'form' | no | 标识字段 | -| child | JSON[] | no | 子控件列表 | - -## caption 字段作用说明 - -当一个控件单独出现时,设置它的 `caption` 没有意义。 - -当一个控件被包含在表单中时,`caption` 将作为这个控件的说明显示出来。 diff --git a/src/autoform.ts b/src/autoform.ts deleted file mode 100644 index 81fd126..0000000 --- a/src/autoform.ts +++ /dev/null @@ -1,29 +0,0 @@ -import mdui from "mdui"; -import { ABase, ElTree, ReturnValue } from "./types.js"; -import { inject } from "./inject.js"; -import { value } from "./value.js"; - -class AutoForm { - readonly template: T - // @ts-ignore - el_tree: ElTree - - constructor (template: T) { - this.template = template - } - - init(el: HTMLDivElement): void { - this.el_tree = inject(this.template) - el.appendChild(this.el_tree.div) - mdui.mutation() - console.log(this.el_tree) - } - - value(): ReturnValue { - return value(this.el_tree, this.template) - } -} - -export { - AutoForm -} \ No newline at end of file diff --git a/src/inject.ts b/src/inject.ts deleted file mode 100644 index fa06d08..0000000 --- a/src/inject.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { ABase, ASelect, AInput, ElASelect, ElAInput, ElTree, isASelect, isAInput, ACheckbox, ElACheckbox, isACheckbox, AForm, ElAForm, isAForm } from "./types.js" - -function inject_input(template: AInput): ElAInput { - let div = document.createElement('div') - div.classList.add('mdui-textfield', 'mdui-textfield-floating-label') - - let label = document.createElement('label') - label.classList.add('mdui-textfield-label') - if (template.hint) - label.innerText = template.hint - div.appendChild(label) - - let input = document.createElement('input') - input.classList.add('mdui-textfield-input') - div.appendChild(input) - - return { - div, label, input - } -} - -function inject_select(template: ASelect): ElASelect { - let div = document.createElement('div') - - let select = document.createElement('select') - select.classList.add('mdui-select') - select.setAttribute('mdui-select', `{position: 'bottom'}`) - div.appendChild(select) - - let options: HTMLOptionElement[] = [] - for (let i = 0; i < template.option.length; i++) { - let option = document.createElement('option') - option.innerText = template.option[i] - option.setAttribute('value', i.toString()) - select.appendChild(option) - options.push(option) - } - - return { - div, select, - option: options - } -} - -function inject_checkbox(template: ACheckbox): ElACheckbox { - let div = document.createElement('div') - - let label = document.createElement('label') - label.classList.add('mdui-checkbox') - div.appendChild(label) - label.innerText = template.label - - let input = document.createElement('input') - input.setAttribute('type', 'checkbox') - label.appendChild(input) - - let i = document.createElement('i') - i.classList.add('mdui-checkbox-icon') - label.appendChild(i) - - return { - div, label, input, i - } -} - -function inject_form>(template: AForm): ElAForm { - let div = document.createElement('div') - div.classList.add('md-form') - - let child = {} - - for (let el in template.child) { - let s_div = document.createElement('div') - s_div.classList.add('md-form-group') - div.appendChild(s_div) - - let label = document.createElement('label') - label.classList.add('md-input-container') - if (template.child[el].caption) - label.innerText = template.child[el].caption! - s_div.appendChild(label) - - let s_el = inject(template.child[el]) - s_div.appendChild(s_el.div) - // @ts-ignore - child[el as string] = { - div, label, - el: s_el - } - } - - return { - div, - // @ts-ignore - child - } -} - -function inject(template: T): ElTree { - if (isAInput(template)) - return inject_input(template) - if (isASelect(template)) - return inject_select(template) - if (isACheckbox(template)) - return inject_checkbox(template) - if (isAForm(template)) - return inject_form(template) - throw new Error('Not implemented') -} - -export { - inject -} \ No newline at end of file diff --git a/src/types.ts b/src/types.ts deleted file mode 100644 index b039ec2..0000000 --- a/src/types.ts +++ /dev/null @@ -1,137 +0,0 @@ -type AMap = Record> = { - input: [AInput, ElAInput, string], - select: [ASelect, ElASelect, string], - checkbox: [ACheckbox, ElACheckbox, boolean], - form: [AForm, ElAForm, { - [K in keyof Child]: AMap[Child[K]["type"]][2] - }] -} - -interface ElBase { - div: HTMLDivElement -} - -interface ElAInput extends ElBase { - label: HTMLLabelElement - input: HTMLInputElement -} - -interface ElASelect extends ElBase { - select: HTMLSelectElement - option: HTMLOptionElement[] -} - -interface ElACheckbox extends ElBase { - label: HTMLLabelElement - input: HTMLInputElement - i: HTMLElement -} - -type ChildOfAForm = - T extends AForm ? Child : never; - -interface ElAFormChild extends ElBase { - el: T - label: HTMLLabelElement -} - -interface ElAForm = Record> extends ElBase { - child: { - [K in keyof Child]: ElAFormChild - } -} - -type ElAElement - = ElAInput - | ElASelect - | ElACheckbox - | ElAForm - -type ElTree = AMap[T['type']][1]; - -export type { - ElAInput, - ElASelect, - ElACheckbox, - ElAForm, - ElAElement, - ElBase, - ElTree -} - -interface ABase { - type: keyof AMap; - caption?: string -} - -interface AInput extends ABase { - type: 'input' - default?: string - hint?: string - validate?: (value: string) => { - isValid: boolean - message?: string - } -} - -interface ASelect extends ABase { - type: 'select' - option: string[] -} - -interface ACheckbox extends ABase { - type: 'checkbox' - label: string -} - -interface AForm = Record> extends ABase { - type: 'form' - child: Child -} - -type ReturnValue = AMap[T['type']][2] - -export type { - ABase, - AInput, - ASelect, - ACheckbox, - AForm, - ReturnValue, - ChildOfAForm -} - -function isAInput(obj: any): obj is AInput { - return obj.type === 'input' -} - -function isASelect(obj: any): obj is ASelect { - return obj.type === 'select' && obj.option instanceof Array -} - -function isACheckbox(obj: any): obj is ACheckbox { - return obj.type === 'checkbox' -} - -function isAForm(obj: any): obj is AForm { - if (!obj.child) - return false - if (obj.type !== 'form') - return false - for (let i of Object.keys(obj.child)) - if (!isAElement(obj.child[i])) - return false - return true -} - -function isAElement(obj: any): obj is ABase { - return isAInput(obj) || isASelect(obj) || isACheckbox(obj) || isAForm(obj) -} - -export { - isAInput, - isASelect, - isACheckbox, - isAForm, - isAElement -} \ No newline at end of file diff --git a/src/value.ts b/src/value.ts deleted file mode 100644 index b883e2f..0000000 --- a/src/value.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { ABase, ACheckbox, AForm, AInput, ASelect, ElACheckbox, ElAForm, ElAInput, ElASelect, ElBase, ReturnValue, isACheckbox, isAForm, isAInput, isASelect } from "./types.js"; - -function value_input(el: ElAInput, _: AInput): string { - return el.input.value -} - -function value_select(el: ElASelect, t: ASelect): string { - return t.option[el.select.selectedIndex] -} - -function value_checkbox(el: ElACheckbox, _: ACheckbox): boolean { - return el.input.checked -} - -function value_form>(el: ElAForm, t: AForm): ReturnValue> { - let ret = {} as ReturnValue> - for (let c in t.child) - ret[c] = value(el.child[c].el, t.child[c]) - return ret -} - -function value(el: ElBase, t: ABase): any { - if (isAInput(t)) - return value_input(el as ElAInput, t) - if (isASelect(t)) - return value_select(el as ElASelect, t) - if (isACheckbox(t)) - return value_checkbox(el as ElACheckbox, t) - if (isAForm(t)) - return value_form(el as ElAForm, t) - throw new Error('Not Implemented.') -} - -export { - value -} \ No newline at end of file From 8045612a46256713d00ce70174acaba51aa7870d Mon Sep 17 00:00:00 2001 From: Zecyel Date: Sun, 8 Oct 2023 10:18:54 +0800 Subject: [PATCH 14/14] =?UTF-8?q?Upd:=20=E6=9B=B4=E6=96=B0=E4=BA=86?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2241c23..7f7b43a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@quasi-dev/autoform", - "version": "0.0.1", + "version": "0.1.0", "type": "module", "license": "MIT", "description": "A simple form generator",