@trimble-oss/moduswebcomponents-mcp 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/versions/1.2.0/component-docs/_all_components.json +56 -0
- package/versions/1.2.0/component-docs/modus-wc-autocomplete.json +415 -0
- package/versions/1.2.0/component-docs/modus-wc-date.json +227 -0
- package/versions/1.2.0/component-docs/modus-wc-dropdown-menu.json +164 -0
- package/versions/1.2.0/component-docs/modus-wc-logo.json +61 -0
- package/versions/1.2.0/component-docs/modus-wc-menu-item.json +165 -0
- package/versions/1.2.0/component-docs/modus-wc-menu.json +106 -0
- package/versions/1.2.0/component-docs/modus-wc-navbar.json +290 -0
- package/versions/1.2.0/component-docs/modus-wc-profile-menu.json +64 -0
- package/versions/1.2.0/component-docs/modus-wc-side-navigation.json +102 -0
- package/versions/1.2.0/component-docs/modus-wc-table.json +202 -0
- package/versions/1.2.0/component-docs/modus-wc-tooltip.json +94 -0
- package/versions/1.2.0/component-docs/modus-wc-typography.json +78 -0
- package/versions/1.2.0/docs/_all_docs.json +15 -0
- package/versions/1.2.0/docs/angular.mdx +374 -0
- package/versions/1.2.0/docs/getting-started.mdx +131 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "A customizable tooltip component used to create tooltips with different content. The tooltip can be dismissed by pressing the Escape key when hovering over it. When forceOpen is enabled, the tooltip will remain open and can only be closed by setting forceOpen to false.",
|
|
3
|
+
"properties": [
|
|
4
|
+
{
|
|
5
|
+
"name": "content",
|
|
6
|
+
"type": "string",
|
|
7
|
+
"description": "The text content of the tooltip.",
|
|
8
|
+
"default": "''",
|
|
9
|
+
"mutable": false,
|
|
10
|
+
"end_line": 36
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "customClass",
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "Custom CSS class to apply to the inner div.",
|
|
16
|
+
"default": "''",
|
|
17
|
+
"mutable": false,
|
|
18
|
+
"end_line": 39
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "disabled",
|
|
22
|
+
"type": "boolean",
|
|
23
|
+
"description": "Disables displaying the tooltip on hover",
|
|
24
|
+
"default": "false",
|
|
25
|
+
"mutable": false,
|
|
26
|
+
"end_line": 42
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "forceOpen",
|
|
30
|
+
"type": "boolean",
|
|
31
|
+
"description": "Use this attribute to force the tooltip to remain open.",
|
|
32
|
+
"default": null,
|
|
33
|
+
"mutable": false,
|
|
34
|
+
"end_line": 45
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "tooltipId",
|
|
38
|
+
"type": "string",
|
|
39
|
+
"description": "The ID of the tooltip element, useful for setting the \"aria-describedby\" attribute of related elements.",
|
|
40
|
+
"default": null,
|
|
41
|
+
"mutable": false,
|
|
42
|
+
"end_line": 48
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "position",
|
|
46
|
+
"type": "'auto' | 'top' | 'right' | 'bottom' | 'left'",
|
|
47
|
+
"description": "The position that the tooltip will render in relation to the element.",
|
|
48
|
+
"default": "'auto'",
|
|
49
|
+
"mutable": false,
|
|
50
|
+
"end_line": 51
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
"events": [
|
|
54
|
+
{
|
|
55
|
+
"name": "dismissEscape",
|
|
56
|
+
"detail": "void",
|
|
57
|
+
"description": "An event that fires when the tooltip is dismissed via Escape key",
|
|
58
|
+
"end_line": 204
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
"methods": [],
|
|
62
|
+
"slots": [
|
|
63
|
+
{
|
|
64
|
+
"name": "default",
|
|
65
|
+
"description": "Slot for default content"
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
"examples": {
|
|
69
|
+
"basic": "<modus-wc-tooltip\n content=${ifDefined(args.content)}\n custom-class=\"${ifDefined(args['custom-class'])}\"\n ?disabled=\"${args.disabled}\"\n ?force-open=\"${args['force-open']}\"\n tooltip-id=\"${ifDefined(args['tooltip-id'])}\"\n position=${ifDefined(args.position)}\n >\n <modus-wc-badge>Hover</modus-wc-badge>\n </modus-wc-tooltip>",
|
|
70
|
+
"variations": [],
|
|
71
|
+
"args": {
|
|
72
|
+
"content": "'Tooltip content'",
|
|
73
|
+
"position": "'auto'"
|
|
74
|
+
},
|
|
75
|
+
"argTypes": {},
|
|
76
|
+
"usage": [],
|
|
77
|
+
"events": [
|
|
78
|
+
"dismissEscape"
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
"tag": "modus-wc-tooltip",
|
|
82
|
+
"storyExample": {
|
|
83
|
+
"template": "<modus-wc-tooltip\n content=${ifDefined(args.content)}\n custom-class=\"${ifDefined(args['custom-class'])}\"\n ?disabled=\"${args.disabled}\"\n ?force-open=\"${args['force-open']}\"\n tooltip-id=\"${ifDefined(args['tooltip-id'])}\"\n position=${ifDefined(args.position)}\n >\n <modus-wc-badge>Hover</modus-wc-badge>\n </modus-wc-tooltip>",
|
|
84
|
+
"args": {
|
|
85
|
+
"content": "'Tooltip content'",
|
|
86
|
+
"position": "'auto'"
|
|
87
|
+
},
|
|
88
|
+
"argTypes": {},
|
|
89
|
+
"events": [
|
|
90
|
+
"dismissEscape"
|
|
91
|
+
],
|
|
92
|
+
"fullContent": "import { Meta, StoryObj } from '@storybook/web-components';\nimport { html } from 'lit';\nimport { ifDefined } from 'lit/directives/if-defined.js';\n\ninterface TooltipArgs {\n content?: string;\n 'custom-class'?: string;\n disabled?: boolean;\n 'force-open'?: boolean;\n 'tooltip-id'?: string;\n position: 'auto' | 'top' | 'right' | 'bottom' | 'left';\n}\n\nconst meta: Meta<TooltipArgs> = {\n title: 'Components/Tooltip',\n component: 'modus-wc-tooltip',\n args: {\n content: 'Tooltip content',\n position: 'auto',\n },\n argTypes: {\n position: {\n control: { type: 'select' },\n options: ['auto', 'top', 'right', 'left', 'bottom'],\n },\n },\n parameters: {\n docs: {\n description: {\n component: `\nA customizable tooltip component used to create tooltips with different content.\n\\nThe component supports a \\`<slot>\\` for injecting custom tooltip content.\n\n### Features\n- **Escape Key Dismissal**: Tooltips can be dismissed by pressing the Escape key\n- **Auto-positioning**: Automatically positions the tooltip to avoid viewport edges\n- **Customizable**: Supports custom CSS classes and positioning\n\n### Keyboard Interaction\n- Press **Escape** to dismiss the tooltip while it's visible\n- The tooltip will automatically re-enable on mouse enter\n `,\n },\n },\n },\n};\n\nexport default meta;\n\ntype Story = StoryObj<TooltipArgs>;\n\nconst Template: Story = {\n parameters: {\n actions: {\n handles: ['dismissEscape'],\n },\n },\n render: (args) => {\n // prettier-ignore\n return html`\n <modus-wc-tooltip\n content=${ifDefined(args.content)}\n custom-class=\"${ifDefined(args['custom-class'])}\"\n ?disabled=\"${args.disabled}\"\n ?force-open=\"${args['force-open']}\"\n tooltip-id=\"${ifDefined(args['tooltip-id'])}\"\n position=${ifDefined(args.position)}\n >\n <modus-wc-badge>Hover</modus-wc-badge>\n </modus-wc-tooltip>\n `;\n },\n};\n\nexport const Default: Story = { ...Template };\n\nexport const Migration: Story = {\n parameters: {\n docs: {\n description: {\n story: `\n#### Breaking Changes\n\n - In 1.0 tooltip positioning was handled by Popper.js. In 2.0, positioning is handled using CSS.\n - The \\`text\\` prop has been renamed to \\`content\\`.\n\n#### Prop Mapping\n\n| 1.0 Prop | 2.0 Prop | Notes |\n|-------------|-------------|------------------------------------------|\n| aria-label | aria-label | |\n| disabled | disabled | |\n| position | position | Added \\`auto\\` option as default value |\n| text | content | |\n `,\n },\n },\n controls: { disable: true },\n canvas: { disable: true },\n },\n render: () => html`<div></div>`,\n};\n"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "A customizable typography component used to render text with different sizes, hierarchy, and weights. Note: - When using heading elements (h1-h6), the default heading CSS styling can be accessed without modifying the default size (size=\"md\") and weight (weight=\"normal\") properties. Default styling can be overridden by providing your own custom values for the size or weight properties from the available options. - If both slot content and `label` are provided, only the slot content will be rendered - Use the `label` prop when you need to dynamically update the text.",
|
|
3
|
+
"properties": [
|
|
4
|
+
{
|
|
5
|
+
"name": "customClass",
|
|
6
|
+
"type": "string",
|
|
7
|
+
"description": "Custom CSS class to apply to the typography element.",
|
|
8
|
+
"default": "''",
|
|
9
|
+
"mutable": false,
|
|
10
|
+
"end_line": 34
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "hierarchy",
|
|
14
|
+
"type": "TypographyHierarchy",
|
|
15
|
+
"description": "The hierarchy of the typography component.",
|
|
16
|
+
"default": "'p'",
|
|
17
|
+
"mutable": false,
|
|
18
|
+
"end_line": 37
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "label",
|
|
22
|
+
"type": "string",
|
|
23
|
+
"description": "The text label to display.",
|
|
24
|
+
"default": null,
|
|
25
|
+
"mutable": false,
|
|
26
|
+
"end_line": 40
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "size",
|
|
30
|
+
"type": "TypographySize",
|
|
31
|
+
"description": "The size of the font.",
|
|
32
|
+
"default": "'md'",
|
|
33
|
+
"mutable": false,
|
|
34
|
+
"end_line": 43
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "weight",
|
|
38
|
+
"type": "TypographyWeight",
|
|
39
|
+
"description": "The weight of the text.",
|
|
40
|
+
"default": "'normal'",
|
|
41
|
+
"mutable": false,
|
|
42
|
+
"end_line": 46
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"events": [],
|
|
46
|
+
"methods": [],
|
|
47
|
+
"slots": [
|
|
48
|
+
{
|
|
49
|
+
"name": "default",
|
|
50
|
+
"description": "Slot for default content"
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
"examples": {
|
|
54
|
+
"basic": "<modus-wc-typography\n custom-class=${ifDefined(args['custom-class'])}\n hierarchy=${args.hierarchy}\n label=${args.label}\n size=${ifDefined(args.size)}\n weight=${ifDefined(args.weight)}\n ></modus-wc-typography>",
|
|
55
|
+
"variations": [],
|
|
56
|
+
"args": {
|
|
57
|
+
"hierarchy": "'p'",
|
|
58
|
+
"label": "content",
|
|
59
|
+
"size": "'md'",
|
|
60
|
+
"weight": "'normal'"
|
|
61
|
+
},
|
|
62
|
+
"argTypes": {},
|
|
63
|
+
"usage": []
|
|
64
|
+
},
|
|
65
|
+
"tag": "modus-wc-typography",
|
|
66
|
+
"storyExample": {
|
|
67
|
+
"template": "<modus-wc-typography\n custom-class=${ifDefined(args['custom-class'])}\n hierarchy=${args.hierarchy}\n label=${args.label}\n size=${ifDefined(args.size)}\n weight=${ifDefined(args.weight)}\n ></modus-wc-typography>",
|
|
68
|
+
"args": {
|
|
69
|
+
"hierarchy": "'p'",
|
|
70
|
+
"label": "content",
|
|
71
|
+
"size": "'md'",
|
|
72
|
+
"weight": "'normal'"
|
|
73
|
+
},
|
|
74
|
+
"argTypes": {},
|
|
75
|
+
"events": [],
|
|
76
|
+
"fullContent": "import { Meta, StoryObj } from '@storybook/web-components';\nimport { html } from 'lit';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport {\n TypographyHierarchy,\n TypographySize,\n TypographyWeight,\n} from './modus-wc-typography';\n\nconst content = 'The quick brown fox jumps over the lazy dog';\n\ninterface TypographyArgs {\n 'custom-class'?: string;\n hierarchy: TypographyHierarchy;\n label: string;\n size?: TypographySize;\n weight?: TypographyWeight;\n}\n\nconst meta: Meta<TypographyArgs> = {\n title: 'Components/Typography',\n component: 'modus-wc-typography',\n args: {\n hierarchy: 'p',\n label: content,\n size: 'md',\n weight: 'normal',\n },\n argTypes: {\n hierarchy: {\n control: { type: 'select' },\n options: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p'],\n },\n size: {\n control: { type: 'select' },\n options: [\n 'xs',\n 'sm',\n 'md',\n 'lg',\n 'xl',\n '2xl',\n '3xl',\n '4xl',\n '5xl',\n '6xl',\n '7xl',\n '8xl',\n '9xl',\n ],\n },\n weight: {\n control: { type: 'select' },\n options: ['light', 'normal', 'semibold', 'bold'],\n },\n },\n};\n\nexport default meta;\n\ntype Story = StoryObj<TypographyArgs>;\n\nconst Template: Story = {\n render: (args) => {\n // prettier-ignore\n return html`\n<modus-wc-typography\n custom-class=${ifDefined(args['custom-class'])}\n hierarchy=${args.hierarchy}\n label=${args.label}\n size=${ifDefined(args.size)}\n weight=${ifDefined(args.weight)}\n ></modus-wc-typography>\n `;\n },\n};\n\nexport const Default: Story = { ...Template };\n\nexport const Heading1: Story = {\n ...Template,\n args: { hierarchy: 'h1', label: content },\n};\n\nexport const Heading2: Story = {\n ...Template,\n args: { hierarchy: 'h2', label: content },\n};\n\nexport const Heading3: Story = {\n ...Template,\n args: { hierarchy: 'h3', label: content },\n};\n\nexport const Heading4: Story = {\n ...Template,\n args: { hierarchy: 'h4', label: content },\n};\n\nexport const Heading5: Story = {\n ...Template,\n args: { hierarchy: 'h5', label: content },\n};\n\nexport const Heading6: Story = {\n ...Template,\n args: { hierarchy: 'h6', label: content },\n};\n\nexport const Paragraph: Story = {\n ...Template,\n args: { hierarchy: 'p', label: content },\n};\n\nexport const WithLabel: Story = {\n ...Template,\n args: {\n hierarchy: 'p',\n label: 'This text is set via the label prop',\n },\n};\n\nexport const WithSlot: Story = {\n render: (args) => {\n // prettier-ignore\n return html`\n<modus-wc-typography\n custom-class=${ifDefined(args['custom-class'])}\n hierarchy=${args.hierarchy}\n size=${ifDefined(args.size)}\n weight=${ifDefined(args.weight)}\n>\n This <u>text</u> is set using <em>slot</em>\n</modus-wc-typography>\n `;\n },\n};\n"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { Meta } from '@storybook/blocks';
|
|
2
|
+
|
|
3
|
+
<Meta title="Documentation/Frameworks/Angular" />
|
|
4
|
+
|
|
5
|
+
# Angular Framework Integration
|
|
6
|
+
|
|
7
|
+
This guide will help you get started with consuming the Modus Angular Web Component library in your Angular project.
|
|
8
|
+
|
|
9
|
+
We highly recommend using the Modus Angular Components library for Angular based projects.
|
|
10
|
+
These components are automatically generated using the Stencil Angular Framework Integration.
|
|
11
|
+
|
|
12
|
+
Follow the steps outlined below to integrate and use Modus Angular Web Components effectively.
|
|
13
|
+
|
|
14
|
+
Please refer to the [official stencil documentation](https://stenciljs.com/docs/angular#consumer-usage) for more information on how to integrate with your Angular project.
|
|
15
|
+
|
|
16
|
+
## Angular with modules
|
|
17
|
+
|
|
18
|
+
Modus Angular Components have a peer dependency with Modus Web Components and require the
|
|
19
|
+
installation of both packages.
|
|
20
|
+
|
|
21
|
+
### 1. Install both `modus-wc` and `modus-wc-angular` dependencies:
|
|
22
|
+
|
|
23
|
+
Ensure that you specify the target version of Angular for the `modus-wc-angular` package (e.g., `ng18` for Angular 18).
|
|
24
|
+
|
|
25
|
+
<b>
|
|
26
|
+
Lock the installed package versions to avoid unintended breakages on future
|
|
27
|
+
npm installs.
|
|
28
|
+
</b>
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install @trimble-oss/moduswebcomponents @trimble-oss/moduswebcomponents-angular@<latest-version>-ng<target-version>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. Set up the styling:
|
|
35
|
+
|
|
36
|
+
You will need to import our styling in your main JavaScript or CSS file:
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
import '@trimble-oss/moduswebcomponents/modus-wc-styles.css';
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 3. Configure asset copying in `angular.json`:
|
|
43
|
+
|
|
44
|
+
For components that use static assets (like `modus-wc-logo`), you need to configure Angular to copy the assets to your build output.
|
|
45
|
+
|
|
46
|
+
Add this to your `angular.json` under `projects > your-app > architect > build > options`:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"projects": {
|
|
51
|
+
"your-app": {
|
|
52
|
+
"architect": {
|
|
53
|
+
"build": {
|
|
54
|
+
"options": {
|
|
55
|
+
"assets": [
|
|
56
|
+
{
|
|
57
|
+
"glob": "**/*",
|
|
58
|
+
"input": "node_modules/@trimble-oss/moduswebcomponents/assets",
|
|
59
|
+
"output": "/assets"
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 4. Import Modus Angular Web Components library into your Angular app's module:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
// app.module.ts
|
|
74
|
+
import { ModusAngularComponentsModule } from '@trimble-oss/moduswebcomponents-angular';
|
|
75
|
+
|
|
76
|
+
@NgModule({
|
|
77
|
+
...
|
|
78
|
+
imports: [ComponentLibraryModule],
|
|
79
|
+
...
|
|
80
|
+
})
|
|
81
|
+
export class AppModule {}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 4. Use Modus Angular Web Components while leveraging Angular template binding syntax:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// app.component.html
|
|
88
|
+
<modus-wc-button label="Click Me" />
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Angular with standalone components
|
|
92
|
+
|
|
93
|
+
Modus Angular Components have a peer dependency with Modus Web Components and require the
|
|
94
|
+
installation of both packages.
|
|
95
|
+
|
|
96
|
+
### 1. Install both `modus-wc` and `modus-wc-angular` dependencies:
|
|
97
|
+
|
|
98
|
+
Ensure that you specify the target version of Angular for the `modus-wc-angular` package (e.g., `ng18` for Angular 18).
|
|
99
|
+
|
|
100
|
+
<b>
|
|
101
|
+
Lock the installed package versions to avoid unintended breakages on future
|
|
102
|
+
npm installs.
|
|
103
|
+
</b>
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm install @trimble-oss/moduswebcomponents @trimble-oss/moduswebcomponents-angular@<latest-version>-ng<target-version>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 2. Set up the styling:
|
|
110
|
+
|
|
111
|
+
You will need to import our styling in your main JavaScript or CSS file:
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
import '@trimble-oss/moduswebcomponents/modus-wc-styles.css';
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 3. Import your component library into your component.
|
|
118
|
+
|
|
119
|
+
You must distribute your components through a primary `NgModule` to use your components in a standalone component.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
// app.component.ts
|
|
123
|
+
import { Component } from '@angular/core';
|
|
124
|
+
import { ModusAngularComponentsModule } from '@trimble-oss/moduswebcomponents-angular';
|
|
125
|
+
|
|
126
|
+
@Component({
|
|
127
|
+
selector: 'app-root',
|
|
128
|
+
standalone: true,
|
|
129
|
+
imports: [ModusAngularComponentsModule],
|
|
130
|
+
templateUrl: './app.component.html',
|
|
131
|
+
})
|
|
132
|
+
export class AppComponent {}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 4. Use Modus Angular Web Components while leveraging Angular template binding syntax:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
// app.component.html
|
|
139
|
+
<modus-wc-button label="Click Me" />
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Custom Elements Schema
|
|
143
|
+
|
|
144
|
+
In the `app.module.ts` file, you need to tell angular that you are using custom element schemas
|
|
145
|
+
so that it does not throw errors when unknown element names are used in the markup.
|
|
146
|
+
|
|
147
|
+
Import `CUSTOM_ELEMENTS_SCHEMA` and add it to your `@NgModule`'s schemas:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
|
151
|
+
|
|
152
|
+
@NgModule({
|
|
153
|
+
...
|
|
154
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
|
155
|
+
...
|
|
156
|
+
})
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Wrapping Components
|
|
160
|
+
|
|
161
|
+
When using Modus Web Components directly, it is recommended to wrap it in corresponding Angular components within your application. This will abstract away from the library dependency, allowing more flexibility for you and your application in the future. Each part of the component is able to be abstracted, leaving you with an Angular-native component.
|
|
162
|
+
|
|
163
|
+
Notice Angular allows `[]` and `()` markup syntax for the web component's inputs and outputs, respectively.
|
|
164
|
+
|
|
165
|
+
Wrapped Modus Button Example:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
|
169
|
+
|
|
170
|
+
@Component({
|
|
171
|
+
selector: 'button-component',
|
|
172
|
+
template: `
|
|
173
|
+
<modus-wc-button
|
|
174
|
+
[buttonStyle]="buttonStyle"
|
|
175
|
+
[color]="color"
|
|
176
|
+
[disabled]="disabled"
|
|
177
|
+
[size]="size"
|
|
178
|
+
(buttonClick)="onButtonClick.emit()"
|
|
179
|
+
>
|
|
180
|
+
<ng-content></ng-content>
|
|
181
|
+
</modus-wc-button>
|
|
182
|
+
`,
|
|
183
|
+
})
|
|
184
|
+
export class ButtonComponent {
|
|
185
|
+
@Input() buttonStyle: 'borderless' | 'fill' | 'outline' = 'fill';
|
|
186
|
+
@Input() color: 'danger' | 'default' | 'primary' | 'secondary' | 'warning' =
|
|
187
|
+
'default';
|
|
188
|
+
@Input() disabled: boolean;
|
|
189
|
+
@Input() size: 'small' | 'medium' | 'large' = 'medium';
|
|
190
|
+
|
|
191
|
+
@Output() onButtonClick = new EventEmitter();
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Reactive Forms
|
|
196
|
+
|
|
197
|
+
Working with a web component's inputs/outputs works great but these components do not integrate with Angular's reactive forms quite as easily. Since web components do not know about Angular's form APIs, we must extend form-compatible components' behavior with simple directives. These directives are applied to the web component selectors, giving the components Angular form functionality.
|
|
198
|
+
|
|
199
|
+
Let's take a look at a directive implementation for a Modus Select's form functionality.
|
|
200
|
+
|
|
201
|
+
#### Wrapper
|
|
202
|
+
|
|
203
|
+
You'll notice the `modus-select` in the markup is taking extra inputs, such as `formControl` and `optionsDisplayProp`, these inputs are provided by the directive below. Here is what our wrapper looks like:
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
|
207
|
+
import { FormControl } from '@angular/forms';
|
|
208
|
+
|
|
209
|
+
@Component({
|
|
210
|
+
selector: 'select-component',
|
|
211
|
+
template: `
|
|
212
|
+
<modus-wc-select
|
|
213
|
+
#select
|
|
214
|
+
[disabled]="disabled"
|
|
215
|
+
[errorText]="errorText"
|
|
216
|
+
[formControl]="formControl"
|
|
217
|
+
[helperText]="helperText"
|
|
218
|
+
[label]="label"
|
|
219
|
+
[options]="options"
|
|
220
|
+
[optionsDisplayProp]="optionsDisplayProp"
|
|
221
|
+
[required]="required"
|
|
222
|
+
[selectValue]="value"
|
|
223
|
+
[size]="size"
|
|
224
|
+
[validText]="validText"
|
|
225
|
+
(valueChange)="onSelectValueChange.emit(select.value)"
|
|
226
|
+
>
|
|
227
|
+
</modus-wc-select>
|
|
228
|
+
`,
|
|
229
|
+
})
|
|
230
|
+
export class SelectComponent {
|
|
231
|
+
@Input() disabled: boolean;
|
|
232
|
+
@Input() errorText: string;
|
|
233
|
+
@Input() formControl: FormControl;
|
|
234
|
+
@Input() helperText: string;
|
|
235
|
+
@Input() label: string;
|
|
236
|
+
@Input() options: unknown[] = [];
|
|
237
|
+
@Input() optionsDisplayProp: string;
|
|
238
|
+
@Input() required: boolean;
|
|
239
|
+
@Input() size: 'medium' | 'large' = 'medium';
|
|
240
|
+
@Input() validText: string;
|
|
241
|
+
@Input() value: unknown;
|
|
242
|
+
|
|
243
|
+
@Output() onSelectValueChange = new EventEmitter<unknown>();
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
#### Directive
|
|
248
|
+
|
|
249
|
+
Moving onto the directive, there are a few things to keep in mind.
|
|
250
|
+
|
|
251
|
+
- The directive's selector is set to the web component's tag, not the wrapper's.
|
|
252
|
+
- Implementing the `ControlValueAccessor` interface helps Angular understand when the form control has been updated or changed.
|
|
253
|
+
- When the value is set, `onChange()` notifies that the control has been updated.
|
|
254
|
+
- Calling `onTouched()` lets Angular know the component has been touched, which is needed for form validation.
|
|
255
|
+
- The `get value()`, and `set value()` are used by Angular's form control.
|
|
256
|
+
- Using the `@HostListener` decorator lets you listen to events from the web component, and execute appropriate logic.
|
|
257
|
+
|
|
258
|
+
Here is what our directive looks like:
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
import {
|
|
262
|
+
Directive,
|
|
263
|
+
forwardRef,
|
|
264
|
+
ElementRef,
|
|
265
|
+
HostListener,
|
|
266
|
+
Input,
|
|
267
|
+
OnInit,
|
|
268
|
+
Output,
|
|
269
|
+
EventEmitter,
|
|
270
|
+
} from '@angular/core';
|
|
271
|
+
import {
|
|
272
|
+
ControlValueAccessor,
|
|
273
|
+
FormControl,
|
|
274
|
+
NG_VALUE_ACCESSOR,
|
|
275
|
+
} from '@angular/forms';
|
|
276
|
+
|
|
277
|
+
@Directive({
|
|
278
|
+
selector: 'modus-wc-select',
|
|
279
|
+
providers: [
|
|
280
|
+
{
|
|
281
|
+
provide: NG_VALUE_ACCESSOR,
|
|
282
|
+
useExisting: forwardRef(() => ModusSelectDirective),
|
|
283
|
+
multi: true,
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
})
|
|
287
|
+
export class ModusSelectDirective implements ControlValueAccessor, OnInit {
|
|
288
|
+
@Input() disabled: boolean;
|
|
289
|
+
@Input() errorText: string;
|
|
290
|
+
@Input() formControl: FormControl;
|
|
291
|
+
@Input() helperText: string;
|
|
292
|
+
@Input() label: string;
|
|
293
|
+
@Input() options: unknown[];
|
|
294
|
+
@Input() optionsDisplayProp: string;
|
|
295
|
+
@Input() required: boolean;
|
|
296
|
+
@Input() selectValue: unknown;
|
|
297
|
+
@Input() size: 'medium' | 'large';
|
|
298
|
+
@Input() validText: string;
|
|
299
|
+
|
|
300
|
+
@Output() valueChange = new EventEmitter<string>();
|
|
301
|
+
|
|
302
|
+
onChange: any = () => {};
|
|
303
|
+
onTouched: any = () => {};
|
|
304
|
+
|
|
305
|
+
private _value: string;
|
|
306
|
+
|
|
307
|
+
get value() {
|
|
308
|
+
return this._value;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
set value(value) {
|
|
312
|
+
if (value !== this._value) {
|
|
313
|
+
this._value = value;
|
|
314
|
+
this.onChange(this._value);
|
|
315
|
+
this.onTouched();
|
|
316
|
+
this.elementRef.nativeElement.value = value;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
constructor(private elementRef: ElementRef) {}
|
|
321
|
+
|
|
322
|
+
ngOnInit(): void {
|
|
323
|
+
const modusSelect = this.elementRef.nativeElement as HTMLModusSelectElement;
|
|
324
|
+
modusSelect.disabled = this.disabled;
|
|
325
|
+
modusSelect.errorText = this.errorText;
|
|
326
|
+
modusSelect.helperText = this.helperText;
|
|
327
|
+
modusSelect.label = this.label;
|
|
328
|
+
modusSelect.options = this.options;
|
|
329
|
+
modusSelect.optionsDisplayProp = this.optionsDisplayProp;
|
|
330
|
+
modusSelect.required = this.required;
|
|
331
|
+
modusSelect.size = this.size;
|
|
332
|
+
modusSelect.validText = this.validText;
|
|
333
|
+
modusSelect.value = this.selectValue;
|
|
334
|
+
|
|
335
|
+
if (!this.formControl) {
|
|
336
|
+
this.formControl = new FormControl(null);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
@HostListener('valueChange', ['$event.detail'])
|
|
341
|
+
listenForValueChange(value: string): void {
|
|
342
|
+
this.value = value;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
registerOnChange(fn: Function): void {
|
|
346
|
+
this.onChange = fn;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
registerOnTouched(fn: Function): void {
|
|
350
|
+
this.onTouched = fn;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
setDisabledState(isDisabled: boolean): void {
|
|
354
|
+
this.disabled = isDisabled;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
writeValue(value: string): void {
|
|
358
|
+
if (value) {
|
|
359
|
+
this.value = value;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Now adding the Modus Select as a form control is as easy as:
|
|
366
|
+
|
|
367
|
+
```ts
|
|
368
|
+
<select-component
|
|
369
|
+
[formControl]="$any(form).controls['select1']"
|
|
370
|
+
[label]="'Select Form Demo'"
|
|
371
|
+
[options]="options"
|
|
372
|
+
[optionsDisplayProp]="'display'">
|
|
373
|
+
</select-component>
|
|
374
|
+
```
|