diff --git a/projects/cli/documentation.json b/projects/cli/documentation.json index 456ac59..043cdb3 100644 --- a/projects/cli/documentation.json +++ b/projects/cli/documentation.json @@ -9,7 +9,7 @@ "components": [ { "name": "ZenAvatarComponent", - "id": "component-ZenAvatarComponent-c1c70b2ab78a2d2de01c96f1e02dffd28942dfa13bfd7daf34f2dfe13d8000e1177c0a20cfcab91141d2545206ba45332267ea8feee6b9b5779d6499882cf72c", + "id": "component-ZenAvatarComponent-4df759231d66d3f8cc9321e00adaa26ec9d295e6a54d9a5e96fbe49012d7aaeea3338bff80ee2bf556b082d8e8b0b534bdb0a58fbaec73fce3514378f7959537", "file": "projects/cli/schematics/components/files/avatar/avatar.component.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": ["ViewEncapsulation.None"], @@ -40,15 +40,15 @@ "modifierKind": [148], "jsdoctags": [ { - "pos": 1474, - "end": 1491, + "pos": 1528, + "end": 1546, "flags": 16842752, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 334, "tagName": { - "pos": 1475, - "end": 1482, + "pos": 1529, + "end": 1536, "flags": 16842752, "modifierFlagsCache": 0, "transformFlags": 0, @@ -58,15 +58,15 @@ "comment": "

''

\n" }, { - "pos": 1491, - "end": 1503, + "pos": 1546, + "end": 1559, "flags": 16842752, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 334, "tagName": { - "pos": 1492, - "end": 1497, + "pos": 1547, + "end": 1552, "flags": 16842752, "modifierFlagsCache": 0, "transformFlags": 0, @@ -76,15 +76,15 @@ "comment": "" }, { - "pos": 1503, - "end": 1515, + "pos": 1559, + "end": 1571, "flags": 16842752, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 351, "tagName": { - "pos": 1504, - "end": 1508, + "pos": 1560, + "end": 1564, "flags": 16842752, "modifierFlagsCache": 0, "transformFlags": 0, @@ -93,15 +93,15 @@ }, "comment": "", "typeExpression": { - "pos": 1509, - "end": 1515, + "pos": 1565, + "end": 1571, "flags": 16842752, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 316, "type": { - "pos": 1509, - "end": 1515, + "pos": 1565, + "end": 1571, "flags": 16777216, "modifierFlagsCache": 0, "transformFlags": 1, @@ -126,7 +126,7 @@ "description": "

A component to display an avatar image. If a valid image source URL is provided,\nit will render an image element. Otherwise, it will display projected content.

\n

The component uses Angular's optimized image directives for enhanced performance.

\n

Usage:

\nExample :
<!-- If an image source is provided, it will display the image -->\n<zen-avatar [src]="'/path/to/image.jpg'" />\n\n<!-- If no image source is provided, it will display the projected content -->\n<zen-avatar> A </zen-avatar>
Example :
<zen-avatar src="https://picsum.photos/32" />
", "rawdescription": "\n\nA component to display an avatar image. If a valid image source URL is provided,\nit will render an image element. Otherwise, it will display projected content.\n\nThe component uses Angular's optimized image directives for enhanced performance.\n\nUsage:\n\n```html\n\n\n\n\n A \n```\n\n```html\n\n```\n\n", "type": "component", - "sourceCode": "import { NgOptimizedImage } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n input,\n ViewEncapsulation,\n} from '@angular/core';\n\n/**\n * A component to display an avatar image. If a valid image source URL is provided,\n * it will render an image element. Otherwise, it will display projected content.\n *\n * The component uses Angular's optimized image directives for enhanced performance.\n *\n * Usage:\n *\n * ```html\n * \n * \n *\n * \n * A \n * ```\n *\n * @example\n * \n *\n * @component\n * @selector `zen-avatar`\n *\n * @license BSD-2-Clause\n * @author Konrad Stępień \n * @link https://github.com/Kordrad/ng-zen\n */\n@Component({\n selector: 'zen-avatar',\n standalone: true,\n imports: [NgOptimizedImage],\n template: `\n @if (src()) {\n \"\"\n } @else {\n \n }\n `,\n styleUrl: './avatar.component.scss',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ZenAvatarComponent {\n /**\n * Path to the image source. If the `src` is provided, an image element will be rendered.\n * Otherwise, the content projected into this component will be displayed.\n *\n * @default ''\n * @input\n * @type string\n */\n readonly src = input('');\n}\n", + "sourceCode": "import { NgOptimizedImage } from '@angular/common';\r\nimport {\r\n ChangeDetectionStrategy,\r\n Component,\r\n input,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\n\r\n/**\r\n * A component to display an avatar image. If a valid image source URL is provided,\r\n * it will render an image element. Otherwise, it will display projected content.\r\n *\r\n * The component uses Angular's optimized image directives for enhanced performance.\r\n *\r\n * Usage:\r\n *\r\n * ```html\r\n * \r\n * \r\n *\r\n * \r\n * A \r\n * ```\r\n *\r\n * @example\r\n * \r\n *\r\n * @component\r\n * @selector `zen-avatar`\r\n *\r\n * @license BSD-2-Clause\r\n * @author Konrad Stępień \r\n * @link https://github.com/Kordrad/ng-zen\r\n */\r\n@Component({\r\n selector: 'zen-avatar',\r\n standalone: true,\r\n imports: [NgOptimizedImage],\r\n template: `\r\n @if (src()) {\r\n \"\"\r\n } @else {\r\n \r\n }\r\n `,\r\n styleUrl: './avatar.component.scss',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class ZenAvatarComponent {\r\n /**\r\n * Path to the image source. If the `src` is provided, an image element will be rendered.\r\n * Otherwise, the content projected into this component will be displayed.\r\n *\r\n * @default ''\r\n * @input\r\n * @type string\r\n */\r\n readonly src = input('');\r\n}\r\n", "styleUrl": "./avatar.component.scss", "assetsDirs": [], "styleUrlsData": "", @@ -135,7 +135,7 @@ }, { "name": "ZenBadgeComponent", - "id": "component-ZenBadgeComponent-71671a8b2d105a1f0687f42fe45ab78400e69e325611e53f7616e070c60e7801822dd72aeccf688b5239479a7126465643fadc51af581f781fc568460e4682d9", + "id": "component-ZenBadgeComponent-a18ab634c6366fa273ca9d843ead3428f0cbcd0e6d5fd1a38812e1ce541e8586140a7cf0e6cbadc828bc2c5a6990c3e0c96963ae31ac8e82bdb3e31a069359ed", "file": "projects/cli/schematics/components/files/badge/badge.component.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": ["ViewEncapsulation.None"], @@ -163,7 +163,7 @@ "description": "

ZenBadgeComponent is a versatile Angular component used to display badges or indicators\nin various positions within its parent container. It allows for flexible placement\nof badge content such as icons, text, or other elements in the top-left, top,\ntop-right, left, right, bottom-left, bottom, and bottom-right positions.

\n

This component offers standalone usage and is highly customizable through its\ntemplate and styling. It leverages Angular's change detection strategy 'OnPush'\nfor optimal performance.

\n

Additionally, it provides support for CSS variables to customize badge positioning

\nExample :
--zen-badge-offset: unset; // Controls the overall offset of the badge\n--zen-badge-offset-x: unset; // Controls the horizontal offset of the badge.\n--zen-badge-offset-y: unset; // Controls the vertical offset of the badge.
Example :
", "rawdescription": "\n\nZenBadgeComponent is a versatile Angular component used to display badges or indicators\nin various positions within its parent container. It allows for flexible placement\nof badge content such as icons, text, or other elements in the top-left, top,\ntop-right, left, right, bottom-left, bottom, and bottom-right positions.\n\nThis component offers standalone usage and is highly customizable through its\ntemplate and styling. It leverages Angular's change detection strategy 'OnPush'\nfor optimal performance.\n\nAdditionally, it provides support for CSS variables to customize badge positioning\n\n```css\n--zen-badge-offset: unset; // Controls the overall offset of the badge\n--zen-badge-offset-x: unset; // Controls the horizontal offset of the badge.\n--zen-badge-offset-y: unset; // Controls the vertical offset of the badge.\n```\n\n```html\n```\n\n", "type": "component", - "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n} from '@angular/core';\n\n/**\n * ZenBadgeComponent is a versatile Angular component used to display badges or indicators\n * in various positions within its parent container. It allows for flexible placement\n * of badge content such as icons, text, or other elements in the top-left, top,\n * top-right, left, right, bottom-left, bottom, and bottom-right positions.\n *\n * This component offers standalone usage and is highly customizable through its\n * template and styling. It leverages Angular's change detection strategy 'OnPush'\n * for optimal performance.\n *\n * Additionally, it provides support for CSS variables to customize badge positioning\n *\n * ```css\n * --zen-badge-offset: unset; // Controls the overall offset of the badge\n * --zen-badge-offset-x: unset; // Controls the horizontal offset of the badge.\n * --zen-badge-offset-y: unset; // Controls the vertical offset of the badge.\n * ```\n *\n * @example Text 🦆\n *\n * @component\n * @selector `zen-badge`,`[zenBadge]`\n *\n * @license BSD-2-Clause\n * @author Konrad Stępień \n * @link https://github.com/Kordrad/ng-zen\n */\n@Component({\n selector: 'zen-badge,[zenBadge]',\n standalone: true,\n template: `\n \n \n \n \n \n \n \n \n \n `,\n styleUrl: './badge.component.scss',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ZenBadgeComponent {}\n", + "sourceCode": "import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\n\r\n/**\r\n * ZenBadgeComponent is a versatile Angular component used to display badges or indicators\r\n * in various positions within its parent container. It allows for flexible placement\r\n * of badge content such as icons, text, or other elements in the top-left, top,\r\n * top-right, left, right, bottom-left, bottom, and bottom-right positions.\r\n *\r\n * This component offers standalone usage and is highly customizable through its\r\n * template and styling. It leverages Angular's change detection strategy 'OnPush'\r\n * for optimal performance.\r\n *\r\n * Additionally, it provides support for CSS variables to customize badge positioning\r\n *\r\n * ```css\r\n * --zen-badge-offset: unset; // Controls the overall offset of the badge\r\n * --zen-badge-offset-x: unset; // Controls the horizontal offset of the badge.\r\n * --zen-badge-offset-y: unset; // Controls the vertical offset of the badge.\r\n * ```\r\n *\r\n * @example Text 🦆\r\n *\r\n * @component\r\n * @selector `zen-badge`,`[zenBadge]`\r\n *\r\n * @license BSD-2-Clause\r\n * @author Konrad Stępień \r\n * @link https://github.com/Kordrad/ng-zen\r\n */\r\n@Component({\r\n selector: 'zen-badge,[zenBadge]',\r\n standalone: true,\r\n template: `\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n `,\r\n styleUrl: './badge.component.scss',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class ZenBadgeComponent {}\r\n", "styleUrl": "./badge.component.scss", "assetsDirs": [], "styleUrlsData": "", @@ -240,6 +240,170 @@ } } }, + { + "name": "ZenSwitchComponent", + "id": "component-ZenSwitchComponent-6016a30f0e96e5474d8cc5f7e8c943d1950b807b10ed47c79f96b0beed3de9f8b8a9f7d89dc6a67ebb57392a06abe19e63e75d8c489098f1c5469a021a893baf", + "file": "projects/cli/schematics/components/files/switch/zen-switch.component.ts", + "changeDetection": "ChangeDetectionStrategy.OnPush", + "encapsulation": [], + "entryComponents": [], + "inputs": [], + "outputs": [], + "providers": [ + { + "name": "{\n provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ZenSwitchComponent), multi: true,\n}", + "type": "component" + } + ], + "selector": "zen-switch", + "styleUrls": [], + "styles": [], + "templateUrl": ["./zen-switch.component.html"], + "viewProviders": [], + "hostDirectives": [], + "inputsClass": [ + { + "name": "checked", + "defaultValue": "false", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "

Model for the checked state of the switch.

\n", + "line": 47, + "rawdescription": "\nModel for the checked state of the switch.", + "required": false + }, + { + "name": "disabled", + "defaultValue": "false", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "

Model for the disabled state of the switch.

\n", + "line": 50, + "rawdescription": "\nModel for the disabled state of the switch.", + "required": false + } + ], + "outputsClass": [], + "propertiesClass": [ + { + "name": "checked", + "defaultValue": "false", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "

Model for the checked state of the switch.

\n", + "line": 47, + "rawdescription": "\nModel for the checked state of the switch.", + "required": false + }, + { + "name": "disabled", + "defaultValue": "false", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "

Model for the disabled state of the switch.

\n", + "line": 50, + "rawdescription": "\nModel for the disabled state of the switch.", + "required": false + } + ], + "methodsClass": [ + { + "name": "onKeyDown", + "args": [ + { + "name": "event", + "type": "KeyboardEvent", + "deprecated": false, + "deprecationMessage": "" + } + ], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 105, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\n\nHandles keyboard events for accessibility.\n", + "description": "

Handles keyboard events for accessibility.

\n", + "jsdoctags": [ + { + "name": "event", + "type": "KeyboardEvent", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + { + "name": "onToggle", + "args": [ + { + "name": "check", + "type": "boolean", + "deprecated": false, + "deprecationMessage": "", + "optional": true + } + ], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 92, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\n\nToggles the switch value and notifies the change.\n", + "description": "

Toggles the switch value and notifies the change.

\n", + "jsdoctags": [ + { + "name": "check", + "type": "boolean", + "deprecated": false, + "deprecationMessage": "", + "optional": true, + "tagName": { + "text": "param" + } + } + ] + } + ], + "deprecated": false, + "deprecationMessage": "", + "hostBindings": [], + "hostListeners": [], + "standalone": true, + "imports": [ + { + "name": "FormsModule", + "type": "module" + }, + { + "name": "NgIf" + } + ], + "description": "

ZenSwitchComponent is a custom switch component that implements ControlValueAccessor to work seamlessly with Angular forms.

\nExample :
", + "rawdescription": "\n\nZenSwitchComponent is a custom switch component that implements ControlValueAccessor to work seamlessly with Angular forms.\n\n```html\n```\n\n", + "type": "component", + "sourceCode": "import { NgIf } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n forwardRef,\n model,\n} from '@angular/core';\nimport {\n ControlValueAccessor,\n FormsModule,\n NG_VALUE_ACCESSOR,\n} from '@angular/forms';\n\ntype OnChangeFn = (value: boolean) => void;\ntype OnTouchedFn = () => void;\n\n/**\n * ZenSwitchComponent is a custom switch component that implements ControlValueAccessor to work seamlessly with Angular forms.\n *\n * @example \n *\n * @export\n * @class ZenSwitchComponent\n * @implements {ControlValueAccessor}\n *\n * @license BSD-2-Clause\n * @author Konrad Stępień \n * @link https://github.com/Kordrad/ng-zen\n */\n@Component({\n selector: 'zen-switch',\n standalone: true,\n templateUrl: './zen-switch.component.html',\n styleUrl: './zen-switch.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => ZenSwitchComponent),\n multi: true,\n },\n ],\n imports: [FormsModule, NgIf],\n})\nexport class ZenSwitchComponent implements ControlValueAccessor {\n /** Model for the checked state of the switch. */\n checked = model(false);\n\n /** Model for the disabled state of the switch. */\n disabled = model(false);\n\n /** @ignore */\n private onChange: OnChangeFn = () => {};\n /** @ignore */\n private onTouched: OnTouchedFn = () => {};\n\n /**\n * Writes a new value to the component.\n * @ignore\n */\n writeValue(value: boolean): void {\n this.checked.set(value);\n }\n\n /**\n * Registers a function to be called when the value changes.\n * @ignore\n */\n registerOnChange(fn: OnChangeFn): void {\n this.onChange = fn;\n }\n\n /**\n * Registers a function to be called when the component is touched.\n * @ignore\n */\n registerOnTouched(fn: OnTouchedFn): void {\n this.onTouched = fn;\n }\n\n /**\n * Sets the disabled state of the component.\n * @ignore\n */\n setDisabledState(isDisabled: boolean): void {\n this.disabled.set(isDisabled);\n }\n\n /**\n * Toggles the switch value and notifies the change.\n */\n onToggle(check?: boolean): void {\n if (this.disabled()) return;\n\n const value = check ?? !this.checked();\n\n this.checked.set(value);\n this.onChange(value);\n this.onTouched();\n }\n\n /**\n * Handles keyboard events for accessibility.\n */\n onKeyDown(event: KeyboardEvent): void {\n switch (event.code) {\n case 'Enter':\n case 'Space': {\n event.preventDefault();\n this.onToggle();\n break;\n }\n case 'ArrowRight': {\n this.onToggle(true);\n break;\n }\n case 'ArrowLeft': {\n this.onToggle(false);\n break;\n }\n }\n }\n}\n", + "styleUrl": "./zen-switch.component.scss", + "assetsDirs": [], + "styleUrlsData": "", + "stylesData": "", + "extends": [], + "implements": ["ControlValueAccessor"], + "templateData": "\n \n\n" + }, { "name": "ZenTagComponent", "id": "component-ZenTagComponent-5228e187c92b9212421b9a731ff7a0a9454a5fce400d56a32c5064c399c1f34718009176b252df35d2b32a63f44b1cf96d3c9c0a89438226dd6b322afd3ecc33", @@ -289,7 +453,7 @@ "deprecated": false, "deprecationMessage": "", "type": "Story", - "defaultValue": "{\n render: () => ({\n template: `\n
\n TOP-LEFT\n TOP\n TOP-RIGHT\n LEFT\n RIGHT\n BOTTOM-LEFT\n BOTTOM\n BOTTOM-RIGHT\n
\n `,\n }),\n}" + "defaultValue": "{\r\n render: () => ({\r\n template: `\r\n
\r\n TOP-LEFT\r\n TOP\r\n TOP-RIGHT\r\n LEFT\r\n RIGHT\r\n BOTTOM-LEFT\r\n BOTTOM\r\n BOTTOM-RIGHT\r\n
\r\n `,\r\n }),\r\n}" }, { "name": "Avatar", @@ -331,6 +495,16 @@ "type": "Story", "defaultValue": "{\r\n decorators: [\r\n moduleMetadata({\r\n imports: [ZenTagComponent, ZenAvatarComponent, ZenBadgeComponent],\r\n }),\r\n ],\r\n render: () => ({\r\n template: `\r\n \r\n KS\r\n \r\n `,\r\n }),\r\n}" }, + { + "name": "Checked", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "projects/cli/stories/components/switch.stories.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "Story", + "defaultValue": "{\n render: () => ({\n template: `\n \n
\n \n `,\n }),\n}" + }, { "name": "Component", "ctype": "miscellaneous", @@ -339,7 +513,7 @@ "deprecated": false, "deprecationMessage": "", "type": "Story", - "defaultValue": "{\n render: () => ({\n template: `\n \n TOP-LEFT\n TOP\n TOP-RIGHT\n LEFT\n RIGHT\n BOTTOM-LEFT\n BOTTOM\n BOTTOM-RIGHT\n \"picsum\"/\n \n `,\n }),\n}" + "defaultValue": "{\r\n render: () => ({\r\n template: `\r\n \r\n TOP-LEFT\r\n TOP\r\n TOP-RIGHT\r\n LEFT\r\n RIGHT\r\n BOTTOM-LEFT\r\n BOTTOM\r\n BOTTOM-RIGHT\r\n \"picsum\"/\r\n \r\n `,\r\n }),\r\n}" }, { "name": "Default", @@ -351,6 +525,16 @@ "type": "Story", "defaultValue": "{}" }, + { + "name": "Default", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "projects/cli/stories/components/switch.stories.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "Story", + "defaultValue": "{\n render: () => ({\n template: `\n \n `,\n }),\n}" + }, { "name": "Default", "ctype": "miscellaneous", @@ -361,6 +545,16 @@ "type": "Story", "defaultValue": "{\r\n render: () => ({ template: `Zen Tag` }),\r\n}" }, + { + "name": "Disabled", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "projects/cli/stories/components/switch.stories.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "Story", + "defaultValue": "{\n render: () => ({\n template: ``,\n }),\n}" + }, { "name": "Emoji", "ctype": "miscellaneous", @@ -369,7 +563,7 @@ "deprecated": false, "deprecationMessage": "", "type": "Story", - "defaultValue": "{\n decorators: [\n moduleMetadata({\n imports: [ZenAvatarComponent, ZenBadgeComponent],\n }),\n ],\n render: () => ({\n template: `\n \n \n 💬\n \n `,\n }),\n}" + "defaultValue": "{\r\n decorators: [\r\n moduleMetadata({\r\n imports: [ZenAvatarComponent, ZenBadgeComponent],\r\n }),\r\n ],\r\n render: () => ({\r\n template: `\r\n \r\n \r\n 💬\r\n \r\n `,\r\n }),\r\n}" }, { "name": "Image", @@ -434,6 +628,28 @@ ], "functions": [], "typealiases": [ + { + "name": "OnChangeFn", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "function", + "file": "projects/cli/schematics/components/files/switch/zen-switch.component.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 184 + }, + { + "name": "OnTouchedFn", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "function", + "file": "projects/cli/schematics/components/files/switch/zen-switch.component.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 184 + }, { "name": "Story", "ctype": "miscellaneous", @@ -467,6 +683,17 @@ "description": "", "kind": 183 }, + { + "name": "Story", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "StoryObj", + "file": "projects/cli/stories/components/switch.stories.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 183 + }, { "name": "Story", "ctype": "miscellaneous", @@ -490,7 +717,7 @@ "deprecated": false, "deprecationMessage": "", "type": "Story", - "defaultValue": "{\n render: () => ({\n template: `\n
\n TOP-LEFT\n TOP\n TOP-RIGHT\n LEFT\n RIGHT\n BOTTOM-LEFT\n BOTTOM\n BOTTOM-RIGHT\n
\n `,\n }),\n}" + "defaultValue": "{\r\n render: () => ({\r\n template: `\r\n
\r\n TOP-LEFT\r\n TOP\r\n TOP-RIGHT\r\n LEFT\r\n RIGHT\r\n BOTTOM-LEFT\r\n BOTTOM\r\n BOTTOM-RIGHT\r\n
\r\n `,\r\n }),\r\n}" }, { "name": "Avatar", @@ -520,7 +747,7 @@ "deprecated": false, "deprecationMessage": "", "type": "Story", - "defaultValue": "{\n render: () => ({\n template: `\n \n TOP-LEFT\n TOP\n TOP-RIGHT\n LEFT\n RIGHT\n BOTTOM-LEFT\n BOTTOM\n BOTTOM-RIGHT\n \"picsum\"/\n \n `,\n }),\n}" + "defaultValue": "{\r\n render: () => ({\r\n template: `\r\n \r\n TOP-LEFT\r\n TOP\r\n TOP-RIGHT\r\n LEFT\r\n RIGHT\r\n BOTTOM-LEFT\r\n BOTTOM\r\n BOTTOM-RIGHT\r\n \"picsum\"/\r\n \r\n `,\r\n }),\r\n}" }, { "name": "Emoji", @@ -530,7 +757,7 @@ "deprecated": false, "deprecationMessage": "", "type": "Story", - "defaultValue": "{\n decorators: [\n moduleMetadata({\n imports: [ZenAvatarComponent, ZenBadgeComponent],\n }),\n ],\n render: () => ({\n template: `\n \n \n 💬\n \n `,\n }),\n}" + "defaultValue": "{\r\n decorators: [\r\n moduleMetadata({\r\n imports: [ZenAvatarComponent, ZenBadgeComponent],\r\n }),\r\n ],\r\n render: () => ({\r\n template: `\r\n \r\n \r\n 💬\r\n \r\n `,\r\n }),\r\n}" } ], "projects/cli/stories/components/avatar.stories.ts": [ @@ -597,6 +824,38 @@ "defaultValue": "{\r\n render: () => ({ template: `Zen Tag` }),\r\n}" } ], + "projects/cli/stories/components/switch.stories.ts": [ + { + "name": "Checked", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "projects/cli/stories/components/switch.stories.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "Story", + "defaultValue": "{\n render: () => ({\n template: `\n \n
\n \n `,\n }),\n}" + }, + { + "name": "Default", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "projects/cli/stories/components/switch.stories.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "Story", + "defaultValue": "{\n render: () => ({\n template: `\n \n `,\n }),\n}" + }, + { + "name": "Disabled", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "projects/cli/stories/components/switch.stories.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "Story", + "defaultValue": "{\n render: () => ({\n template: ``,\n }),\n}" + } + ], "projects/cli/stories/components/pin.stories.ts": [ { "name": "Default", @@ -645,6 +904,30 @@ "groupedFunctions": {}, "groupedEnumerations": {}, "groupedTypeAliases": { + "projects/cli/schematics/components/files/switch/zen-switch.component.ts": [ + { + "name": "OnChangeFn", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "function", + "file": "projects/cli/schematics/components/files/switch/zen-switch.component.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 184 + }, + { + "name": "OnTouchedFn", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "function", + "file": "projects/cli/schematics/components/files/switch/zen-switch.component.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 184 + } + ], "projects/cli/stories/components/avatar.stories.ts": [ { "name": "Story", @@ -684,6 +967,19 @@ "kind": 183 } ], + "projects/cli/stories/components/switch.stories.ts": [ + { + "name": "Story", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "StoryObj", + "file": "projects/cli/stories/components/switch.stories.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 183 + } + ], "projects/cli/stories/components/tag.stories.ts": [ { "name": "Story", @@ -701,7 +997,7 @@ }, "routes": [], "coverage": { - "count": 19, + "count": 20, "status": "low", "files": [ { @@ -741,6 +1037,15 @@ "coverageCount": "2/3", "status": "good" }, + { + "filePath": "projects/cli/schematics/components/files/switch/zen-switch.component.ts", + "type": "component", + "linktype": "component", + "name": "ZenSwitchComponent", + "coveragePercent": 100, + "coverageCount": "7/7", + "status": "very-good" + }, { "filePath": "projects/cli/schematics/components/files/tag/tag.component.ts", "type": "component", @@ -870,6 +1175,36 @@ "coverageCount": "0/1", "status": "low" }, + { + "filePath": "projects/cli/stories/components/switch.stories.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "Checked", + "coveragePercent": 0, + "coverageCount": "0/1", + "status": "low" + }, + { + "filePath": "projects/cli/stories/components/switch.stories.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "Default", + "coveragePercent": 0, + "coverageCount": "0/1", + "status": "low" + }, + { + "filePath": "projects/cli/stories/components/switch.stories.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "Disabled", + "coveragePercent": 0, + "coverageCount": "0/1", + "status": "low" + }, { "filePath": "projects/cli/stories/components/tag.stories.ts", "type": "variable", diff --git a/projects/cli/schematics/components/components-generator.ts b/projects/cli/schematics/components/components-generator.ts index 33abddb..83d5b19 100644 --- a/projects/cli/schematics/components/components-generator.ts +++ b/projects/cli/schematics/components/components-generator.ts @@ -1,4 +1,4 @@ export interface ComponentGeneratorSchema { - components: ('avatar' | 'badge' | 'pin' | 'tag')[]; + components: ('avatar' | 'badge' | 'pin' | 'switch' | 'tag')[]; path: string; } diff --git a/projects/cli/schematics/components/files/switch/index.ts b/projects/cli/schematics/components/files/switch/index.ts new file mode 100644 index 0000000..7f5068b --- /dev/null +++ b/projects/cli/schematics/components/files/switch/index.ts @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; + +import { ZenSwitchComponent } from './zen-switch.component'; + +@NgModule({ + imports: [ZenSwitchComponent], + exports: [ZenSwitchComponent], +}) +export class ZenSwitchModule {} +export * from './zen-switch.component'; diff --git a/projects/cli/schematics/components/files/switch/zen-switch.component.html b/projects/cli/schematics/components/files/switch/zen-switch.component.html new file mode 100644 index 0000000..256c0c0 --- /dev/null +++ b/projects/cli/schematics/components/files/switch/zen-switch.component.html @@ -0,0 +1,11 @@ + diff --git a/projects/cli/schematics/components/files/switch/zen-switch.component.scss b/projects/cli/schematics/components/files/switch/zen-switch.component.scss new file mode 100644 index 0000000..bedb856 --- /dev/null +++ b/projects/cli/schematics/components/files/switch/zen-switch.component.scss @@ -0,0 +1,62 @@ +// Define SCSS variables for better maintainability +$switch-width: 36px; +$switch-height: 22px; +$slider-size: 16px; +$transition-duration: 0.4s; +$switch-bg-color: grey; +$switch-checked-bg-color: green; +$switch-disabled-bg-color: lightgrey; +$slider-bg-color: white; + +// Other +$slider-padding: calc(($switch-height - $slider-size) / 2); +$slider-transform-distance: calc( + $switch-width - $slider-size - $slider-padding * 2 +); + +/* Switch container */ +.switch { + position: relative; + display: inline-block; + width: $switch-width; + height: $switch-height; + background-color: $switch-bg-color; + border: none; + border-radius: 99999px; // force perfectly rounded + cursor: pointer; + transition: background-color $transition-duration; + + /* + outline: none; + &:focus { + box-shadow: 0 0 3px 2px rgba(21, 156, 228, 0.4); + } + */ +} + +/* Slider */ +.slider { + position: absolute; + top: $slider-padding; + left: $slider-padding; + height: $slider-size; + width: $slider-size; + background-color: $slider-bg-color; + transition: transform $transition-duration; + border-radius: 50%; +} + +/* Styles when the button is checked */ +.switch[aria-checked='true'] { + background-color: $switch-checked-bg-color; + + .slider { + transform: translateX($slider-transform-distance); + } +} + +/* Styles when the button is disabled */ +.switch[aria-disabled='true'] { + background-color: $switch-disabled-bg-color; + cursor: not-allowed; +} diff --git a/projects/cli/schematics/components/files/switch/zen-switch.component.spec.ts b/projects/cli/schematics/components/files/switch/zen-switch.component.spec.ts new file mode 100644 index 0000000..ecad8c4 --- /dev/null +++ b/projects/cli/schematics/components/files/switch/zen-switch.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ZenSwitchComponent } from './zen-switch.component'; + +describe('SwitchComponent', () => { + let component: ZenSwitchComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ZenSwitchComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ZenSwitchComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/cli/schematics/components/files/switch/zen-switch.component.ts b/projects/cli/schematics/components/files/switch/zen-switch.component.ts new file mode 100644 index 0000000..bcfdc6b --- /dev/null +++ b/projects/cli/schematics/components/files/switch/zen-switch.component.ts @@ -0,0 +1,123 @@ +import { NgIf } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + forwardRef, + model, +} from '@angular/core'; +import { + ControlValueAccessor, + FormsModule, + NG_VALUE_ACCESSOR, +} from '@angular/forms'; + +type OnChangeFn = (value: boolean) => void; +type OnTouchedFn = () => void; + +/** + * ZenSwitchComponent is a custom switch component that implements ControlValueAccessor to work seamlessly with Angular forms. + * + * @example + * + * @export + * @class ZenSwitchComponent + * @implements {ControlValueAccessor} + * + * @license BSD-2-Clause + * @author Konrad Stępień + * @link https://github.com/Kordrad/ng-zen + */ +@Component({ + selector: 'zen-switch', + standalone: true, + templateUrl: './zen-switch.component.html', + styleUrl: './zen-switch.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ZenSwitchComponent), + multi: true, + }, + ], + imports: [FormsModule, NgIf], +}) +export class ZenSwitchComponent implements ControlValueAccessor { + /** Model for the checked state of the switch. */ + checked = model(false); + + /** Model for the disabled state of the switch. */ + disabled = model(false); + + /** @ignore */ + private onChange: OnChangeFn = () => {}; + /** @ignore */ + private onTouched: OnTouchedFn = () => {}; + + /** + * Writes a new value to the component. + * @ignore + */ + writeValue(value: boolean): void { + this.checked.set(value); + } + + /** + * Registers a function to be called when the value changes. + * @ignore + */ + registerOnChange(fn: OnChangeFn): void { + this.onChange = fn; + } + + /** + * Registers a function to be called when the component is touched. + * @ignore + */ + registerOnTouched(fn: OnTouchedFn): void { + this.onTouched = fn; + } + + /** + * Sets the disabled state of the component. + * @ignore + */ + setDisabledState(isDisabled: boolean): void { + this.disabled.set(isDisabled); + } + + /** + * Toggles the switch value and notifies the change. + */ + onToggle(check?: boolean): void { + if (this.disabled()) return; + + const value = check ?? !this.checked(); + + this.checked.set(value); + this.onChange(value); + this.onTouched(); + } + + /** + * Handles keyboard events for accessibility. + */ + onKeyDown(event: KeyboardEvent): void { + switch (event.code) { + case 'Enter': + case 'Space': { + event.preventDefault(); + this.onToggle(); + break; + } + case 'ArrowRight': { + this.onToggle(true); + break; + } + case 'ArrowLeft': { + this.onToggle(false); + break; + } + } + } +} diff --git a/projects/cli/schematics/components/schema.json b/projects/cli/schematics/components/schema.json index c65d840..59b3ab2 100644 --- a/projects/cli/schematics/components/schema.json +++ b/projects/cli/schematics/components/schema.json @@ -10,7 +10,7 @@ "type": "array", "items": { "type": "string", - "enum": ["avatar", "badge", "pin", "tag"] + "enum": ["avatar", "badge", "pin", "switch", "tag"] }, "multiselect": true, "x-prompt": "Which component should be generated?" diff --git a/projects/cli/stories/components/switch.stories.ts b/projects/cli/stories/components/switch.stories.ts new file mode 100644 index 0000000..92db702 --- /dev/null +++ b/projects/cli/stories/components/switch.stories.ts @@ -0,0 +1,43 @@ +import { FormsModule } from '@angular/forms'; +import type { Meta, StoryObj } from '@storybook/angular'; +import { moduleMetadata } from '@storybook/angular'; + +import { ZenSwitchComponent } from '../../schematics/components/files/switch'; + +export default { + title: 'Components/Switch', + component: ZenSwitchComponent, + tags: ['autodocs'], + decorators: [ + moduleMetadata({ + imports: [FormsModule], + }), + ], + render: args => ({ props: { ...args } }), +} satisfies Meta; + +type Story = StoryObj; + +export const Default: Story = { + render: () => ({ + template: ` + + `, + }), +}; + +export const Disabled: Story = { + render: () => ({ + template: ``, + }), +}; + +export const Checked: Story = { + render: () => ({ + template: ` + +
+ + `, + }), +};