cycraft / blitzar Goto Github PK
View Code? Open in Web Editor NEWGenerate Vue Forms and Data-tables fast with a simple JSON-like syntax ⚡
Home Page: https://blitzar.cycraft.co
License: MIT License
Generate Vue Forms and Data-tables fast with a simple JSON-like syntax ⚡
Home Page: https://blitzar.cycraft.co
License: MIT License
inspired by
{
id: "items",
component: "ol",
span: true,
evaluatedProps: ["showCondition", "slot"],
showCondition: (val: any[]) => isFullArray(val),
defaultValue: () => [],
slot: (val: any[]) =>
val.map(name => ({
component: "li",
slot: name,
}))
},
when opening https://blitzar.cycraft.co/docs/blitz-form
you see:
The reason is because:
input
and field-input
event on render (this is the behaviour I want to keep)might need some extra logic in the example files before triggering Notify
.
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.
CON: you have to include this "Computed Field" in all forms the user can edit the related fields (and probably with showCondition: false)
showCondition: false
and see if it worksI 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
a doc page with some examples might be nice
and an api card at the bottom
besides newData
and oldData
I should also emit formData
a separate doc page ?
or just the api card ?
add these
maxRows
propI have released the official Vue 3 Blitzar! 🎉
--
--
Breaking Changes For Blitzar + Vue 3
BlitzTable was completely rewritten and has changed a lot.
@input
to @update:modelValue
@field-input
to @updateField
update-field
makes sense with Vue 3 terminology (because Vue 3 doesn't rely on @input
anymore)@input-cell
to @updateCell
fieldInput
(the function) to updateField
evaluatedProps
to dynamicProps
mode: 'view'
to mode: 'readonly'
upgrade strategy for the above:
rules
validation array per field
rules
was only used because Quasar's QField uses it for Validationerror
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.// 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
internalErrors: true
to the fields you need thisdeprecate prop validator
global validation function
deprecate mode: 'add'
mode: 'add'
by doing a global search for add
mode: 'add'
and mode: 'edit'
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
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',
},
},
}
}
should add example styling css for blitzlistform
eg. make a nice list with lines in between?
Currently the classes to target in a table are a mess
headerClasses
and having the dev target that seems like a good option.We also need one for footerClasses
but that doesn't exist in Quasar yet.
footerClasses
: quasarframework/quasar#7723npm run dev:docs
go to docs
need to use 2 components of planetar on this page:
These two components need to be developed inside Planetar and then integrated into the Blitzar documentation website.
Finilise the release draft:
https://github.com/CyCraft/blitzar/releases/tag/untagged-818284fa88bc48928931
Blitzar release! see blog post here: ... see documentation here: ...
upgrade guide from easy-forms
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.
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
remove use of quasar components throughout the other examples, in order to simplify the documentation and make it more focussed
packages/table
packages/blitzar
as wellpropName: 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"
develop on dev
branch -- make dev
the "main" branch
production
(or even to dev
) to run automated testsproduction
if automated tests passedDoes 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.
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?
Need to write something on how BlitzField uses grid and how it can be changed with CSS
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.
Change the "advanced example" in basics to only use HTML5 elements.
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.
I don't mind the dep:
scripts, but the others are currently getting out of hand...
I just need these scripts:
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:
/packages/form
changes, run something similar to the currently set up prepare:form
/packages/table
changes, run something similar to the currently set up prepare:table
/packages/utils
changes, execute npm run build
in that folderThe 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",
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>;
}
}
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.
Hello,
how would I put a validation message under a quasar input field in a specific row? For example a second row.
Thanks.
easy-forms
easy-tables
currently using a regular "input" as "component" will render an <input />
but the events are not working properly.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.