From 653e09280b3edf2cb35b9e2b0ab7568b04efa648 Mon Sep 17 00:00:00 2001 From: Anton Malofeev Date: Tue, 2 Mar 2021 18:50:18 +0300 Subject: [PATCH] feat: Drawer,AppBar completed for release --- CHANGELOG.md | 66 ++++++++++++++++--- README.md | 13 +++- example/src/pages/Home.tsx | 12 ++-- example/src/pages/ScaffoldApp.tsx | 6 +- lib/abstract/Alignment.ts | 12 ++-- lib/components/Align.tsx | 6 +- lib/components/AppBar.tsx | 1 + lib/components/Center.tsx | 2 +- lib/components/Dialog.tsx | 40 ++++++------ lib/components/Drawer.ts | 65 +++++++++++-------- lib/components/Navigation.tsx | 85 ++++++++++++++++++------- lib/components/Provider.tsx | 50 +++++++-------- lib/components/Scaffold.tsx | 102 ++++++++++++++++++++++++++---- 13 files changed, 323 insertions(+), 137 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d0b49e..d6deb8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,25 +1,75 @@ ## 0.8.0 -BREAKING CHANGE: Now package name is Vuefer. vue_flutter_tailwind will be deprecated as it will reach stable version. +BREAKING CHANGE: Now package name is `Vuefer`. `vue_flutter_tailwind` will be deprecated as it will reach stable version. BREAKING CHANGE: Webpack replaced with Vite -BREAKING CHANGE: MultiProvider.create replaced to build -BREAKING CHANGE: Scaffold now builds as Scaffold.build +BREAKING CHANGE: `MultiProvider.create` replaced to `MultiProvider.build` +BREAKING CHANGE: `Scaffold({})` now builds as `Scaffold.build({})` +BREAKING CHANGE: `Alignment.toOverlay` no is `Alignment.overlay` feat: Navigation push now can align Dialog(route) window feat: Drawer feat: AppBar +feat: Scaffold now has drawer and appBar -feat: Simplified provider call -Old: +To use Drawer you must first initialize somewhere above `Navigation` with `NavigationContorller` as below: ```typescript - +MultiProvider.build({ + models: [NavigationController], + child: Navigation({ + child: ..., + }), +}) ``` -New: +To open Drawer use +`Scaffold.openDrawer()` +To close Drawer use +`Scaffold.closeDrawer()` ```typescript - +Scaffold.build({ + drawer: Drawer({ + child: Column({ + children: [ + Text({ + text: ref('Drawer header'), + }), + ], + }), + }), + appBar: AppBar({ + leading: ElevatedButton({ + child: Text({ + text: ref('='), + }), + onTap: () => { + Scaffold.openDrawer() + }, + }), + title: Text({ + text: ref('Title'), + }), + actions: [ + ElevatedButton({ + child: Text({ + text: ref('a'), + }), + }), + ElevatedButton({ + child: Text({ + text: ref('b'), + }), + }), + ElevatedButton({ + child: Text({ + text: ref('c'), + }), + }), + ], + }), + body: Home(), +}) ``` ## 0.7.0 diff --git a/README.md b/README.md index fdd88b1..a0aaf40 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ export const wrapperApp = () => { return Scaffold({ body: Align({ - toOverlay: true, + overlay: true, alignment: Alignment.bottom, child: Container({ padding, @@ -310,16 +310,23 @@ MultiProvider.create({ [] Color palette - [] Scaffold + [x] Drawer + [x] AppBar + +- [] Drawer + [x] basic + [] animation + +- [] AppBar + [x] basic ## Next - [] Flexible - [] OutlinedButton - [] Ripple -- [] Drawer - [] Progress - [] Card -- [] AppBar - [] Icon - [] IconButton - [] Bar diff --git a/example/src/pages/Home.tsx b/example/src/pages/Home.tsx index d5eff92..5f66d43 100644 --- a/example/src/pages/Home.tsx +++ b/example/src/pages/Home.tsx @@ -140,7 +140,7 @@ export const Home = () => { return defineComponent({ name: 'App', setup() { - const layoutMatrix = reactive([ + const layoutMatrix = ref([ { x: 0, y: 0, width: 2, height: 2, index: 0 }, { x: 2, y: 0, width: 2, height: 4, index: 1 }, { x: 4, y: 0, width: 6, height: 8, index: 2 }, @@ -166,7 +166,7 @@ export const Home = () => { NavigationController ) const gridViewDelegate = GridViewDelegate.use({ - gridViewItems: layoutMatrix.map((el) => + gridViewItems: layoutMatrix.value.map((el) => GridViewItem({ child: TextButton({ child: Text({ @@ -195,7 +195,7 @@ export const Home = () => { return () => h( Align({ - toOverlay: true, + overlay: true, alignment: Alignment.center, child: Container({ padding, @@ -293,16 +293,16 @@ export const Home = () => { isDraggable: ref(true), isResizable: ref(true), onPositionUpdate: (newPosition) => { - const i = layoutMatrix.findIndex( + const i = layoutMatrix.value.findIndex( (el) => el.index == newPosition?.index ) // console.log({ i }) if (i) { if (newPosition) { - layoutMatrix.splice(i, 1, newPosition) + layoutMatrix.value.splice(i, 1, newPosition) return } - layoutMatrix.splice(i, 1) + layoutMatrix.value.splice(i, 1) } }, delegate: gridViewDelegate, diff --git a/example/src/pages/ScaffoldApp.tsx b/example/src/pages/ScaffoldApp.tsx index 52c14b4..7736e31 100644 --- a/example/src/pages/ScaffoldApp.tsx +++ b/example/src/pages/ScaffoldApp.tsx @@ -9,7 +9,7 @@ import { } from '../../../lib' import { Home } from './Home' -export const ScaffoldApp = Scaffold({ +export const ScaffoldApp = Scaffold.build({ drawer: Drawer({ child: Column({ children: [ @@ -24,7 +24,9 @@ export const ScaffoldApp = Scaffold({ child: Text({ text: ref('='), }), - onTap: () => {}, + onTap: () => { + Scaffold.openDrawer() + }, }), title: Text({ text: ref('Title'), diff --git a/lib/abstract/Alignment.ts b/lib/abstract/Alignment.ts index 5eb83cf..728e1f1 100644 --- a/lib/abstract/Alignment.ts +++ b/lib/abstract/Alignment.ts @@ -15,7 +15,7 @@ export enum AlignmentEdge { } interface AlignmentI { alignment?: AlignmentEdge - toOverlay?: boolean + overlay?: boolean } // export class AlignmentHelper { // static getClassNames({ alignment, heightFactor, widthFactor }: AlignI) { @@ -25,10 +25,10 @@ interface AlignmentI { export class Alignment { alignment: AlignmentEdge - toOverlay: boolean - constructor({ alignment, toOverlay }: AlignmentI) { + overlay: boolean + constructor({ alignment, overlay }: AlignmentI) { this.alignment = alignment ?? AlignmentEdge.topLeft - this.toOverlay = toOverlay ?? false + this.overlay = overlay ?? false } static _factory(alignment: AlignmentEdge) { return new Alignment({ alignment }) @@ -129,8 +129,6 @@ export class Alignment { return 'left-0 top-0' } })() - return [this.toOverlay ? 'absolute ' + edge : '', relativeAlignment].join( - ' ' - ) + return [this.overlay ? 'absolute ' + edge : '', relativeAlignment].join(' ') } } diff --git a/lib/components/Align.tsx b/lib/components/Align.tsx index 5d795d7..d0a0fec 100644 --- a/lib/components/Align.tsx +++ b/lib/components/Align.tsx @@ -8,13 +8,13 @@ export interface AlignI { alignment: Alignment // widthFactor?: EdgeInsetsStep; // heightFactor?: EdgeInsetsStep; - toOverlay?: Maybe + overlay?: Maybe key?: Maybe } -export const Align = ({ child, toOverlay, alignment }: AlignI) => { +export const Align = ({ child, overlay, alignment }: AlignI) => { const finalAlignment = alignment - finalAlignment.toOverlay = toOverlay ?? false + finalAlignment.overlay = overlay ?? false return defineComponent({ name: 'Align', render() { diff --git a/lib/components/AppBar.tsx b/lib/components/AppBar.tsx index c36d489..bd6420a 100644 --- a/lib/components/AppBar.tsx +++ b/lib/components/AppBar.tsx @@ -14,6 +14,7 @@ import { import { Column } from './Column' import { Row } from './Row' import { SizedBox } from './SizedBox' + interface AppBarI { key?: Maybe leading?: Maybe diff --git a/lib/components/Center.tsx b/lib/components/Center.tsx index d8925a6..7e680f2 100644 --- a/lib/components/Center.tsx +++ b/lib/components/Center.tsx @@ -15,7 +15,7 @@ export const Center = ({ child, key }: CenterI) => { render() { return h( Align({ - toOverlay: true, + overlay: true, alignment: Alignment.center, child, }) diff --git a/lib/components/Dialog.tsx b/lib/components/Dialog.tsx index 6feafda..67f978c 100644 --- a/lib/components/Dialog.tsx +++ b/lib/components/Dialog.tsx @@ -53,29 +53,29 @@ export const showDialog = ({ * * Be sure that you have Navigation widget on top of tree * - * ```typescript - * const navigationController = MultiProvider.get( - * NavigationController - * ) - * ``` + ```typescript + const navigationController = MultiProvider.get( + NavigationController + ) + ``` * * Second - call a function inside for example Button.onTap: * - * ```typescript - * ElevatedButton({ - * child: Text({ - * text: ref('Show dialog'), - * }), - * onTap: () => { - * showDialog({ - * builder: Dialog({ - * child: Text({ text: ref('Hello World') }), - * }), - * navigationController, - * }) - * }, - * }), - * ``` + ```typescript + ElevatedButton({ + child: Text({ + text: ref('Show dialog'), + }), + onTap: () => { + showDialog({ + builder: Dialog({ + child: Text({ text: ref('Hello World') }), + }), + navigationController, + }) + }, + }), + ``` * * To close Dialog, just use `navigationController.pop()` */ diff --git a/lib/components/Drawer.ts b/lib/components/Drawer.ts index 3b96cfe..546d7c2 100644 --- a/lib/components/Drawer.ts +++ b/lib/components/Drawer.ts @@ -1,4 +1,5 @@ import { Component, computed, defineComponent, h } from 'vue' +import { Alignment } from '../abstract/Alignment' import { Maybe } from '../abstract/BasicTypes' import { BoxShadow } from '../abstract/BoxShadow' import { Color } from '../abstract/Color' @@ -14,31 +15,45 @@ interface DrawerI { child: Component backgroundColor?: Maybe elevation?: Maybe + alignment?: Maybe } -export const Drawer = ({ backgroundColor, child, elevation }: DrawerI) => { - return defineComponent({ - name: 'Drawer', - setup() { - const heightBox = new SizedBoxHeight({ height: SizeBoxStep.max }) - const widthBox = new SizedBoxWidth({ width: EdgeInsetsStep.s56 }) - const classes = computed((): string[] => { - return [ - (elevation ?? BoxShadow.lg).css, - EdgeInsets.all(EdgeInsetsStep.s10).paddingCss, - heightBox.css, - widthBox.css, - (backgroundColor ?? Colors.white).backgroundCss, - ] - }) - return () => - h( - 'div', - { - class: classes.value, - }, - [h(child)] - ) - }, - }) +export interface DrawerBuilder { + widget: Component + alignment?: Maybe +} + +export const Drawer = ({ + backgroundColor, + child, + elevation, + alignment, +}: DrawerI): DrawerBuilder => { + return { + alignment, + widget: defineComponent({ + name: 'Drawer', + setup() { + const heightBox = new SizedBoxHeight({ height: SizeBoxStep.max }) + const widthBox = new SizedBoxWidth({ width: EdgeInsetsStep.s56 }) + const classes = computed((): string[] => { + return [ + (elevation ?? BoxShadow.lg).css, + EdgeInsets.all(EdgeInsetsStep.s10).paddingCss, + heightBox.css, + widthBox.css, + (backgroundColor ?? Colors.white).backgroundCss, + ] + }) + return () => + h( + 'div', + { + class: classes.value, + }, + [h(child)] + ) + }, + }), + } } diff --git a/lib/components/Navigation.tsx b/lib/components/Navigation.tsx index d948e9f..2c3e751 100644 --- a/lib/components/Navigation.tsx +++ b/lib/components/Navigation.tsx @@ -1,4 +1,13 @@ -import { Component, defineComponent, h, reactive, ref, watch } from 'vue' +import { + Component, + computed, + defineComponent, + h, + reactive, + ref, + watch, +} from 'vue' +import { AlignmentEdge } from '../abstract/Alignment' import { Colors } from '../abstract/Colors' import { EdgeInsetsStep } from '../abstract/EdgeInsets' import { @@ -10,7 +19,7 @@ import { OpacityDecorationSteps, } from '../abstract/OpacityDecoration' import { SizedBoxHeight, SizedBoxWidth } from '../abstract/SizedBox' -import { Align } from './Align' +import { Center } from './Center' import { Positioned } from './Positioned' import { MultiProvider } from './Provider' import { Visibility } from './Visibility' @@ -27,14 +36,14 @@ interface NavigationI { * * Add controller into MultiPorvider and Navigation widget below: * - * ```typescript - * MultiProvider.create({ - * models: [NavigationController, ...], - * child: Navigation({ - * child: ..., - * }), - * }) - * ``` + ```typescript + MultiProvider.create({ + models: [NavigationController, ...], + child: Navigation({ + child: ..., + }), + }) + ``` */ export const Navigation = ({ child }: NavigationI) => { return defineComponent({ @@ -54,7 +63,11 @@ export const Navigation = ({ child }: NavigationI) => { const isRoutesExists = ref(false) const isFullscreen = ref(false) const isNotFullscreen = ref(false) - + const alignment = computed(() => { + const finalAlignment = routeController._alignment + finalAlignment.overlay = true + return finalAlignment + }) watch( routeController.routes, (newRoutes) => { @@ -83,6 +96,7 @@ export const Navigation = ({ child }: NavigationI) => { isNotFullscreen, isRoutesExists, routeController, + alignment, } }, render() { @@ -132,19 +146,42 @@ export const Navigation = ({ child }: NavigationI) => { }} > {h( - Align({ - alignment: this.routeController._alignment, - toOverlay: true, - child: h( -
- event.stopPropagation() - } - > - {h(this.currentRoute.widget ??
)} -
- ), - }) + (() => { + switch (this.alignment.alignment) { + case AlignmentEdge.center: + return Center({ + child: ( +
+ event.stopPropagation() + } + > + {h( + this.currentRoute.widget ?? ( +
+ ) + )} +
+ ), + }) + + default: + return ( +
+ event.stopPropagation() + } + > + {h( + this.currentRoute.widget ?? ( +
+ ) + )} +
+ ) + } + })() )}
), diff --git a/lib/components/Provider.tsx b/lib/components/Provider.tsx index 1aa0723..7b383c2 100644 --- a/lib/components/Provider.tsx +++ b/lib/components/Provider.tsx @@ -21,35 +21,35 @@ enum InstanceTypes { * * Let's suppose we have a model: * - * ```typescript - * export class Hero { - * constructor(public name: string) {} - * } - * export class HeroesModel { - * heroes = reactive[]>([]) - * add(hero: Hero) { - * this.heroes.push(hero) - * } - * get count() { - * return this.heroes.length - * } - * } - * ``` + ```typescript + export class Hero { + constructor(public name: string) {} + } + export class HeroesModel { + heroes = reactive[]>([]) + add(hero: Hero) { + this.heroes.push(hero) + } + get count() { + return this.heroes.length + } + } + ``` * * Create Provider on top of tree * - * ```typescript - * MultiProvider.create({ - * models: [HeroesModel], - * child: wrapperApp(), - * }) - * ``` + ```typescript + MultiProvider.create({ + models: [HeroesModel], + child: wrapperApp(), + }) + ``` * * And somewhere in tree just call * - * ```typescript - * const heroModel = MultiProvider.get(HeroesModel) - * ``` + ```typescript + const heroModel = MultiProvider.get(HeroesModel) + ``` * */ export class MultiProvider { @@ -104,13 +104,11 @@ export class MultiProvider { static get = Constructor>( modelName: P ) { - const guardType = (_model: unknown): _model is P => true - const symbol = this._allProvidersSymbols.get(modelName.name) if (symbol == null) throw Error(`${modelName} doesn't have a provider!`) const model: Maybe = inject(symbol) if (model == null) throw new Error(`${modelName} have a provider but return null:(!`) - return guardType(model) + return model } } diff --git a/lib/components/Scaffold.tsx b/lib/components/Scaffold.tsx index e2d3546..497b7fb 100644 --- a/lib/components/Scaffold.tsx +++ b/lib/components/Scaffold.tsx @@ -1,24 +1,90 @@ import { Component, computed, defineComponent, h } from 'vue' import { + Alignment, Maybe, NavigationController, SizeBoxStep, SizedBoxHeight, } from '../abstract' +import { DrawerBuilder } from './Drawer' import { MultiProvider } from './Provider' +interface ScaffoldI { + body: Component + appBar?: Maybe + drawer?: Maybe +} + +/** + * To init Scaffold use `Scaffold.build` + * + * To use Drawer you must first initialize somewhere above `Navigation` with `NavigationContorller` as below: +```typescript +MultiProvider.build({ + models: [NavigationController], + child: Navigation({ + child: ..., + }), +}) +``` + + * + * To open Drawer use + * `Scaffold.openDrawer()` + * To close Drawer use + * `Scaffold.closeDrawer()` + * + * +```typescript +Scaffold.build({ + drawer: Drawer({ + child: Column({ + children: [ + Text({ + text: ref('Drawer header'), + }), + ], + }), + }), + appBar: AppBar({ + leading: ElevatedButton({ + child: Text({ + text: ref('='), + }), + onTap: () => { + Scaffold.openDrawer() + }, + }), + title: Text({ + text: ref('Title'), + }), + actions: [ + ElevatedButton({ + child: Text({ + text: ref('a'), + }), + }), + ElevatedButton({ + child: Text({ + text: ref('b'), + }), + }), + ElevatedButton({ + child: Text({ + text: ref('c'), + }), + }), + ], + }), + body: Home(), +}) +``` +**/ export class Scaffold { - static _drawer?: Maybe - static openDrawer: CallableFunction - static build({ - body, - appBar, - drawer, - }: { - body: Component - appBar?: Maybe - drawer?: Maybe - }) { + static _drawer?: Maybe + static openDrawer: CallableFunction = () => '' + static closeDrawer: CallableFunction = () => '' + static build({ body, appBar, drawer }: ScaffoldI) { Scaffold._drawer = drawer return defineComponent({ name: 'Scaffold', @@ -33,7 +99,19 @@ export class Scaffold { const navigationController = MultiProvider.get( NavigationController ) - + const openDrawer = () => { + if (Scaffold._drawer == null) { + console.warn('You forgot to supply Drawer to Scaffold.') + return + } + navigationController.push({ + widget: Scaffold._drawer.widget, + alignment: Scaffold._drawer.alignment ?? Alignment.left, + fullscreen: false, + }) + } + Scaffold.openDrawer = openDrawer + Scaffold.closeDrawer = () => navigationController.pop() return () => h('div', {}, [ h(appBar ??
),