Giter Club home page Giter Club logo

cycraft / blitzar Goto Github PK

View Code? Open in Web Editor NEW
125.0 11.0 22.0 14.2 MB

Generate Vue Forms and Data-tables fast with a simple JSON-like syntax ⚡

Home Page: https://blitzar.cycraft.co

License: MIT License

JavaScript 2.90% Vue 81.67% TypeScript 14.11% CSS 1.03% HTML 0.29%
vue-framework component-framework vue vuejs prototyping fast-prototyping data-table data-tables table-generator table-builder tables forms form-builder form-generator schema

blitzar's Introduction

hero

Blitzar

Generate Vue Forms and Data-tables fast with a simple JSON-like syntax ⚡️

npm i blitzar

Documentation

Vue 3
blitzar.cycraft.co

Vue 2
blitzar0.web.app

Motivation

Check out this blog post to understand my motivation for creating Blitzar: Better, Faster Vue Forms with Blitzar

Meet the family

  • Magnetar 🌟

    State-management done right with automatic sync to remote databases and services. Framework-agnostic & optimistic-UI built-in

  • Blitzar ⚡️

    Generate Vue Forms and Data-tables fast with a simple JSON-like syntax

  • Planetar 🪐

    A Vue framework for creating a design system styleguide with interactive component explorer

blitzar's People

Contributors

dependabot[bot] avatar lovelyandy avatar mesqueeb avatar robinradic avatar ymarcon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

blitzar's Issues

build: offload package.json scripts to ts-node scripts

I don't mind the dep: scripts, but the others are currently getting out of hand...

I just need these scripts:

  • dev
  • dev:docs
  • publish

And all other things that happen should happen in a ts-node script inside scripts/, using the dependency fs-extra as much as possible.

Also:
Anything that happens in the prepare script should be automated with a watcher somehow:

  • whenever anything in /packages/form changes, run something similar to the currently set up prepare:form
  • whenever anything in /packages/table changes, run something similar to the currently set up prepare:table
  • whenever anything in /packages/utils changes, execute npm run build in that folder

The scripts in package.json shown below should therefore be reduced to just the 3 I mention above.

"dev": "npm run prepare && npm run start:dev && cd packages/dev && npm run dev",
    "dev:docs": "npm run prepare && npm run start:dev && cd packages/docs && npm run dev",
    "dev:vuepress": "npm run prepare && npm run start:dev && cd packages/docs-vuepress && npm run dev",
    "test": "lerna run test",
    "build": "lerna run build",
    "build:utils": "cd packages/utils && npm run build",
    "build:form": "cd packages/form && npm run build",
    "build:for-publish": "npm run prepare && npm run start:build && lerna run build && npm run build:vue-int && git add -A && git commit -m \"chore: build\" && git push",
    "build:favicon": "copyfiles media/* packages/docs/public && cd packages/docs && npm run build:favicon",
    "build:vue-int": "lerna run vue-int && ts-node scripts/vueInt.ts",
    "build-and-publish": "npm run build:for-publish && npm run publish",
    "publish": "lerna publish && npm run publish:post",
    "publish:post": "npm run start:dev && git add -A && git commit -m \"chore: post-build\" && git push",
    "deploy:docs": "npm run build:for-publish && firebase deploy --only hosting && git add -A && git commit -m \"chore: deploy\" && git push",
    "prepare": "npm run prepare:form && npm run prepare:table",
    "prepare:form": "npm run copy:form-to-docs && npm run copy:form-to-vuepress && npm run copy:form-to-dev",
    "prepare:table": "npm run copy:table-to-docs && npm run copy:table-to-vuepress && npm run copy:table-to-dev",
    "copy:form-to-docs": "rimraf packages/docs/src/components/atoms/form && copyfiles --up 3 'packages/form/src/**/*' packages/docs/src/components/atoms/form",
    "copy:form-to-vuepress": "rimraf packages/docs-vuepress/src/.vuepress/components/atoms/form && copyfiles --up 3 'packages/form/src/**/*' packages/docs-vuepress/src/.vuepress/components/atoms/form",
    "copy:form-to-dev": "rimraf packages/dev/src/components/atoms/form && copyfiles --up 3 'packages/form/src/**/*' packages/dev/src/components/atoms/form",
    "copy:table-to-docs": "rimraf packages/docs/src/components/atoms/table && copyfiles --up 3 'packages/table/src/**/*' packages/docs/src/components/atoms/table",
    "copy:table-to-vuepress": "rimraf packages/docs-vuepress/src/.vuepress/components/atoms/table && copyfiles --up 3 'packages/table/src/**/*' packages/docs-vuepress/src/.vuepress/components/atoms/table",
    "copy:table-to-dev": "rimraf packages/dev/src/components/atoms/table && copyfiles --up 3 'packages/table/src/**/*' packages/dev/src/components/atoms/table",
    "replace:dist-src": "replace-in-files --string='\"module\": \"dist/index.js\"' --replacement='\"module\": \"src/index.js\"' 'packages/*/package.json'",
    "replace:src-dist": "replace-in-files --string='\"module\": \"src/index.js\"' --replacement='\"module\": \"dist/index.js\"' 'packages/*/package.json'",
    "start:dev": "npm run replace:dist-src",
    "start:build": "npm run replace:src-dist",

feat(form): add validation strategy: only on save [2h]

propName: tbd - suggestions welcome

perhaps a prop called "executeValidation" and have it have "immediate" by default and as other options "never" and "onsave"

will need to use quasar's lazyRules="ondemand"

  • create a doc example for it
  • create the feature

typescript types

Quickly mashed this together, for anyone interested.

blitzar.d.ts

declare module 'blitzar' {
    import Vue, { PluginFunction, VueConstructor } from 'vue';

    export interface BlitzarLang {
        archive: string
        delete: string
        cancel: string
        edit: string
        save: string
        requiredField: string
        formValidationError: string
    }

    export interface BlitzFieldProps {
        /**
         * The value of the field. The BlitzForm `formData` is an object with the value of each field and the id for key.
         * @type {any}
         * @category model
         */
        value: any

        /**
         * An `id` is required for the BlitzForm to be able to know which fields have which value.
         *
         * A string with dot notation will become a nested field in the `formData`.
         * @type {string}
         * @category model
         */
        id: string

        /**
         * A defaultValue value to be used when the `value` is `undefined`.
         *
         * You can also pass a function that will receive two params you can work with: `(formData, context)`
         * - `formData` is the value object of your BlitzForm. This will be undefined when BlitzField is used as stand-alone (without BlitzForm) unless you manually pass it.
         * - `context` is either your BlitzForm or BlitzField context with many usefull props. See the documentation on Evaluated Props for more info.
         * @type {(formData: Record<string, any>, formContext: FormContext) => any | any}
         * @category model
         */
        defaultValue: any

        /**
         * A function that modifies a value before it is used in the actual component. (see `parseInput` for the reverse)
         * @type {(val: any) => any}
         * @example val => val && val.split(' ').map(str => !str ? '' : `${str[0].toUpperCase()}${str.slice(1)}`).join(' ')
         * @example val => Number(val)
         * @example val => Date(val)
         * @category model
         */
        parseValue: Function

        /**
         * A function that modifies a value after user input but before the value is emitted. (see `parseValue` for the reverse)
         * @type {(val: any) => any}
         * @example val => (val || '').toLowerCase()
         * @example val => val.toISOString()
         * @category model
         */
        parseInput: Function

        /**
         * The component to be used for the field. Is mounted via `<component :is="component" />`. You can pass the name of a native HTML5 element or Vue component that is globally registered. You can also import the Vue file and directly pass the imported object, just like you would when you add it to a Vue file's components prop.
         * @type {string | Function | EvaluatedProp<string | Function>}
         * @example 'input'
         * @example 'MyCustomField'
         * @category content
         */
        component: any
        // object for imported vue instances

        /**
         * An Object with keys for the slot names and an object for values. The object you pass to a slot is itself applied as a `<component is="" />`.
         *
         * The last example below shows how this is actually used under the hood.
         * @type {{ label?: string | Record<string, any> | Record<string, any>[], default?: string | Record<string, any> | Record<string, any>[] } | EvaluatedProp<{ label?: string | Record<string, any> | Record<string, any>[], default?: string | Record<string, any> | Record<string, any>[] }>}
         * @example { label: { component: 'MyTooltip', tip: 'hi' } } }
         * @example <slot name="label"><component :is="slots.label.component" v-bind="slots.label" /></slot>
         * @category content
         */
        slots: object | Function

        /**
         * The text used in the UI for the action buttons and some error messages.
         *
         * The example shows how the error message for required fields is overwritten.
         * @type {{ archive?: string, delete?: string, cancel?: string, edit?: string, save?: string, requiredField?: string, formValidationError?: string } | EvaluatedProp<{ archive?: string, delete?: string, cancel?: string, edit?: string, save?: string, requiredField?: string, formValidationError?: string }>}
         * @example { requiredField: `Don't forget this field!` }
         * @category content
         */
        lang: BlitzarLang
        /**
         * The field label.
         * @type {string | EvaluatedProp<string>}
         * @example 'Your Name'
         * @category content
         */
        label: string | Function

        /**
         * A smaller label for extra info.
         * @type {string | EvaluatedProp<string>}
         * @example 'first and last'
         * @category content
         */
        subLabel: string | Function

        /**
         * The mode represents how fields are rendered
         * - `'edit'` — (default) show editable fields based on the schema
         * - `'view'` — show each field with `readonly: true`
         * - `'disabled'` — show each field with `disabled: true`
         * - `'raw'` — used to show raw data of your form (for select components, it will show the data label instead of its value)
         * - `'add'` — the same as `'edit'`
         *
         * This prop can be set on a BlitzField or on a BlitzForm (in which case it's applied to all fields).
         * @type {'edit' | 'view' | 'disabled' | 'raw' | 'add' | EvaluatedProp<'edit' | 'view' | 'disabled' | 'raw' | 'add'>}
         * @category state
         */
        mode: string | Function

        /**
         * An Object with an event name as key and the handler function as value. The function you pass will receive the native event payload as first parameter and the BlitzField context (the component instance) as second: `($event, context) => {}`
         * @type {Record<string, (event: any, formContext: FormContext) => any> | EvaluatedProp<Record<string, (event: any, formContext: FormContext) => any>>}
         * @example { click: (val, { formData }) => console.log(formData) }
         * @category behavior
         */
        events: any

        /**
         * Whether or not the field is required or not. If a field is marked 'required' it will add a default rule like so: `[val => (val !== null && val !== undefined) || 'Field is required']`. The default message can be set in the `lang` prop as `requiredField`.
         * @type {boolean | EvaluatedProp<boolean>}
         * @category behavior
         */
        required: boolean | Function

        /**
         * An array of rule functions that receive the value of the field as parameter and should return `true` if the rule passes or a `string` if the rule fails. The string represents the error message that is then shown underneath the field in red.
         * @type {((val: any) => (true | string))[] | EvaluatedProp<((val: any) => (true | string))[]>}
         * @example [val => (val && val.length <= 3) || 'Maximum 3 characters']
         * @category behavior
         */
        rules: any

        /**
         * An array with prop names that should be treated as Evaluated Props when passed a function.
         *
         * This prop can be set on a BlitzField or on a BlitzForm (in which case it's applied to all fields).
         * @type {string[]}
         * @category behavior
         */
        evaluatedProps: string[]

        /**
         * Set to `true` if the component has its own labels and you do not want the BlitzField to show a label.
         *
         * When `true` subLabels will also be hidden and passed to the component instead as a prop called 'hint'.
         *
         * This prop can be set on a BlitzField or on a BlitzForm (in which case it's applied to all fields).
         * @type {boolean | undefined | EvaluatedProp<boolean | undefined>}
         * @category style
         */
        internalLabels: boolean

        /**
         * Set to true if the component has its own error handling. This makes sure it passes on props like `rules` and does nothing with them in the BlitzField.
         *
         * This prop can be set on a BlitzField or on a BlitzForm (in which case it's applied to all fields).
         * @type {boolean | undefined | EvaluatedProp<boolean | undefined>}
         * @category behavior
         */
        internalErrors: boolean

        /**
         * Setting to `false` will hide the field. When using as an Evaluated Prop it can used to conditionally hide fields based on the other `formData`.
         * @type {boolean | EvaluatedProp<boolean>}
         * @example (val, { mode }) => (mode === 'edit')
         * @example false
         * @category state
         */
        showCondition: boolean

        /**
         * `readonly` defaults to `true` on `mode: 'view'`
         * @type {boolean | 'readonly' | EvaluatedProp<boolean | 'readonly'>}
         * @category state
         */
        readonly: boolean

        /**
         * `disabled` defaults to `true` on `mode: 'disabled'`
         * @type {boolean | 'disabled' | EvaluatedProp<boolean | 'disabled'>}
         * @category state
         */
        disabled: boolean

        /**
         * The position of the label in comparison to the field.
         *
         * This prop can be set on a BlitzField or on a BlitzForm (in which case it's applied to all fields).
         * @type {'top' | 'left' | EvaluatedProp<'top' | 'left'>}
         * @category style
         */
        labelPosition: string | Function

        /**
         * Custom styling to be applied to the BlitzField. Applied like so `:style="fieldStyle"`. Can be an Evaluated Prop (this is why I opted to have a different name from `style`).
         *
         * In a BlitzForm schema you can also just write `style: '...'` and BlitzForm will pass that as fieldStyle for you, because "style" is not a valid prop name.
         * @type {string | Record<string, boolean> | (string | Record<string, boolean>)[] | EvaluatedProp<string | Record<string, boolean> | (string | Record<string, boolean>)[]>}
         * @example 'padding: 0.5em; color: white'
         * @category style
         */
        fieldStyle: any

        /**
         * Custom classes to be applied to the BlitzField. Applied like so `:class="fieldClasses"`. Can be an Evaluated Prop (this is why I opted to have a different name from `class`).
         *
         * In a BlitzForm schema you can also just write `class: '...'` and BlitzForm will pass that as `fieldClasses` for you, because "class" is not a valid prop name.
         * @type {string | Record<string, boolean> | (string | Record<string, boolean>)[] | EvaluatedProp<string | Record<string, boolean> | (string | Record<string, boolean>)[]>}
         * @example ['dark-theme']
         * @category style
         */
        fieldClasses: any

        /**
         * Custom styling to be applied to the inner component of BlitzField. Applied like so `:style="componentStyle"`. Can be an Evaluated Prop.
         * @type {string | Record<string, boolean> | (string | Record<string, boolean>)[] | EvaluatedProp<string | Record<string, boolean> | (string | Record<string, boolean>)[]>}
         * @example 'padding: 1em;'
         * @category style
         */
        componentStyle: any

        /**
         * Custom classes to be applied to the inner component of BlitzField. Applied like so `:class="componentClasses"`. Can be an Evaluated Prop.
         * @type {string | Record<string, boolean> | (string | Record<string, boolean>)[] | EvaluatedProp<string | Record<string, boolean> | (string | Record<string, boolean>)[]>}
         * @example ['dark-theme']
         * @category style
         */
        componentClasses: any

        /**
         * Custom styling to be applied to the label of BlitzField. Applied like so `:style="componentStyle"`. Can be an Evaluated Prop.
         *
         * This prop can be set on a BlitzField or on a BlitzForm (in which case it's applied to all fields).
         * @type {string | Record<string, boolean> | (string | Record<string, boolean>)[] | EvaluatedProp<string | Record<string, boolean> | (string | Record<string, boolean>)[]>}
         * @example 'font-weight: 200;'
         * @category style
         */
        labelStyle: any

        /**
         * Custom classes to be applied to the label of BlitzField. Applied like so `:class="labelClasses"`. Can be an Evaluated Prop.
         *
         * This prop can be set on a BlitzField or on a BlitzForm (in which case it's applied to all fields).
         * @type {string | Record<string, boolean> | (string | Record<string, boolean>)[] | EvaluatedProp<string | Record<string, boolean> | (string | Record<string, boolean>)[]>}
         * @example ['text-h6']
         * @category style
         */
        labelClasses: any

        /**
         * This is the *nested* data of all the fields inside a BlitzForm. (When using BlitzListForm as standalone, this is an array.)
         *
         * It's not something you can pass via the schema, but something that BlitzForm will automatically pass to each of its fields so you can use it in Evaluated Props.
         * @type {Record<string, any> | Record<string, any>[]}
         * @category readonly
         */
        formData: object | Array<any>

        /**
         * This is the *flattened* data of all the fields inside a BlitzForm.
         *
         * It's not something you can pass via the schema, but something that BlitzForm will automatically pass to each of its fields so you can use it in Evaluated Props.
         * @type {Record<string, any>}
         * @category readonly
         */
        formDataFlat: object

        /**
         * A manually set 'id' of the BlitzForm. This only exists if you passed an id directly to the BlitzForm.
         *
         * It's not something you can pass via the schema, but something that BlitzForm will automatically pass to each of its fields so you can use it in Evaluated Props.
         * @type {string}
         * @category readonly
         */
        formId: string

        /**
         * The `fieldInput` function of BlitzForm. Is passed so it can be used in events. Eg.: `events: { input: (value, { fieldInput } => fieldInput({ id: 'otherField', value }))}`
         *
         * It's not something you can pass via the schema, but something that BlitzForm will automatically pass to each of its fields so you can use it in Evaluated Props.
         * @type {(val: any, formContext: FormContext) => void}
         * @category readonly
         */
        fieldInput: Function

        /**
         * (only present in BlitzListForm!)
         * The `rowInput` function of BlitzForm. Is passed so it can be used in events. Eg.: `events: { input: (value, { fieldInput } => fieldInput({ id: 'otherField', value }))}`
         *
         * It's not something you can pass via the schema, but something that BlitzListForm will automatically pass to each of its fields so you can use it in Evaluated Props.
         * @type {(val: any, formContext: FormContext) => void}
         * @category readonly
         */
        rowInput: Function

        /**
         * (only present in BlitzListForm!)
         * The current row index of this field.
         *
         * It's not something you can pass via the schema, but something that BlitzListForm will automatically pass to each of its fields so you can use it in Evaluated Props.
         * @type {number}
         * @category readonly
         */
        rowIndex: number

        /**
         * (only present in BlitzListForm!)
         * This is the *nested* data of all the fields of the row.
         *
         * It's not something you can pass via the schema, but something that BlitzListForm will automatically pass to each of its fields so you can use it in Evaluated Props.
         * @type {Record<string, any>}
         * @category readonly
         */
        rowData: object

        /**
         * (only present in BlitzListForm!)
         * This is a function that you can call to delete the row.
         *
         * It's not something you can pass via the schema, but something that BlitzListForm will automatically pass to each of its fields so you can use it in Evaluated Props.
         * @type {() => void}
         * @category readonly
         */
        deleteRow: Function
    }

    export interface BlitzFormProps extends Vue {
        /**
         * An object with the data of the entire form. The object keys are the ids of the fields passed in the `schema`.
         *
         * To be used with `:value` or `v-model`.
         * @type {Record<string, any>}
         * @example { name: '' }
         * @category model
         */
        value: any

        /**
         * A manually set `id` of the BlitzForm. This prop is accessible in the `context` (as `formId`) of any Evaluated Prop and event.
         *
         * Read more on Evaluated Props in its dedicated page.
         * @type {string}
         * @category model
         */
        id: string

        /**
         * This is the heart of your BlitzForm. It's the schema that defines what fields will be generated.
         *
         * The possible props you can pass are:
         * - BlitzField props (see BlitzField API Card in the documentation)
         * - any props of the actual component you define
         * @type {Record<string, any>[]}
         * @example [{id: 'name', label: 'Name', component: 'input', style: 'color: white'}, {id: 'age', label: 'Age', component: 'input', type: 'number', style: 'color: white'}]
         * @category model
         */
        schema: BlitzFieldProps[]

        /**
         * Buttons on top of the form that control the `mode` of the form. The possible pre-made buttons are:
         * - `'edit'` — a button which puts the form in 'edit' mode & does `emit('edit')`
         * - `'cancel'` — a button which puts the form in 'view' mode & does `emit('cancel')`
         * - `'save'` — a button which puts the form in 'edit' mode & does `emit('save', {newData, oldData})`
         * - `'delete'` — a red button which does `emit('delete')`
         * - `'archive'` — a red button which does `emit('archive')`
         *
         * You can also pass custom buttons with the same schema to generate forms.
         *
         * See the documentation on Action Buttons for more info.
         * @type {('edit' | 'cancel' | 'save' | 'delete' | 'archive' | Record<string, any>)[]}
         * @example ['delete', 'cancel', 'edit', 'save']
         * @example [{component: 'button', type: 'button', slot: 'log', events: {click: console.log}}]
         * @category content
         */
        actionButtons: any[]

        /**
         * You can overwrite the schema used for the default action buttons for edit, cancel, save, delete & archive.
         * @type {{ edit?: Record<string, any>, cancel?: Record<string, any>, save?: Record<string, any>, delete?: Record<string, any>, archive?: Record<string, any>, }}
         * @example {'save': {push: true}, 'delete': {color: 'secondary'}}
         * @category content
         */
        actionButtonDefaults: any

        /**
         * The position of the action buttons.
         * @type {'top' | 'bottom' | 'right' | 'left'}
         * @category content
         */
        actionButtonsPosition: string

        /**
         * A function which serves as global validator for your form. It will receive the edited data as first param and the original data (before user edits) as second. It should return true if all is OK or a string with error message.
         * @type {(newData: Record<string, any>, oldData: Record<string, any>) => (true | string)}
         * @example (newData, oldData) => newData.pass1 === newData.pass2 || 'passwords don't match'
         * @category behavior
         */
        validator: Function

        /**
         * The amount of columns the form should have. Each field can set a specific 'span' to be able to span multiple columns.
         * @type {number}
         * @category style
         */
        columnCount: number

        /**
         * The size of the gap between each field in the form.
         * @type {string}
         * @category style
         */
        gridGap: string

        /**
         * The text used in the UI for the action buttons and some error messages.
         * @type {{ archive?: string, delete?: string, cancel?: string, edit?: string, save?: string, requiredField?: string, formValidationError?: string } | EvaluatedProp<{ archive?: string, delete?: string, cancel?: string, edit?: string, save?: string, requiredField?: string, formValidationError?: string }>}
         * @example { cancel: 'キャンセル', edit: '編集', save: '保存' }
         * @category content
         */
        lang: BlitzarLang

        /**
         * The mode represents how fields are rendered
         * - `'edit'` — (default) show editable fields based on the schema
         * - `'view'` — show each field with `readonly: true`
         * - `'disabled'` — show each field with `disabled: true`
         * - `'raw'` — used to show raw data of your form (for select components, it will show the data label instead of its value)
         * - `'add'` — the same as `'edit'`
         *
         * This prop can be set on a BlitzField or on a BlitzForm (in which case it's applied to all fields).
         * @type {'edit' | 'view' | 'disabled' | 'raw' | 'add'}
         * @category state
         */
        mode: string

        /**
         * The position of the label in comparison to the field.
         *
         * This prop can be set on a BlitzField or on a BlitzForm (in which case it's applied to all fields).
         * @type {'top' | 'left'}
         * @category style
         */
        labelPosition: string | Function

        /**
         * Custom styling to be applied to the label of BlitzField. Applied like so `:style="componentStyle"`. Can be an Evaluated Prop.
         *
         * This prop can be set on a BlitzField or on a BlitzForm (in which case it's applied to all fields).
         * @type {string | Record<string, boolean> | (string | Record<string, boolean>)[] | EvaluatedProp<string | Record<string, boolean> | (string | Record<string, boolean>)[]>}
         * @example 'font-weight: 200;'
         * @category style
         */
        labelStyle: any

        /**
         * Custom classes to be applied to the label of BlitzField. Applied like so `:class="labelClasses"`. Can be an Evaluated Prop.
         *
         * This prop can be set on a BlitzField or on a BlitzForm (in which case it's applied to all fields).
         * @type {string | Record<string, boolean> | (string | Record<string, boolean>)[] | EvaluatedProp<string | Record<string, boolean> | (string | Record<string, boolean>)[]>}
         * @example ['text-h6']
         * @category style
         */
        labelClasses: any

        /**
         * An array with prop names that should be treated as Evaluated Props when passed a function.
         *
         * This prop can be set on a BlitzField or on a BlitzForm (in which case it's applied to all fields).
         * @type {string[]}
         * @category behavior
         */
        evaluatedProps: Array<string | 'component' | 'showCondition' | 'label' | 'subLabel' | 'required' | 'rules' | 'fieldStyle' | 'fieldClasses' | 'componentStyle' | 'componentClasses' | 'disable' | 'events' | 'lang'>

        /**
         * Set to true if the entire form has its own labels and you do not want the BlitzField to show a label.
         *
         * When `true` subLabels will be passed as a prop called 'hint'.
         *
         * This prop can be set on a BlitzField or on a BlitzForm (in which case it's applied to all fields).
         * @type {boolean | undefined}
         * @category behavior
         */
        internalLabels: boolean
        /**
         * Set to true if the entire form has its own error handling. This makes sure it passes on props like `rules` and does nothing with them in the BlitzField.
         *
         * This prop can be set on a BlitzField or on a BlitzForm (in which case it's applied to all fields).
         * @type {boolean | undefined}
         * @category behavior
         */
        internalErrors: boolean

        /**
         * Pass the component names (without `.vue`) that have internal error handling. This makes sure it passes on props like `rules` and does nothing with them in the BlitzField.
         * @type {string[]}
         * @category behavior
         */
        internalErrorsFor: Array<string | 'QInput' | 'QSelect' | 'QField' | 'q-input' | 'q-select' | 'q-field'>
        /**
         * The component that should be used to generate the form. Defaults to QForm. You can pass the name of a native HTML5 element or Vue component that is globally registered. You can also import the Vue file and directly pass the imported object, just like you would when you add it to a Vue file's components prop.
         * @type {string | Function}
         * @example 'form'
         * @example 'tr'
         * @example 'MyFormWrapper'
         */
        formComponent: string | Function
    }

    export interface BlitzField extends BlitzFieldProps, Vue {}

    export interface BlitzForm extends BlitzFormProps, Vue {
        fieldInput(_ref5)

        resetState()

        restoreBackup()

        tapCancel()

        validate()

        tapEdit()

        tapSave()

        tapDelete()

        tapArchive()
    }

    export const BlitzField: VueConstructor<BlitzForm>;
    export const BlitzForm: VueConstructor<BlitzFormProps>;

    // as an alias
    export interface Schema extends BlitzFieldProps {}

    /**
     * Validates a form data based on its schema
     *
     * @export
     * @param {PlainObject} formData the form data in an object that looks like: `{[fieldId: string]: any}`
     * @param {Schema} schema
     * @param {StringObject} lang the lang object with at least the key `requiredField` used as error message for required fields
     * @returns {ValidationResultForm}
     */
    export function validateFormPerSchema(formData:any , schema:Schema, lang:BlitzarLang)

    export default class Blitzar {
        static BlitzForm: BlitzForm;
        static BlitzField: BlitzField;
        static BlitzH: any;
        static BlitzListForm: any;
        static install: PluginFunction<any>;
    }
}

fix(docs): surpress initial notifications when opening docs [1.5h]

when opening https://blitzar.cycraft.co/docs/blitz-form

you see:

image

The reason is because:

  • easy form emits an input and field-input event on render (this is the behaviour I want to keep)

where to fix this issue:

might need some extra logic in the example files before triggering Notify.

pain point

The problem is that I want the least amount of code in the example files, because the users will see that code in the example source code. The more unrelated code, the more confusion this will cause.

maybe instead of doing "Notify" directly, we can create a helper "notify" function that does some extra logic outside of the actual example files.

feat: only use native HTML5 elements

  • split up the "advanced" example between one using Quasar components as is now and one with all native HTML components

remove use of quasar components throughout the other examples, in order to simplify the documentation and make it more focussed

  • use HTML5 elements in BlitzForm docs
  • use HTML5 elements in BlitzTable docs
  • use HTML5 elements for BlitzTable selection
  • use HTML5 elements for BlitzTable BlitzBtns
  • use HTML5 elements for BlitzForm BlitzBtns

Vue 3 support

I have released the official Vue 3 Blitzar! 🎉

--

  • double check wether or not the documentation of blitzar covers all use cases & features for the Vue 2 version
  • recreate the documentation in Vite + Vue 3 with all the markdown content ready.
  • start implementing all the examples of the docs one by one.
    • completely rewrite BlitzForm, BlitzTable, BlitzListForm based on this rewrite
    • keep an ongoing list of breaking changes

--
Breaking Changes For Blitzar + Vue 3

BlitzTable

BlitzTable was completely rewritten and has changed a lot.

  • Please check out the new documentation on BlitzTable and adjust accordingly.
  • Some features are not yet re-implemented like conditional/dynamic styles & classes but they will be soon

BlitzForm

renamed terminology

  • @input to @update:modelValue
    • reason: Vue 3 requirement
  • @field-input to @updateField
    • reason: update-field makes sense with Vue 3 terminology (because Vue 3 doesn't rely on @input anymore)
  • @input-cell to @updateCell
    • reason: same as above
  • fieldInput (the function) to updateField
    • reason: same as above
  • evaluatedProps to dynamicProps
    • reason: clarity — Dynamic Props is a bit easier to understand imo.
  • mode: 'view' to mode: 'readonly'
    • reason: clarity — closer to HTML5 readonly attribute

upgrade strategy for the above:

  • find and replace

Deprecated props

  • deprecate rules validation array per field
    • reason: we do not use Quasar under the hood anymore, rules was only used because Quasar's QField uses it for Validation
    • I have thought a long time about the best way of implementing Validation, looking at VeeValidate, Vuelidate, Quasar, Vuetify and others, and have realised we already have a very simple implementation of form validation via Dynamic Props... So to reduce codebase complexity, I have added a new prop called error where you can simple pass a validation function much like the rules prop before. The difference is, it's a single function (to be marked as dynamicProps: ['error']) and you make it a single function that returns null if there's no error, or the error you want to display when there is one.
    • upgrade strategy: Read the new Documentation on Validation here. Please scroll to "validation" at the bottom
// before
{
  rules: [(val) => Number(val) > 18 || 'no minors allowed']
}
// after
{
  dynamicProps: ['error'],
  error: (val) => Number(val) > 18 ? null : 'no minors allowed'
}
  • deprecate prop internalErrorsFor array of field-names

    • reason: reduce codebase complexity & delete less common options to reduce size
    • upgrade strategy: just add internalErrors: true to the fields you need this
  • deprecate prop validator global validation function

    • reason: reduce codebase complexity & delete less common options to reduce size
    • upgrade strategy: just add error validation to each field in your schema instead
  • deprecate mode: 'add'

    • reason: reduce complexity
    • upgrade strategy:
      • check if your forms rely on mode: 'add' by doing a global search for add
      • write custom logic to difference between mode: 'add' and mode: 'edit'

Replace `EasyForm` with `BlitzForm`

also file names and any places it's used in the docs.

remember, the source of truth for the EasyForm is packages/form and it's copied to several locations via npm run prepare

feat(docs): add an example of resetting the formData & input fields [1h]

  • explain that a form only rendered once at the start, and making outside changes to the formData you pass to an EasyForm does not re-render the form with the new data. (for performance reasons)

  • Explain that the only way to make a form "reactive" when making changes is to detect an outside change and increment a counter set as "key" manually. - this might result in loosing focus on field though? (needs to be checked)

  • eg. "you can reset a form visually by adding eg. <EasyForm :key="counter" /> and incrementing the counter."

  • Add an example that explains the above & have the example do this when the "save" action button is clicked, & make a mocked api call request via setTimeout and reset the form after.

can a computed field work when showCondition: false

  • Search docs for CON: you have to include this "Computed Field" in all forms the user can edit the related fields (and probably with showCondition: false)
  • in the related example try actually making the computed field showCondition: false and see if it works

feat(docs): add BlitzH documentation [2h]

I want the blitzar schema concept to be usable as a replacement for render functions: https://v3.vuejs.org/guide/render-function.html#v-on

The schema concept is really user friendly and easy to use.

But we need a more lightweight option without it necessarily wrapping a <form> and without the concept of "labels", "label position" etc.

PS: the h() of render functions stands for HyperScript

initial implementation available at https://github.com/cycraft/blitzar/blob/production/packages/form/src/components/BlitzH.vue

  • I really need to flesh out this idea
  • write some basic documentation
  • determine other tasks

docs: add something on form style responsive columns (screen width)

targeting .blitz-form__form
you can apply responsive columns (I think)
eg.

.blitz-form__form
  grid-template-columns: 1fr 1fr !important
@media (max-width: 600px)
  .blitz-form__form
    grid-template-columns: 1fr !important

need to add some example similar to this to docs.

deprecate easy-forms & tables [2h]

first finish #12, #52, #51

  • check latest easy-forms changes & compare to current state of blitzar/form

easy-forms

  • update documentation website with link to blitzar website & upgrade guide release notes
  • deprecate on github by updating the readme
  • deprecate on npm

easy-tables

  • update documentation website with link to blitzar website & upgrade guide release notes
  • deprecate on github by updating the readme
  • deprecate on npm

refactor(table): try to deprecate BlitzRow and BlitzCell and reuse logic from BlitzForm

Currently I'm replicating how BlitzForm and BlitzField behave with BlitzRow and BlitzCell. But this quickly becomes unmanageable and is very WET.

I might be able to deprecate BlitzRow and -Cell completely by providing a scoped slot in BlitzForm to which I pass the data used to render the BlitzFields.

Then in BlitzTable use BlitzForm and use the slot to render a QTr and v-for on QTd's in which I pass BlitzField nested.

Review semantics

  • make chapters title case
  • make prop names usage backticked``
  • make self-made concepts title case
  • remove periods at the end of bullet point sentences
  • add — to bullet point concepts

build: set up CI

develop on dev branch -- make dev the "main" branch

wishes for CI

  1. opening a PR to production (or even to dev) to run automated tests
  2. can only merge into production if automated tests passed
  3. automatic version increase (maybe like lerna a choice...? need to discuss)
  4. automatic release notes (based on commit messages)
  5. automatic deploy of documentation (firebase hosting, see https://firebase.googleblog.com/2020/10/preview-channels-firebase-hosting.html)
  6. automatic publish to npm
  7. automatic version added to Github releases with release notes (or link to changelog.md)
  8. automatic update of changelog.md

related

open questions

  1. Does npm workspaces help or bring more issues with package scopes?
    With lerna I always have issues with package scoping. Adding dev dependencies at root means you can't execute them in the package folder etc. Quasar project dependencies at root also caused a loot of issues when trying to build the quasar projects. Does npm workspaces bring new issues or will the fact that all packages are in a single node_modules folder at root be helpful.

  2. does npm workspaces replace lerna?
    Besides symlinking local packages to each other, lerna has the benefit of lerna deploy, which automatically increments all package versions throughout the local versions and dependencies, and automatically adds a "tag" with that version number on Github.
    Is NPM Workspaces to be used WITH lerna, or maybe we don't need lerna anymore since we're changing the whole CI pipeline anyway?

fix(docs): 1 frame of white screen when opening nav [2h]

when you look at this frame by frame you'll see there's a single frame that's completely white when the nav menu is opened.

  • need to find out why and try to fix.

could be an issue with Quasar itself.
probably css related?

https://www.dropbox.com/s/8nsvco28ahxalmg/Kapture%202020-08-27%20at%2015.26.45%202.mp4?dl=0

might be worth to try on a brand new quasar project with similar layout. if the issue is with quasar at its core lets look together if it's an easy fix or not

easy-tables → BlitzTable

  • add @blitzar/table in packages/table
  • add to packages/blitzar as well
  • convert the few documentation easy-table had into new documentation

fix issues outside of Quasar projects with `$q` not available on prototype

when $q is not available on prototype, we should provide it in order for some components not to break.

The 2 required props I've found so far are $q.dark and $q.iconSet.field

if (!('$q' in Vue.prototype)) {
  Vue.prototype['$q'] = {
    version: '1.14.7',
    config: {},
    platform: {
      userAgent:
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
      is: {
        chrome: true,
        version: '87.0.4280.88',
        versionNumber: 87,
        mac: true,
        desktop: true,
        webkit: true,
        name: 'chrome',
        platform: 'mac',
      },
      has: { touch: false, webStorage: true },
      within: { iframe: false },
    },
    dark: { isActive: false, mode: false },
    screen: {
      width: 1110,
      height: 986,
      name: 'md',
      sizes: { sm: 600, md: 1024, lg: 1440, xl: 1920 },
      lt: { sm: false, md: false, lg: true, xl: true },
      gt: { xs: true, sm: true, md: false, lg: false },
      xs: false,
      sm: false,
      md: true,
      lg: false,
      xl: false,
    },
    lang: {
      isoName: 'en-us',
      nativeName: 'English (US)',
      label: {
        clear: 'Clear',
        ok: 'OK',
        cancel: 'Cancel',
        close: 'Close',
        set: 'Set',
        select: 'Select',
        reset: 'Reset',
        remove: 'Remove',
        update: 'Update',
        create: 'Create',
        search: 'Search',
        filter: 'Filter',
        refresh: 'Refresh',
      },
      date: {
        days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
        daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
        months: [
          'January',
          'February',
          'March',
          'April',
          'May',
          'June',
          'July',
          'August',
          'September',
          'October',
          'November',
          'December',
        ],
        monthsShort: [
          'Jan',
          'Feb',
          'Mar',
          'Apr',
          'May',
          'Jun',
          'Jul',
          'Aug',
          'Sep',
          'Oct',
          'Nov',
          'Dec',
        ],
        firstDayOfWeek: 0,
        format24h: false,
        pluralDay: 'days',
      },
      table: {
        noData: 'No data available',
        noResults: 'No matching records found',
        loading: 'Loading...',
        recordsPerPage: 'Records per page:',
        allRows: 'All',
        columns: 'Columns',
      },
      editor: {
        url: 'URL',
        bold: 'Bold',
        italic: 'Italic',
        strikethrough: 'Strikethrough',
        underline: 'Underline',
        unorderedList: 'Unordered List',
        orderedList: 'Ordered List',
        subscript: 'Subscript',
        superscript: 'Superscript',
        hyperlink: 'Hyperlink',
        toggleFullscreen: 'Toggle Fullscreen',
        quote: 'Quote',
        left: 'Left align',
        center: 'Center align',
        right: 'Right align',
        justify: 'Justify align',
        print: 'Print',
        outdent: 'Decrease indentation',
        indent: 'Increase indentation',
        removeFormat: 'Remove formatting',
        formatting: 'Formatting',
        fontSize: 'Font Size',
        align: 'Align',
        hr: 'Insert Horizontal Rule',
        undo: 'Undo',
        redo: 'Redo',
        heading1: 'Heading 1',
        heading2: 'Heading 2',
        heading3: 'Heading 3',
        heading4: 'Heading 4',
        heading5: 'Heading 5',
        heading6: 'Heading 6',
        paragraph: 'Paragraph',
        code: 'Code',
        size1: 'Very small',
        size2: 'A bit small',
        size3: 'Normal',
        size4: 'Medium-large',
        size5: 'Big',
        size6: 'Very big',
        size7: 'Maximum',
        defaultFont: 'Default Font',
        viewSource: 'View Source',
      },
      tree: { noNodes: 'No nodes available', noResults: 'No matching nodes found' },
      rtl: false,
    },
    iconSet: {
      name: 'material-icons',
      type: {
        positive: 'check_circle',
        negative: 'warning',
        info: 'info',
        warning: 'priority_high',
      },
      arrow: {
        up: 'arrow_upward',
        right: 'arrow_forward',
        down: 'arrow_downward',
        left: 'arrow_back',
        dropdown: 'arrow_drop_down',
      },
      chevron: { left: 'chevron_left', right: 'chevron_right' },
      colorPicker: { spectrum: 'gradient', tune: 'tune', palette: 'style' },
      pullToRefresh: { icon: 'refresh' },
      carousel: {
        left: 'chevron_left',
        right: 'chevron_right',
        up: 'keyboard_arrow_up',
        down: 'keyboard_arrow_down',
        navigationIcon: 'lens',
      },
      chip: { remove: 'cancel', selected: 'check' },
      datetime: {
        arrowLeft: 'chevron_left',
        arrowRight: 'chevron_right',
        now: 'access_time',
        today: 'today',
      },
      editor: {
        bold: 'format_bold',
        italic: 'format_italic',
        strikethrough: 'strikethrough_s',
        underline: 'format_underlined',
        unorderedList: 'format_list_bulleted',
        orderedList: 'format_list_numbered',
        subscript: 'vertical_align_bottom',
        superscript: 'vertical_align_top',
        hyperlink: 'link',
        toggleFullscreen: 'fullscreen',
        quote: 'format_quote',
        left: 'format_align_left',
        center: 'format_align_center',
        right: 'format_align_right',
        justify: 'format_align_justify',
        print: 'print',
        outdent: 'format_indent_decrease',
        indent: 'format_indent_increase',
        removeFormat: 'format_clear',
        formatting: 'text_format',
        fontSize: 'format_size',
        align: 'format_align_left',
        hr: 'remove',
        undo: 'undo',
        redo: 'redo',
        heading: 'format_size',
        code: 'code',
        size: 'format_size',
        font: 'font_download',
        viewSource: 'code',
      },
      expansionItem: { icon: 'keyboard_arrow_down', denseIcon: 'arrow_drop_down' },
      fab: { icon: 'add', activeIcon: 'close' },
      field: {
        clear: 'cancel',
        error: `img:data:image/svg+xml;charset=utf8,<svg xmlns="http://www.w3.org/2000/svg" style="color:rgb(222,55,55);" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M10 7v4"/><circle cx="10" cy="14" r="1" fill="currentColor"/><circle cx="10" cy="10" r="7" stroke="currentColor" stroke-width="2"/></g></svg>`,
      },
      pagination: {
        first: 'first_page',
        prev: 'keyboard_arrow_left',
        next: 'keyboard_arrow_right',
        last: 'last_page',
      },
      rating: { icon: 'grade' },
      stepper: { done: 'check', active: 'edit', error: 'warning' },
      tabs: {
        left: 'chevron_left',
        right: 'chevron_right',
        up: 'keyboard_arrow_up',
        down: 'keyboard_arrow_down',
      },
      table: {
        arrowUp: 'arrow_upward',
        warning: 'warning',
        firstPage: 'first_page',
        prevPage: 'chevron_left',
        nextPage: 'chevron_right',
        lastPage: 'last_page',
      },
      tree: { icon: 'play_arrow' },
      uploader: {
        done: 'done',
        clear: 'clear',
        add: 'add_box',
        upload: 'cloud_upload',
        removeQueue: 'clear_all',
        removeUploaded: 'done_all',
      },
    },
  }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.