@rainbow-d9/n3 v1.2.31
d9-n3
It is the No.3 project of group d9.
This project is Markdown engine, parse to JSON configuration, which used to do rendering.
Idea
The purpose of d9-n3 is to provide a Markdown-based approach that allows non-programmers to easily participate in the process of page
design and creation to a great extent, without the assistance of programmers. Additionally, due to the easily storable and comparable
structure of Markdown, this approach also brings convenience in terms of page management.
Add Into Your Project
yarn add @rainbow-d9/n3Parse
Parsing Markdown involves three main steps: pre-parsing of Markdown content, syntax parsing, and creation and parsing of widget configurations.
Preparse
The pre-parsing of Markdown content uses mdast for parsing. It mainly processes the text content into a structured format and
builds the structure tree of the given document based on the hierarchy of headings. It is worth noting that according to the parsing result
of mdast, all headings (if the syntax follows the normal writing conventions, as Markdown has loose syntax requirements) are placed under
the root node and are not organized into a tree structure. Therefore, the pre-parsing process reorganizes the headings based on their
hierarchy, with N-level headings appearing as child nodes under the nearest preceding N-1 level heading.
Semantic Parse
The semantic parsing will parse headings and list items into components, following the following parsing rules:
- Headings, in the format of
WidgetType[::Headline][::$Id]. - If no text definition is provided, it is considered a reserved heading, - If the text ends with::IGNOREor::EXPORT, it is considered a reserved heading., - If no widget type is detected, it is considered a reserved heading. - List Items, content can be defined as a component or property definition.
- If the first child element of the list item in Markdown syntax is not text, it is considered a reserved item.
- If the first child element is not on the same line as the list item, it is considered a reserved item.
- If there is no text definition on the same line, it is considered a reserved item.
- If it starts with
REF.,Ref., orref., it is considered a reference to an external component. - If the text format isWidgetType::[Label[::PropertyPath]], it is considered a component. - If it is in the formatx: y, it is considered a property definition. - Others are considered a property group definition, specifically used for defining boolean properties.
Reserved heading and list item currently not supported in the runtime.
Reference list item currently not supported in the runtime.
Creation and Parsing of Widget Configurations
After the pre-parsing and syntax parsing stages, we have obtained a complete syntax tree. The remaining task is to continue parsing and
creating configuration data that can be used for rendering with the d9 core.
Before explaining the final step, an important concept must be clarified: the parsing of Markdown syntax has no direct physical
relationship with the actual widget library selection. However, logically, all widget types parsed in the previous
process must be supported in the final rendering process. In particular, the parsing of specific widget properties, due to the uniqueness
of each individual widget (each widget may have its own unique properties and corresponding values), requires support from a
corresponding widget parsing implementation if we are rendering based on a specific widget library. However, d9-n3 already has
standard support for standard properties, and if there are no specific properties that need support (which is highly unlikely in the real
world), there is essentially no need for additional widget-level plugin support.
All discussions below are based on the parsing support for the d9-n2 widget library that comes with d9-n3. If you are using a
different widget library or a mixed scenario, you can also customize and register your own widget parsing by understanding the
following instructions and referring to the relevant source code of d9-n3.
Referencing the implementation of SemanticHelper#classifyParsedHeadings, Markdown allows multiple top-level nodes. Currently, only nodes
with a widget type of Page are considered renderable widgets. In the absence of any Page node definitions, only nodes defined as
Export will be considered renderable widgets. In the final parsing process, only the first node considered renderable will be selected
for further parsing and ultimately rendered.
Let's take a look at a very simple example,
# Page
- Input::Name::name
# Page
[//]: # (something else)In the above definition, only the first Page node will be considered for rendering, and the second one will be ignored. Therefore, in
actual practice, we recommend defining one page per Markdown and not merging them together.
Regarding the inclusion of multiple widgets in a Markdown file, reserved headings, reserved list items, and related support, we will plan for it in future versions. Currently, it can be understood as just a placeholder to support flexibility in definitions.
After the initial demonstration, we have learned how to define a page using a Markdown file. Next, we will see a more complex example as follows:
# Page::Policy Info
## Section::Policy Info
- Input::Company Code::companyCode
- length: 2;
- disabled
- Input::Prefix::symbol
- length: 3;
- disabled
- Input::Policy No.::policyNo
- disabled
- length: 6;
- Input::Suffix::renewalRewriteVer
- length: 1..2;
- disabled
- Dropdown::Country::countryCode
- options:
- 183: NZ
- Input::Insured Name::insuredName
- length: 0..128;
- Input::Insured Alias Name::insuredAliasName
- length: 0..40;
- Input::Holder Contact Phone::holderContactPhone
- length: 0..15;
- Input::Endorsement No.::endoNo
- length: 3;
- disabled
- Input::Endorsement Cancel Ver::endoCancelVer
- length: 1..2;
- disabled
- Input::Ccvb No.::ccvb
- disabled
- Dropdown::Transaction Type::transactionType
- options:
- AP: New Business
- CP: Cancel Policy Pro-rata
- MP: Endorsement
- RENEW: Renew
- REIN: Reinstatement
- Date::Trans Date::transDate
- Date::Application Date::applicationDate
- Dropdown::Status::status
- options:
- A: Active
- C: Cancelled
- Input::Previous Status::previousStatus
- length: 1;
- Date::Policy Effective Date::effectiveDate
- Date::Policy Expiry Date::expiryDate
- Input::Terms::terms
- length: 1..3;
- integer: Must be an integer.
- Date::Origin Issue Date::originIssueDate
- Date::Origin Effective Date::originEffectiveDate
- Date::Endorsement Effective Date::endoEffectiveDate
- Date::Cancel Date::cancelDate
- Date::Cancel Process Date::cancelProcessDate
- Input::Cancel Reason Code::cancelReasonCode
- length: 2;
- Date::Reinstate Process Date::reinstateProcessDate
- Dropdown::Reinstate Type::reinstateType
- options:
- P: P
- Dropdown::Branch::branchCode
- options:
- 01: 01
- 02: 02
- Input::Underwriter Code::underwriterCode
- length: 0..10;
- Input::Underwriter Origin::underwriterOrigin
- length: 0..10;
- Dropdown::Broker Company Code::agentCode
- options: @ext.codes.mdBrokerCompanyOptions
- sort: asc
- Input::Broker Name Code::brokerCode
- length: 0..6;
- Number::Commission Percentage::commissionPercentage
- notNegative: Must be not negative.
- numeric: Must be numeric.
- Dropdown::Account Type Code::accountTypeCode
- options:
- A: A
- C: C
- B: B
- Y: Y
- Input::Source Code::sourceCode
- length: 1..3;
- Input::Production Source Code::productionSourceCode
- length: 1..3;
- Dropdown::Coinsurance Indicator::coinsuranceIndicator
- options:
- N: N (Non-Coinsurance)
- L: L (Co-AIG as Leader)
- Y: Y (Co-AIG as Follower)
- Dropdown::Renewal Flag::renewalFlag
- options:
- 0: N
- 1: Y
- Dropdown::Renewal Code::renewalCode
- options:
- 00: 00
- 01: 01
- 02: 02
- 03: 03
- 04: 04
- 05: 05
- 06: 06
- 07: 07
- 08: 08
- 09: 09
- 10: 10
- Dropdown::Renewal Indicator::renewalIndicator
- options:
- N: N
- Y: Y
- Input::Assume Business Indicator::assumeBusinessIndicator
- length: 1;
- Dropdown::Inward RI Company Code::inwardRICode
- options: @ext.codes.mdInwardRiCompanyOptions
- sort: asc
- Dropdown::Assumed Type::agentSubproducerCode
- options:
- NRI: NRI
- IRI: IRI
- Dropdown::Billing Code::billingCode
- Options:
- AC:AC
- DB:DB
- AR:AR
- Input::Pay Plan Code::payPlanCode
- length:2
- Dropdown::Currency Code::currencyCode
- options:
- 108: 108
- Input::Quote Currency Code::quoteCurrencyCode
- length: 3;
- Dropdown::Deductible Costs Flag::deductibleCostsFlag
- options:
- E: E
- I: I
- N: N
- Dropdown::Rated Type::ratedType
- options:
- M: M
- A: A
- H: H
- P: P
- Number::Short Rate Percentage::shortRatePercentage
- numeric: Must be numeric.
- notNegative: Must be not negative.
- Date::Reinstatement Date::reinstatementDate
- Input::Renewal Certificate Number::renewalCertificateNumber
- length: 8;
- Dropdown::Audit Frequency::auditFrequency
- options:
- A: A
- S: S
- M: M
- Dropdown::Personal Package Policy Status::personalPackagePolicyStatus
- options:
- A: A
- H: H
- B: B
- Input::Stateside Collection Indicator::statesideCollectionIndicator
- length: 0..1;
- Dropdown::MM Maker Type::mmMakerType
- options:
- MJ: MJ
- MM: MM
- M: M
- Checkbox::eDit Flag::eDitFlag
- values: Y, N
- Input::Request Revision No.::requestRevisionNo
- Input::Lae Method Flag::laeMethodFlag
- length: 1;
- Dropdown::Prefix Segment Code::prefixSegmentCode
- options:
- C: C
- S: S
- T: T
- A: A
- O: O
- N: N
- 0: 0
- Dropdown::Waats Gds Reference No.::waatsGdsReferenceNo
- options:
- 0: 0
- NZD: NZD
- ANZ: ANZ
- FRA: FRA
- Input::Document Note::documentNote
- disabled
- Dropdown::Print Renewal Notice::printRenewalNotice
- options:
- Y: Y
- Date::Error Date::errorDate
- Input::Form Number::formNumber
- length: 0..5;
- Dropdown::Region Field::regionField
- options:
- N: N
- Input::Renewal Extract::renewalExtract
- length: 0..1;
- Checkbox::Has End::hasEnd
- values: Y, N
- Input::Filler Field::fillerField
- place: 12
- length: 0..65;
- Input::Manual Modify::manualModify
- disabledThe above is a fairly comprehensive example used to render a page for inputting basic insurance information. We don't need to focus on the specific content here; we are interested in understanding how to define it quickly. Let’s select a representative section from it for a detailed observation.
# Page::Policy Info
## Section::Policy Info
- Input::Company Code::companyCode
- length: 2;
- disabled
- Dropdown::Country::countryCode
- options:
- 183: NZ
- Checkbox::Has End::hasEnd
- values: Y, N
- Input::Filler Field::fillerField
- place: 12
- length: 0..65;# Page::Policy Info: This represents a page,## Section::Policy Info: This is a Section, and it is titledPolicy Info,- Input::Company Code::companyCode: This is an input, and it is titledCompany Code, bind two-way with the model propertycompanyCode,- length: 2;: The length ofCompany Codemust be 2 characters. Custom validation exception messages can be written after the semicolon, but it is not specified in this case, so the standard message will be used,- disabled:Company Codecannot be edited or modified,- Dropdown::Country::countryCode: This is a dropdown, and it is titledCountry, bind two-way with the model propertycountryCode,- options:,- 183: NZ: This dropdown only has one option, with a value of183and a label ofNZ,- Checkbox::Has End::hasEnd: This is a checkbox, and it is titledHas End, bind two-way with the model propertyhasEnd,- values: Y, N: The value of this checkbox isYandN,- Input::Filler Field::fillerField: This is an input, and it is titledFiller Field, bind two-way with the model propertyfillerField,- place: 12: Occupying 12 columns, since theSectionuses a 12-column layout, this input field actually spans the entire row,- length: 0..65;: Similar to the length limit mentioned above, here it is defined that 0 to 65 characters are all valid.
Once the above Markdown is defined, simply invoke the parseDoc function to obtain the parsed configuration, which can then be used for
rendering, as shown below:
// Assuming that content is a markdown text definition that satisfies the syntax requirements,
const {node: def} = parseDoc(content);
return <StandaloneRoot $root={model} {...def} />;Built-in d9-n2 Widgets Parser
All the built-in d9-n2 widgets parsing support can be found in the /src/lib/n2 directory. Before using the d9-n2 widget parsing,
need to call the registerN2Widgets method to register all the parsers. All parsers need to
implement SpecificWidgetTranslator. If it is an array widget, please implement SpecificArrayWidgetTranslator. d9-n3 already
provides a variety of basic parsers for customizing new parsers, and you can observe the specific implementation through the source code.
After writing your custom parsers, you also need to provide a function similar to registerN2Widgets to register your parser
into the parser repository.
Syntax of Markdown
When defining a page, the most important aspect is attribute definition. This section will focus on the attribute definition part and provide comprehensive documentation to facilitate quick learning for users.
Property or attribute have the same meaning in this chapter and are no longer distinguished.
Heading
Syntax: WidgetType[::Headline[::PropertyPath[::Id]]].
Connect with ::,
- If there is no
::, only the widget type is present, - If there are two parts, it includes widget type and headline,
- If there are three parts, it includes widget type, headline and property path,
- If there are more than three parts,
- The last section represents the id,
- The second to last section represents property path,
- The first section represents the widget type,
- Rest sections are reconnected with
::to represent the headline.
Some examples:
# Page
## Section::Basic Info
### Table::Order Items::orderItems
- property: itemsIt is important to note that the last section of the heading is not a property path, so an additional property path definition is needed through the list item approach.
If heading ends with
::EXPORT, means it is exported from this markdown configuration, the first one will be rendered if there is noPagepresent.
If heading ends with::IGNORE, means it is ignored in rendering, which can be used to annotate exclusions that are not meant to be removed from the entire configuration.Except for the root node, the content of the
headlinewill be used as thelabelattribute.
List Item
If list item ends with
::IGNORE, means it is ignored in rendering, which can be used to annotate exclusions that are not meant to be removed from the entire configuration.
Widget
Syntax: WidgetType::[Label[::PropertyPath]].
Connect with ::,
- If there is no
::, it will not be recognized as a widget, - If there are two parts, it includes widget type and label (could be empty or blank),
- If there are more than three parts, the last section represents the property path, the first section represents the widget type. All the
middle sections are reconnected with
::to represent the headline.
Some examples:
- Input::Name::name
- Dropdown::
- property: gender
- Dropdown::will be parsed to two parts, which presents widget type and an empty label.
Reference Widget
Syntax: REF.Id or Ref.Id or ref.Id.
Reserved, currently not supported in runtime.
Attribute
Syntax: X:y.
Single attribute definition. Additionally, this list item node can contain child lists, depending on the implementation of the parser.
Some examples:
- property: name
- options: @ext.codes.hasEndOptions
- options:
- A: A
- C: CIn the examples above, all property definitions are written at the top level. However, in actual definitions, properties are always associated with a component and therefore cannot be written at the top level.
Attributes
Syntax: a[, b[, !c]].
To define multiple properties with boolean values, connected by commas. To represent a false value, simply prefix it with an exclamation mark.
Some examples:
- disabled
- disabled, !visibleIn the examples above, all property definitions are written at the top level. However, in actual definitions, properties are always associated with a component and therefore cannot be written at the top level.
Position
Syntax:
place,position,posand$poscan all be used as attribute names, and they have the same meaning,place: columns,place: row, column,place: row, column, columns,place: row, column, columns, rows,place: [c|$c|col|$col|column|$column: column][, r|$r|row|$row: row][, cols|$cols|columns|$columns: columns][, rows|$rows: rows].
Some examples:
- place: 12
- position: 2, 4
- pos: 2, 4, 6
- $pos: 2, 4, 6, 3
- place: c: 4, r: 2, cols: 6, rows: 3
- place: $c: 4, $r: 2, $cols: 6, $rows: 3
- $mpos: 2, 4, 6, 3
- mpos: 2, 4, 6, 3
$mpos,mposis position for mobile only.
Render On Specific Devices
Specify specific widgets to render only on particular devices using $renderOn or renderOn. The valid values
are desktop, mobile, tablet, and touchable. You can also specify multiple devices by connecting values with , or ;. It's
important to note that if no rendering device filter is applied, the component won't render at all, and thus, won't have any event
listeners. If no device is specified, it will be assumed to render on all devices.
data- Attribute
Attributes starting with data- are standard HTML DOM attributes. For these attributes, the following enhancements apply:
- First, standard attributes like boolean values, numeric values, etc.
- If it's a string type, check if it starts with
$pp.. If it does, consider it needs to read a value from the model. If not, consider it as aDataAttributeCalculatorfunction. - Otherwise, use the original value.
It's important to note that data- attributes can be reused, for example, when a component is wrapped by a form cell. In such usage
scenarios, if a data- attribute is associated with its own property value changes, it can only be perceived by the component itself,
rather than directly by the form cell. Therefore, it's necessary to combine it with the repaint definition to enable the entire form cell
to perceive it.
Tips
The built-in tip can be easily activated by adding some attributes to the component. The following attributes are necessary:
data-tip-body: Essential for activating the tip. If it has no value, the tip will not be activated.data-tip-title: Title of the tip.data-tip-min-width: Minimum width.data-tip-max-width: Maximum width.data-tip-max-height: Maximum height.data-tip-delay: Display duration in seconds.data-tip-tag: Custom tip DOM attribute name, used for customizing tip styles.
Most complex components do not support the tip feature, as there isn't actually much necessity for it. If additional support is needed, you can use a Box to wrap around the component.
Components with pop-up layers, such as Dropdowns and Calendars, although supported, are prone to conflict with Popups, so it's not recommended to use them. If you must use them, please use them with a delay to avoid overlapping issues.
For decorator components, the tip feature utilizes the
data-di-*attributes and also supportsdata-*, but it's not recommended to use them simultaneously.
Attribute Guard
Any attributes that are not captured by a specific parser will be eventually parsed by the attribute guard. The attribute guard follows the following principles for parsing:
- If the attribute value is empty, it is considered as an empty string.
- If it is any of
True,true,T,t,Yes,yes,Y,y, it is considered as true. - If it is any of
False,false,F,f,No,no,N,n, it is considered as false. - Attempt to convert it to a numeric type, if successful, it is converted to a numeric value; if failed, the string value is retained.
Therefore, if the value of a component attribute conforms to the above rules, there is no need to provide additional parsers.
Some examples:
- property: name
- disabled: yesExternal Definition
Any attribute value starting with @ext. will be translated as requiring an external definition.
Some examples:
- options: @ext.codes.hasEndOptionsIn the examples above, using
codes.hasEndOptionsto find external definitions, which passed toStandardRoot.Unless otherwise specified, in markdown, the
@ext.syntax is only allowed when declaring properties in the first level under a component. Many properties of components are complex and require multi-level description. In such cases, the@ext.syntax is generally not supported.
Form Cell
$fc is a boolean attribute. When this attribute is defined (typically with a value of true), the current widget will have
the .FC suffix added, indicating that it is included within the component represented by FC. Note that if a widget
already has a label defined, it will by default have the .FC suffix added. Unless the widget parser declares that it should not be
included, please refer to the SpecificWidgetTranslator#shouldWrapByFormCell implementation.
Form cell introduces the concept of label, which is generally a static string and can be defined using - label: SomeText. In complex
cases, component combinations can be defined using the following approach, as an example, we will take the input box:
labelin headline,- Input::A Label::namelabelin attribute,- Input::::name - label: A Label- Use a
Captionaslabel, in this case, label is same as input text,- Input::::name - label: - valueOnLabel - property: name - Use any widget as
label,- Input::::name - label: Input - property: name
Please be aware that label can be an attribute of widget itself, in this case,
use SpecificWidgetTranslator#shouldTranslateLabelAttribute to avoid default parsing behavior.
Built-in Validation Properties
required: boolean,numeric: boolean,integer: boolean,positive: boolean,notNegative: boolean,length: syntax as below, -length: number: presents fix length, -length: number..: presents minimum length, -length: ..number: presents maximum length, -length: number..number: presents both minimum and maximum length, - all above syntax, connected by,, - no negative value accepted.numberRange: : syntax as below, -numberRange: [min..max]: presents a range, - allow given minimum value by[, or ignore it. Or exclude given minimum value by(, - allow given maximum value by], or ignore it. Or exclude given maximum value by), - minimum/maximum value can be ignored, but at least one should be present, - all above syntax, connected by,,regexorregexp: syntax as below, -regex: ^\d+$: presents a regex pattern, - multiple patterns connected by,.
Some examples:
- required, numeric, positive
- integer, notNegative
- length: 5
- length: 1..5
- length: ..5
- length: 3..
- length: 8, 11
- length: 5..8, 11..20All built-in validation properties can use tailing ; message to identify the customization message. For boolean attribute, string value
should be treated as customization message.
Some examples:
- required: Name is required.
- length: 5; Name should be 5 characters.Built-in validation attribute must be declared in customized
SpecificWidgetTranslator, otherwise it will not take effect.
Name Mapping
The names of certain attributes in actual configurations may start with a $, or they may be abbreviated or use shorthand
forms. These names can be confusing to read when configuring in Markdown. Therefore, d9-n3 provides a mechanism for attribute name
mapping, which consists of the following parts:
- Widget scoped: by
WidgetType.nameregister, - Global: by
nameregister, - Built-in.
Some examples:
export class N2DropdownTranslator extends SpecificWidgetTranslator<N2WidgetType.DROPDOWN> {
// declare sort is mapping to optionSort, for dropdown only
// in markdown, eg. - sort: asc
public getAttributeNamesMapping(): Undefinable<Record<CustomAttributeName, WidgetPropertyName>> {
return {'Dropdown.sort': 'optionSort'};
}
}
// declare sort is mapping to optionSort, for all widgets
AttributeNameUtils.register({'sort': 'optionSort'});Built-in name mapping as below,
| Name in Markdown | Name in Definition |
|---|---|
| disabled | $disabled |
| visible | $visible |
| validate | $valid |
| watch | $reaction |
| property | $pp |
| place | $pos |
| position | $pos |
| pos | $pos |
d9-n2 Widgets Support
Common
| Attribute Name | Type | Need Declare by Widget Parser? | Description |
|---|---|---|---|
| property, $pp | property path | No | - property: name- property: customer.name |
| disabled, $disabled | boolean | No | - disabled |
| visible, $visible | boolean | No | - visible: false |
| validate, $valid | various | No | |
| validateScopes, $validationScopes | text | No | - validateScopes: s1, s2Define the applicable validation scopes. |
| watch, $reaction | various | No | |
| place, position, pos, $pos | various | No | Refer to Position |
| $fc | boolean | No | - $fcForce current widget wrapped by a form cell. |
| label | text | No | - Section::Customer- label: CustomerWorks when widget is wrapped by form cell. |
| holdPositionWhenInvisible | boolean | No | Hold position even widget is invisible, for form cell. |
| required | boolean | Yes | - required- required: This field is mandantory. |
| numeric | boolean | Yes | - numeric- numeric: This field should be a number. |
| integer | boolean | Yes | - integer- integer: This field should be an integer. |
| positive | boolean | Yes | - positive- positive: This field should be a positive number. |
| notNegative | boolean | Yes | - notNegative- notNegative: This field should be a non-negative number. |
| length | number range | Yes | - length: 5- length: 5..10, etc. |
| regex,regexp | regexp | Yes | - regex: ^\d{5}$, etc. |
Disablement and Visibility
For components that support responsive availability and visibility, besides directly using boolean values, $disabled and $visible
provide more complex definition approaches as follows:
- Checkbox::Allowed to watch PG-13 rated films.::allowPG13
- disabled:
- on: name, age
- handle: `(model.name ?? '').trim().length === 0 || parseInt(model.age ?? 0) <= 12`The above syntax indicates that if the name is not filled in or the age is less than or equal to 12, the current component will be
disabled.
handlecan be defined using a JavaScript block to enable syntax highlighting. It also supports the@ext.syntax.It is important to note that for
disabledandvisible, thehandledefinition will also be called as a default value calculation. Therefore, please note that within thehandlefunction body, only theroot,model, andvaluearguments are applicable.
Validation
In addition to the built-in validation rules, you can also customize validation rules in the same way as Disablement and Visibility.
However, Validation can choose not to listen to any other property changes. If defined, it will be applied to all validation rules.
Validationcan also be defined with only the listening properties without defining any rules.
Here is a simple example:
- Input::Property D::propD
- required
- validate:
- on: propA
- handle:
```javascript
if (VUtils.isBlank(model.propA)) {
return value === 'blank' ? {valid: true}: {valid:false, failReason: 'A is blank, D should be "blank".'};
} else if (VUtils.isNumber(model.propA).test) {
return value === 'number' ? {valid: true}: {valid:false, failReason: 'A is number, D should be "number".'};
} else {
return value === 'string' ? {valid: true}: {valid:false, failReason: 'A is string, D should be "string"'};
}
```The above definition means that property D is required and its value must have a certain mapping relationship with the type of
property A.
Reaction
Similar to the Disablement, Visibility, and Validation mentioned above, the Reaction can also be customized. All the syntax rules
for Reaction are consistent with these three basic attributes. However, Reaction provides multiple writing styles, including the
possibility of having special Reaction keywords for components. Here are several common Reaction keywords that all components have:
repaint: Refreshes itself when changes are detected.clearMe: Clears its value when changes are detected and then refreshes itself.watch: Performs custom operations when changes are detected.
It is important to note that watch must have a defined handler, but it is not required to have a return value. By default, it
uses repaint as the standard behavior. As for repaint and clearMe, since they already have standard behaviors, in most cases, it is
only necessary to define the listeners without the need to define a handle.
Here is a simple example:
- Input::Property D::propD
- required
- repaint:
- on: propA
- Input::Property E::propE
- required
- clearMe:
- on: propA
- Input::Property F::propF
- required
- watch:
- on: propA
- handle:
```javascript
model.propF = model.propA;
return 'repaint';
```Additionally, if using watch, the return can also specify changes in attribute values, so that the component can actively initiate
attribute value change events. This is typically used when it is necessary to notify other components after the response itself has made
certain changes to the data model.
Here is a simple example:
- Input::Property G::propG
- watch:
- on: propG
- handle:
```javascript
const oldValue = model.propH;
model.propH = model.propG;
// path must be absolute
return ['value-changed', {path: '/propH', from: oldValue, to: model.propH}];
```Internationalization
Use $d9n2.intl.labels to define the internationalization string package. For example, you can add $d9n2.intl.labels.zh to define a
Chinese package. After the definition is completed, use the following command to notify the language switch:
const {fire} = useGlobalEventBus();
const onZhClicked = () => {
$d9n2.intl.language = 'zh';
fire(GlobalEventTypes.LANGUAGE_CHANGED, 'zh');
};Please note that the language package
$d9n2.intl.labels['en-US']already exists and can be overwritten to override the built-in string package.
Page
Strictly adhere to the heading parsing rules without any additional attribute definitions.
Section
- Default Wrapped by Form Cell:
false, - Default Grid Column Span:
12.
| Attribute Name | Type | Description |
|---|---|---|
| label, title | text | - Section::Customer- title: Customer |
| collapsible | boolean | Default false. |
| collapsed | boolean | Default false, only works when collapsible is true. |
labelandtitleattribute follows thelabeldefault parsing behavior.
Box
A container, default use flex layout.
Tabs
- Default Wrapped by Form Cell:
false, - Default Grid Column Span:
12.
| Attribute Name | Type | Description |
|---|---|---|
| initActive | number, text | Number represents tab index (starts from 0), text represents tab marker. |
Tab
| Attribute Name | Type | Description |
|---|---|---|
| title | text | Tab title. |
| marker | text | Tab marker, global unique. |
| badge | text | Tab title badge. |
| body | function | |
| data | function |
titleandbadgeattribute follows thelabeldefault parsing behavior.
The configuration of tab content is the same as Section, except follows,
- No
titleattribute, which has already been used in tab title, - No
labelattribute.
Ignore all child node definitions when
bodypresents.
dataattribute is used to retrieve the data of the tab content, simply set data into tab model and widget will refresh automatically.
Wizard
- Default Wrapped by Form Cell:
false, - Default Grid Column Span:
12.
| Attribute Name | Type | Description |
|---|---|---|
| balloon | boolean | Use ballon style or not, default true. |
| emphasisActive | boolean | Enable active emphasis animation or not, default true. |
| freeWalk | boolean | Freely switch between all the steps or not, default false. |
| omitWalker | boolean | Omit the default step switching buttons (forward/backward) or not, default false. |
| reached | number, text | Number represents step index (starts from 0), text represents step marker. All steps before the specified step can be switched freely. |
Wizard Step (WStep)
| Attribute Name | Type | Description |
|---|---|---|
| title | text | Tab title. |
| marker | text | Tab marker, global unique. |
| body | function | |
| data | function |
titleattribute follows thelabeldefault parsing behavior.
The configuration of tab content is the same as Section, except follows,
- No
titleattribute, which has already been used in tab title, - No
labelattribute.
Ignore all child node definitions when
bodypresents.
dataattribute is used to retrieve the data of the step content, simply set data into step model and widget will refresh automatically.
Wizard Shared Content (WShared)
| Attribute Name | Type | Description |
|---|---|---|
| lead | boolean | Shared content block comes before each step content or not, default false. |
The configuration of tab content is the same as Section, except follows,
- No
titleattribute, - No
labelattribute.
Caption, Label
- Default Wrapped by Form Cell:
true, - Default Grid Column Span:
3.
| Attribute Name | Type | Description |
|---|---|---|
| label, text | text | - Caption::Customer Name- label: Customer Name |
| labelOnValue | boolean | - labelOnValue: true |
| valueToLabel | various | |
| click | text | - click: alert message- click: alert:message- click: dialog key- click: dialog:key |
Caption and Label have slight differences.
- For
Caption, the model value must be explicitly specified; otherwise, only the givenlabelwill be used, - While definingvalueToLabel, the parser will automatically setlabelOnValuetotrue, - For
Label, defaults to using the model value and ignores thelabelattribute, - For both, while defining
text, will ignore thelabelattribute.
labelandtextattribute follows thelabeldefault parsing behavior.
Syntax of valueToLabel:
valueToLabel: `value`: If usingCaptionand specifying to use the model value, no additional decoration applied. If usingLabel, this can be ignored.valueToLabel: `value ?? ''`: Use empty text instead when value isnull,valueToLabel: `$.nf0(value)`: Format the value, by#0,000,valueToLabel: `$.nf1(value)`: Format the value, by#0,000.0,valueToLabel: `$.nf2(value)`: Format the value, by#0,000.00,valueToLabel: `$.nfx(value)`: Format the value, by#0,000.000,xcould be3to99, identify the fraction digits,valueToLabel: `$.nf(0, false)(value)`: Format with the given parameters, keep 0 fraction digits, and do not use thousands separator in the example.valueToLabel: `$.df(value)`: Parse the value by default datetime format and format it to default date format. - Default datetime format:getDefaultCalendarDatetimeFormat, - Default date format:getDefaultCalendarDateFormat, - Change default formats:setCalendarDefaults,valueToLabel: `$.df(value, {from: 'YYYY/MM/DD', to: 'DD/MM/YYYY''})`: Parse the value by givenfrom formatand format it toto format, -fromandtoare optional, will use corresponding default format when ignored.
Some examples:
- Label::Name::name
- Label::Middle Name::middleName
- valueToLabel: `value ?? ''`
- Caption::Middle Name::middleName
- valueToLabel: `value ?? ''`Starting from version v1.2.9, valueToLabel not only supports the above single-line syntax but also complete function bodies.
Additionally,
apart from value and $ (an alias for the formats parameter), also access other parameters as follows:
options.root: Root model,options.model: Current model,options.global: Global handlers.
Because
Captionis also enhanced byForm Cellby default, the behavior of thelabelattribute onCaptionmay seem strange. When not specifying using data from the model value, the samelabelwill be displayed twice. Therefore, if you only want to useCaptionbut don't want it to be displayed twice, you should useCaption::. This way, the system no longer considers it enhanced byForm Celland it will not be displayed twice. This usage is common inTable, and we will discuss it in more detail later.
Syntax of click
click requires external support in order to respond to the defined event. For example:
import {BaseModel, PropValue} from '@rainbow-d9/n1';
import {PageDef} from '@rainbow-d9/n2';
const markdown = `# Page
- Label::Customer::name
- valueToLabel: value ?? ''
- click: dialog:customerDetails
`;
const DialogDefs: Record<string, PageDef> = {
// ...
};
const Dialogs = () => {
const {on, off} = useGlobalEventBus();
const {show: showDialog} = useDialog();
useEffect(() => {
const openDialog = async <R extends BaseModel, M extends PropValue>(
def: PageDef, models?: { root: R; model: M; }) => {
showDialog(<>
{title}
<DialogBody>
<StandaloneRoot {...someDialogDef} $root={model as BaseModel}/>
</DialogBody>
{footer}
</>);
};
const onCustomEvent = <R extends BaseModel, M extends PropValue>(
key: string, models?: { root: R; model: M; }) => {
if (key.startsWith('dialog ') || key.startsWith('dialog:')) {
const dialogKey = key.slice('dialog '.length).trim();
if (VUtils.isNotEmpty(dialogKey)) {
const def = DialogDefs[dialogKey];
if (def !== null) {
(async () => await openDialog(def, models))();
} else {
console.log(`Custom event[key=${key}] is ignored since no definition found by given dialog key[${dialogKey}].`);
}
} else {
console.log(`Custom event[key=${key}] is ignored since no dialog key detected.`);
}
} else {
console.log(`Custom event[key=${key}] is ignored.`);
}
};
on(GlobalEventTypes.CUSTOM_EVENT, onCustomEvent);
return () => {
off(GlobalEventTypes.CUSTOM_EVENT, onCustomEvent);
};
});
}
const Page = () => {
const def = parseDoc(markdown);
// alert handled by <Alert/>, so no additional supporte required
// dialog is using custom event, which needs to supported by <Dialogs />, and bridge to <Dialog />
return <GlobalEventBusProvider>
<Alert/>
<Dialog/>
<Dialogs/>
<StandaloneRoot $root={model} {...def} />
</GlobalEventBusProvider>;
};Input, Number, Pwd
- Default Wrapped by Form Cell:
true, - Default Grid Column Span:
3, - Declared Built-in Validation:
required,numeric,integer,positive,notNegative,length,regex.
Some examples:
- Input::Name::name
- required
- length: ..10; At most 10 characters for name.
- Number::Age::age
- required
- integer
- positiveThe given regular expression will first match against the predefined expressions, and if no match is found, it will directly parse the string as a regular expression. If the given string ends with
/i, it is case-insensitive.
Predefine regular expressions throughValidatorUtils.registerRegexps.
Mask
The mask prompts for input fields support integration from imask, can be defined using relevant attributes:
Input: -mask: A string (pattern) or a function returningFactoryOpts, please refer to the imask definition.Number: - grouping:trueto enable number format grouping. please note default fraction digits is 2. - format: customized mask, aNumberInputFormatobject or a function returningNumberInputFormat.
If the mask definition for
Numberdoesn't meet your requirements, please useInputfor custom masking, while declaring- valueToNumber: true.Please note that here
Maskrefers to text formatting and placeholder display, not the same concept as masking sensitive information like PII (Personally Identifiable Information). Content masking should be controlled through data handling models, not just a matter of formatting display.
Decoration
Input and Number can have decorations, including leadings and tailings. Both leadings and tailings support standard strings or built-in
icons. If you want to use built-in icons, you need to use $icons. followed by the icon name, for example, $icons.date.
- DecoInput::Name::name
- leads: $icons.date
- tails: $icons.timeMultiple decorators could be split by
;.
Decoration also supported forButton,Caption,Label.
DecoNumberis available for decoratingNumber,DecoPwdis available for decoratingPwd.
placeholderfor decorated input supports i18n, and will be disabled automatically when mask presents. However, the number mask typically doesn't display the mask placeholder, so the placeholder will still take effect.The
data-di-*attributes are used for decorators, while others are applied directly to the input box.
Textarea
- Default Wrapped by Form Cell:
true, - Default Grid Column Span:
3, - Declared Built-in Validation:
required,length.
Some examples:
- Textarea::Description::desc
- required
- length: 10..256Checkbox, Radio
- Default Wrapped by Form Cell:
true, - Default Grid Column Span:
3, - Declared Built-in Validation:
required.
| Attribute Name | Type | Description |
|---|---|---|
| values | text | - values: Y- values: Y, N |
| emptyWhenFalse | boolean | - emptyWhenFalse |
emptyWhenFalseis forCheckboxonly.
Syntax of values
The default values for a checkbox are true and false, where any value other than true is considered as false in the display logic.
However, if the actual model value is not of boolean type, such as 1 and 0, Y and N, T and F, etc., it needs to be explicitly
specified.
It is important to note that in JavaScript, the value
1is considered astrue.Radio is a unidirectional form of Checkbox, which means that once selected, it cannot be unselected.
Some examples:
- Checkbox::Agree::agreed
- required
- values: Y, N
- Checkbox::Agree::agreed
- required
- values: Y
- Checkbox::Agree::agreed
- required
- values: , NTypically,
valuesappear in pairs, but in reality, having only one value is also allowed. However, we generally do not recommend such loose data definition as it can lead to data confusion to some extent.The default mapping will translate
True,true,T,t,Yes,yes,Y,ytotrue, andFalse,false,F,f,No,no,N,ntofalse. If a string value is required, use thes:orS:prefix to mark it; for example,s:Ywill be recognized as the stringY, not the defaulttrue. If a numeric value is needed, use then:orN:prefix to mark it.
Dropdown, MultiDropdown, DropdownTree (DDT), MultiDropdownTree (MDDT)
- Default Wrapped by Form Cell:
true, - Default Grid Column Span:
3, - Declared Built-in Validation:
required.
| Attribute Name | Type | Description |
|---|---|---|
| please | string | Placeholder. |
| clearable | boolean | Value can be cleared or not. |
| filterable | boolean | Options can be filter or not. |
| filterChanged | function | Obtain additional matching options when filtering.Available for Dropdown and MultiDropdown only. |
| sort, optionSort | text | - sort: asc- sort: descValue is case insensitive. |
| options | various | |
| couldSelect | function | Returns false when given value cannot be chosen, otherwise it can.Available for DropdownTree and MultiDropdownTree only. |
Syntax of options
options: value: label[; value: label[; value: label...]]: Static options can be defined by separating each option with a semicolon and separating the value and label with a colon,- Use sub-lists to define options, where each sublist item represents an option, and use a colon to separate the value and label,
options: @ext.keys: referencing external definitions is also possible.
Some examples:
- Dropdown::Agree::agreed
- required
- options: Y: Yes; N: No
- Checkbox::Agree::agreed
- required
- options:
- Y: Yes
- N: No
- Checkbox::Agree::agreed
- required
- options: @ext.codes.yesNoOptions
codes.yesNoOptionsdepends on external definitions, it must follow signatureDropdownDef['options'].For "DropdownTree," currently only external definitions are supported, as tree nodes are too complex to describe in Markdown.
Reaction to Refresh Options
Use the refreshOptions attribute to respond to changes and refresh the available options. It is important to note that the options are
still refreshed using the definition of options. Therefore, if the options definition is static, even if refreshOptions is defined,
there will be no changes. In addition, since refreshing the options may result in the originally selected value becoming invalid, clearMe
can be used in combination to handle this situation.
- Dropdown::Options::value
- options: @ext.codes.options
- refreshOptions:
- on: anotherValueTo enable refreshOptions, should define @ext.codes.options as a function. Here is an example:
// put this as external defs
const ext = {
codes: {
options: async ({model}) => {
const options = [
{label: 'Option #1', value: '1', anotherValue: 1},
{label: 'Option #2', value: '2', anotherValue: 2},
{label: 'Option #3', value: '3', anotherValue: 1}
];
if (`${model.anotherValue ?? ''}`.trim().length === 0) {
// all options are avaiable when another value is empty
return options;
} else {
return options.filter(option => option.anotherValue == model.anotherValue);
}
}
}
}Search
All dropdowns provide default option search functionality and offer two styles, which can be set using setDropdownDefaults.
Please note that the search functionality is intended for basic text display. If the label provided in the option item is of type ReactNode, the search functionality will not work. In such cases, you should also provide a stringify function within the option item to retrieve the corresponding text content.
Checkboxes (Checks), Radios
In fact, Checkboxes and Radios are just alternative representations of MultiDropdown and Dropdown, respectively. Therefore, all
the parameters are the same.
When there are too many options (usually limited to 5 or 6), it is not recommended to use a combination of checkboxes and radios, but rather to revert to a
MultiDropdownorDropdownform.
Due to the differences in presentation, Checkboxes and Radios have additional rendering parameters:
| Attribute Name | Type | Description |
|---|---|---|
| columns | number | Number of columns in option arrangement. |
| compact | boolean | When there are multiple options in a row, whether to display them continuously or in a table column format. |
| single | boolean | Only one choice available, only for checks, default false. Should use a primitive value instead of an array when single is enabled. |
| boolOnSingle | boolean | Only one choice available, only for checks, default false. And if want to put a false as value, set as true. |
Reaction to Refresh Options
It is completely consistent with the Dropdown, please refer to the previous section.
Calendar, DateTime, Date
- Default Wrapped by Form Cell:
true, - Default Grid Column Span:
3, - Declared Built-in Validation:
required.
| Attribute Name | Type | Description |
|---|---|---|
| please | text | - please: Please select... |
| clearable | boolean | - !clearable- clearable: falseAllowed to clear the selected value or not. |
| date | boolean | - date: false, only for Calendar |
| dateFormat | text | - dateFormat: YYYY/MM/DD, follows Dayjs. |
| time | boolean | - time: true, only for Calendar |
| timeFormat | text | - timeFormat: HH:mm:ss, follows Dayjs |
| storeFormat | text | - storeFormat: YYYY/MM/DD HH:mm:ss, follows Dayjs |
| autoConfirm | boolean | - autoConfirm: trueSelected value should be applied to model automatically on blur or not. |
| autoConfirmOnDate | boolean | - autoConfirmOnDate: trueSelected date should be applied to model automatically on date clicked when no time part. |
| useCalendarIcon | boolean | - useCalendarIcon: trueUse calendar icon instead of caret down. |
| fixedTimeAt | json | For Calendar, works when time is false; and for Date. |
| initTimeAt | json | For Calendar, typically it is not need for date only. |
| couldPerform | function | Returns false when given value cannot be performed, otherwise it can. |
Automatically detect the time format to determine whether to display minutes and seconds. When seconds are present in the time format, minutes will always be displayed.
Formats
All formats have default values, see below for more details,
setCalendarDefaults: to change default settings,getDefaultCalendarDateFormat: default of date format,getDefaultCalendarTimeFormat: default of time format,getDefaultCalendarDatetimeFormat: default of store format.
Introduce more Dayjs plugins to support additional date formats. For example, to support the Buddhist calendar:
// in your entrypoint, make sure it runs before rendering
import dayjs from 'dayjs';
import BuddhistEra from 'dayjs/plugin/buddhistEra';
dayjs.extend(BuddhistEra);Auto Confirm
Auto confirm selected value on blur has default value, see below for more details,
setCalendarDefaults: to change default settings,isCalendarAutoConfirm: default of autoConfirm.
Stick Icon
The stick icon default uses the dropdown style, which is the caret down icon. You can change it to a calendar icon through global settings, see below for more details,
setCalendarDefaults: to change default settings,isStickIconUseCalendar: default of stick icon.
Fixed Time At, Initial Time At
- fixedTimeAt: startor- fixedTimeAt: 0: fix at00:00:00.0,- fixedTimeAt: end: fix at23:59:59.999,- fixedTimeAt: 01:00:00: fix at01:00:00.0. Any valid time not23:59:59, millisecond should be set as0,- fixedTimeAt: 23:59:59: fix at23:59:59.999,- fixedTimeAt: 23:59:59.1: fix at23:59:59.001,- fixedTimeAt: 23:59:59.12: fix at23:59:59.012,- fixedTimeAt: 23:59:59.123: fix at23:59:59.123.
Some examples:
- Calendar::Date of Birth::dob
- required, !time
- fixedTimeAt: start
- Date::Date of Birth::dob
- required
- fixedTimeAt: 0
- DateTime::Registration Time::registrationTime
- required, !clearableButton
- Default Wrapped by Form Cell:
true, - Default Grid Column Span:
3.
| Attribute Name | Type | Description |
|---|---|---|
| head | text | - head: **Text before text. |
| text | text | - text: Click Me |
| tail | text | - tail: **Text after text. |
| ink | text | - ink: primaryOne of primary, danger, warn, success, info, waive. |
| fill | text | - fill: plainOne of link, plain, fill. |
| click | various |
Syntax of click
Alert and Dialog
Regarding Alert and Dialog, please refer to the description for Caption as it remains consistent.
Validation
click: validate,click: validate me,click: validate:me: automatically detect the validation range and trigger validation. The detection of the validation range is done in the following order: - Whether the range is specified on the button, - Whether it is within the range of array sub-elements, - The closest container range among ancestors, - The entire range starting from the root node.click: validate block,click: validate:block: automatically detect the validation range and trigger validation. The detection of the validation range is done in the following order: - Whether it is within the range of array sub-elements, - The closest container range among ancestors, - The entire range starting from the root node.click: validate all,click: validate:all: trigger validation, the entire range starting from the root node,click: validate scope1[, scope2[, scope3...]],click: validate:scope1[; scope2[; scope3...]],click: validate:scope1[ scope2[ scope3...]]: trigger validation, limited to the given scopes only. If multiple scopes are triggered simultaneously, use a comma or semicolon or blank space as separators. All widgets that match at least one scope will trigger automatic validation.
Some examples:
- Button::
- ink: danger
- fill: link
- click: validate blockButton Bar
- Default Wrapped by Form Cell:
false, - Default Grid Column Span:
12.
| Attribute Name | Type | Description |
|---|---|---|
| alignment | text | - alignment: leftOne of left, center, right. |
Some examples:
- ButtonBar::
- alignment: centerPagination
- Default Wrapped by Form Cell:
false, - Default Grid Column Span:
12.
| Attribute Name | Type | Description |
|---|---|---|
| freeWalk | boolean | - freeWalk, render a dropdown for choose page number. |
| maxButtons | number | - maxButtons: 5, how many page number buttons. |
| sizes, possibleSizes | number array | - sizes: 5;10, split by ;. |
Ribs, RibsView
- Default Wrapped by Form Cell:
false, - Default Grid Column Span:
12.
| Attribute Name | Type | Description |
|---|---|---|
| marker | string | Identity |
| elementTitle, caption | various | Ref to form cell label definition. |
| useSectionStyleIcons | boolean | Change expand and collapse icons to use section style icons or not. |
| showRowIndex | boolean | Show row index or not. |
| initExpanded | function | |
| noElementReminder | text | - noElementReminder: No Data |
| addable | boolean | - !addable.Not available for RibsView. |
| addLabel | text | - addLabel: Create New One.Not available for RibsView. |
| couldAddElement | function | Not available for RibsView. |
| disableOnCannotAdd | boolean | - disableOnCannotAdd.Not available for RibsView. |
| elementAdded | function | Not available for RibsView. |
| createElement | function | Not available for RibsView. |
| removable | boolean | - !removable.Not available for RibsView. |
| removeLabel | text | - removeLabel: Remove This One.Not available for RibsView. |
| elementRemoved | function | Not available for RibsView. |
| couldRemoveElement | function | Not available for RibsView. |
| getElement
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
10 months ago
8 months ago
1 year ago
1 year ago
8 months ago
6 months ago
6 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago