@ng-primitives/mcp 0.126.0 → 0.128.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/src/generated/api-data.json +881 -263
- package/src/generated/primitives-data.json +426 -39
- package/src/generated/reusable-components.json +27 -20
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
}
|
|
77
77
|
],
|
|
78
78
|
"reusableComponent": {
|
|
79
|
-
"code": "import { Component, input } from '@angular/core';\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport { heroChevronDownMini } from '@ng-icons/heroicons/mini';\nimport {\n NgpAccordionContent,\n NgpAccordionItem,\n NgpAccordionTrigger,\n} from 'ng-primitives/accordion';\nimport { NgpButton } from 'ng-primitives/button';\n\n@Component({\n selector: 'app-accordion-item',\n hostDirectives: [\n {\n directive: NgpAccordionItem,\n inputs: ['ngpAccordionItemValue:value', 'ngpAccordionItemDisabled:disabled'],\n },\n ],\n imports: [NgpAccordionContent, NgpAccordionTrigger, NgpButton, NgIcon],\n providers: [provideIcons({ heroChevronDownMini })],\n template: `\n <button ngpAccordionTrigger ngpButton>\n {{ heading() }}\n\n <ng-icon name=\"heroChevronDownMini\" />\n </button>\n <div ngpAccordionContent>\n <ng-content />\n </div>\n `,\n styles: `\n :host {\n display: block;\n }\n\n :host:has(+ :host) {\n border-bottom: 1px solid var(--ngp-border);\n }\n\n [ngpAccordionTrigger] {\n display: flex;\n padding-left: 1rem;\n padding-right: 1rem;\n font-size: 0.875rem;\n line-height: 1.25rem;\n font-weight: 500;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n height: 2.75rem;\n border-radius: 0.75rem;\n outline: none;\n color: var(--ngp-text-primary);\n background-color: var(--ngp-background);\n border: none;\n }\n\n [ngpAccordionTrigger][data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n }\n\n [ngpAccordionContent] {\n font-size: 0.875rem;\n color: var(--ngp-text-secondary);\n overflow: hidden;\n padding: 0 16px;\n box-sizing: border-box;\n }\n\n [ngpAccordionContent][data-open] {\n animation: slideDown 0.2s ease-in-out forwards;\n }\n\n [ngpAccordionContent][data-closed] {\n animation: slideUp 0.2s ease-in-out forwards;\n }\n\n ng-icon {\n font-size: 1.25rem;\n color: var(--ngp-text-secondary);\n }\n\n [ngpAccordionTrigger][data-open] ng-icon {\n transform: rotate(180deg);\n }\n\n @keyframes slideDown {\n from {\n height: 0;\n }\n to {\n height: var(--ngp-accordion-content-height);\n }\n }\n\n @keyframes slideUp {\n from {\n height: var(--ngp-accordion-content-height);\n }\n to {\n height: 0;\n }\n }\n `,\n})\nexport class AccordionItem {\n /** The accordion item heading */\n readonly heading = input.required<string>();\n}\n",
|
|
79
|
+
"code": "import { Component, input } from '@angular/core';\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport { heroChevronDownMini } from '@ng-icons/heroicons/mini';\nimport {\n NgpAccordionContent,\n NgpAccordionItem,\n NgpAccordionTrigger,\n} from 'ng-primitives/accordion';\nimport { NgpButton } from 'ng-primitives/button';\n\n@Component({\n selector: 'app-accordion-item',\n hostDirectives: [\n {\n directive: NgpAccordionItem,\n inputs: ['ngpAccordionItemValue:value', 'ngpAccordionItemDisabled:disabled'],\n },\n ],\n imports: [NgpAccordionContent, NgpAccordionTrigger, NgpButton, NgIcon],\n providers: [provideIcons({ heroChevronDownMini })],\n template: `\n <button ngpAccordionTrigger ngpButton>\n {{ heading() }}\n\n <ng-icon name=\"heroChevronDownMini\" />\n </button>\n <div ngpAccordionContent>\n <ng-content />\n </div>\n `,\n styles: `\n :host {\n display: block;\n }\n\n :host:has(+ :host) {\n border-bottom: 1px solid var(--ngp-border);\n }\n\n [ngpAccordionTrigger] {\n display: flex;\n padding-left: 1rem;\n padding-right: 1rem;\n font-size: 0.875rem;\n line-height: 1.25rem;\n font-weight: 500;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n height: 2.75rem;\n border-radius: 0.75rem;\n outline: none;\n color: var(--ngp-text-primary);\n background-color: var(--ngp-background);\n border: none;\n }\n\n [ngpAccordionTrigger][data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n }\n\n [ngpAccordionContent] {\n font-size: 0.875rem;\n color: var(--ngp-text-secondary);\n overflow: hidden;\n padding: 0 16px;\n box-sizing: border-box;\n }\n\n [ngpAccordionContent][data-open] {\n animation: slideDown 0.2s ease-in-out forwards;\n }\n\n [ngpAccordionContent][data-closed] {\n animation: slideUp 0.2s ease-in-out forwards;\n }\n\n ng-icon {\n font-size: 1.25rem;\n color: var(--ngp-text-secondary);\n }\n\n [ngpAccordionTrigger][data-open] ng-icon {\n transform: rotate(180deg);\n }\n\n @keyframes slideDown {\n from {\n height: 0;\n }\n to {\n height: var(--ngp-accordion-content-height);\n }\n }\n\n @keyframes slideUp {\n from {\n height: var(--ngp-accordion-content-height);\n }\n to {\n height: 0;\n }\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n [ngpAccordionContent][data-open],\n [ngpAccordionContent][data-closed] {\n animation-duration: 0s;\n }\n }\n `,\n})\nexport class AccordionItem {\n /** The accordion item heading */\n readonly heading = input.required<string>();\n}\n",
|
|
80
80
|
"hasVariants": false,
|
|
81
81
|
"hasSizes": true
|
|
82
82
|
}
|
|
@@ -93,22 +93,51 @@
|
|
|
93
93
|
"NgpPromptComposerDictation",
|
|
94
94
|
"NgpThreadViewport",
|
|
95
95
|
"NgpThreadSuggestion",
|
|
96
|
+
"NgpPromptComposerStateToken",
|
|
97
|
+
"ngpPromptComposer",
|
|
96
98
|
"providePromptComposerState",
|
|
97
99
|
"injectPromptComposerState",
|
|
100
|
+
"type NgpPromptComposerState",
|
|
101
|
+
"type NgpPromptComposerProps",
|
|
102
|
+
"NgpPromptComposerDictationStateToken",
|
|
103
|
+
"ngpPromptComposerDictation",
|
|
98
104
|
"providePromptComposerDictationState",
|
|
99
105
|
"injectPromptComposerDictationState",
|
|
106
|
+
"type NgpPromptComposerDictationState",
|
|
107
|
+
"type NgpPromptComposerDictationProps",
|
|
108
|
+
"NgpPromptComposerInputStateToken",
|
|
109
|
+
"ngpPromptComposerInput",
|
|
100
110
|
"providePromptComposerInputState",
|
|
101
111
|
"injectPromptComposerInputState",
|
|
112
|
+
"type NgpPromptComposerInputState",
|
|
113
|
+
"NgpPromptComposerSubmitStateToken",
|
|
114
|
+
"ngpPromptComposerSubmit",
|
|
102
115
|
"providePromptComposerSubmitState",
|
|
103
116
|
"injectPromptComposerSubmitState",
|
|
117
|
+
"type NgpPromptComposerSubmitState",
|
|
118
|
+
"type NgpPromptComposerSubmitProps",
|
|
119
|
+
"NgpThreadStateToken",
|
|
120
|
+
"ngpThread",
|
|
104
121
|
"provideThreadState",
|
|
105
122
|
"injectThreadState",
|
|
123
|
+
"type NgpThreadState",
|
|
124
|
+
"NgpThreadSuggestionStateToken",
|
|
125
|
+
"ngpThreadSuggestion",
|
|
106
126
|
"provideThreadSuggestionState",
|
|
107
127
|
"injectThreadSuggestionState",
|
|
128
|
+
"type NgpThreadSuggestionState",
|
|
129
|
+
"type NgpThreadSuggestionProps",
|
|
130
|
+
"NgpThreadViewportStateToken",
|
|
131
|
+
"ngpThreadViewport",
|
|
108
132
|
"provideThreadViewportState",
|
|
109
133
|
"injectThreadViewportState",
|
|
134
|
+
"type NgpThreadViewportState",
|
|
135
|
+
"type NgpThreadViewportProps",
|
|
136
|
+
"NgpThreadMessageStateToken",
|
|
137
|
+
"ngpThreadMessage",
|
|
110
138
|
"provideThreadMessageState",
|
|
111
|
-
"injectThreadMessageState"
|
|
139
|
+
"injectThreadMessageState",
|
|
140
|
+
"type NgpThreadMessageState"
|
|
112
141
|
],
|
|
113
142
|
"hasSecondaryEntryPoint": true,
|
|
114
143
|
"category": "utility",
|
|
@@ -281,7 +310,7 @@
|
|
|
281
310
|
}
|
|
282
311
|
],
|
|
283
312
|
"reusableComponent": {
|
|
284
|
-
"code": "import { Component, input } from '@angular/core';\nimport { NgpButton } from 'ng-primitives/button';\n\n/**\n * The size of the button.\n */\nexport type ButtonSize = 'sm' | 'md' | 'lg' | 'xl';\n\n/**\n * The variant of the button.\n */\nexport type ButtonVariant = 'primary' | 'secondary' | 'destructive' | 'outline' | 'ghost' | 'link';\n\n@Component({\n selector: 'button[app-button]',\n hostDirectives: [{ directive: NgpButton, inputs: ['disabled'] }],\n template: `\n <ng-content select=\"[slot=leading]\" />\n <ng-content />\n <ng-content select=\"[slot=trailing]\" />\n `,\n host: {\n '[attr.data-size]': 'size()',\n '[attr.data-variant]': 'variant()',\n },\n styles: `\n :host {\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n color: var(--ngp-text-primary);\n border: none;\n height: 2.5rem;\n font-weight: 500;\n background-color: var(--ngp-background);\n transition: background-color 300ms cubic-bezier(0.4, 0, 0.2, 1);\n box-shadow: var(--ngp-button-shadow);\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n gap: 0.5rem;\n }\n\n :host[data-hover] {\n background-color: var(--ngp-background-hover);\n }\n\n :host[data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n }\n\n :host[data-press] {\n background-color: var(--ngp-background-active);\n }\n\n /* Size variants */\n :host[data-size='sm'] {\n height: 2rem;\n padding-left: 0.75rem;\n padding-right: 0.75rem;\n font-size: 0.875rem;\n --ng-icon__size: 0.875rem;\n }\n\n :host[data-size='md'],\n :host:not([data-size]) {\n height: 2.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n font-size: 0.875rem;\n --ng-icon__size: 0.875rem;\n }\n\n :host[data-size='lg'] {\n height: 3rem;\n padding-left: 1.25rem;\n padding-right: 1.25rem;\n font-size: 1rem;\n --ng-icon__size: 1rem;\n }\n\n :host[data-size='xl'] {\n height: 3.5rem;\n padding-left: 1.5rem;\n padding-right: 1.5rem;\n font-size: 1.125rem;\n --ng-icon__size: 1.125rem;\n }\n\n /* Variant styles */\n :host[data-variant='primary'],\n :host:not([data-variant]) {\n background-color: var(--ngp-background);\n color: var(--ngp-text-primary);\n border: none;\n }\n\n :host[data-variant='primary'][data-hover],\n :host:not([data-variant])[data-hover] {\n background-color: var(--ngp-background-hover);\n }\n\n :host[data-variant='primary'][data-press],\n :host:not([data-variant])[data-press] {\n background-color: var(--ngp-background-active);\n }\n\n :host[data-variant='secondary'] {\n background-color: var(--ngp-secondary-background, #f1f5f9);\n color: var(--ngp-secondary-text, #0f172a);\n border: none;\n }\n\n :host[data-variant='secondary'][data-hover] {\n background-color: var(--ngp-secondary-background-hover, #e2e8f0);\n }\n\n :host[data-variant='secondary'][data-press] {\n background-color: var(--ngp-secondary-background-active, #cbd5e1);\n }\n\n :host[data-variant='destructive'] {\n background-color: var(--ngp-destructive-background, #ef4444);\n color: var(--ngp-destructive-text, #ffffff);\n border: none;\n }\n\n :host[data-variant='destructive'][data-hover] {\n background-color: var(--ngp-destructive-background-hover, #dc2626);\n }\n\n :host[data-variant='destructive'][data-press] {\n background-color: var(--ngp-destructive-background-active, #b91c1c);\n }\n\n :host[data-variant='outline'] {\n background-color: transparent;\n color: var(--ngp-text-primary);\n border: 1px solid var(--ngp-outline-border, #e2e8f0);\n box-shadow: none;\n }\n\n :host[data-variant='outline'][data-hover] {\n background-color: var(--ngp-background-hover);\n border-color: var(--ngp-outline-border-hover, #cbd5e1);\n }\n\n :host[data-variant='outline'][data-press] {\n background-color: var(--ngp-outline-background-active, rgba(15, 23, 42, 0.1));\n }\n\n :host[data-variant='ghost'] {\n background-color: transparent;\n color: var(--ngp-text-primary);\n border: none;\n box-shadow: none;\n }\n\n :host[data-variant='ghost'][data-hover] {\n background-color: var(--ngp-background-hover);\n }\n\n :host[data-variant='ghost'][data-press] {\n background-color: var(--ngp-background-active);\n }\n\n :host[data-variant='link'] {\n background-color: transparent;\n color: var(--ngp-link-color, #3b82f6);\n border: none;\n box-shadow: none;\n text-decoration: underline;\n text-underline-offset: 4px;\n }\n\n :host[data-variant='link'][data-hover] {\n text-decoration-thickness: 2px;\n }\n\n :host[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n }\n `,\n})\nexport class Button {\n /**\n * The size of the button.\n */\n readonly size = input<ButtonSize>('md');\n\n /**\n * The variant of the button.\n */\n readonly variant = input<ButtonVariant>('primary');\n}\n",
|
|
313
|
+
"code": "import { Component, input } from '@angular/core';\nimport { NgpButton } from 'ng-primitives/button';\n\n/**\n * The size of the button.\n */\nexport type ButtonSize = 'sm' | 'md' | 'lg' | 'xl';\n\n/**\n * The variant of the button.\n */\nexport type ButtonVariant = 'primary' | 'secondary' | 'destructive' | 'outline' | 'ghost' | 'link';\n\n@Component({\n selector: 'button[app-button]',\n hostDirectives: [{ directive: NgpButton, inputs: ['disabled'] }],\n template: `\n <ng-content select=\"[slot=leading]\" />\n <ng-content />\n <ng-content select=\"[slot=trailing]\" />\n `,\n host: {\n '[attr.data-size]': 'size()',\n '[attr.data-variant]': 'variant()',\n },\n styles: `\n :host {\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n color: var(--ngp-text-primary);\n border: none;\n height: 2.5rem;\n font-weight: 500;\n background-color: var(--ngp-background);\n transition: background-color 300ms cubic-bezier(0.4, 0, 0.2, 1);\n box-shadow: var(--ngp-button-shadow);\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n gap: 0.5rem;\n }\n\n :host[data-hover] {\n background-color: var(--ngp-background-hover);\n }\n\n :host[data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n }\n\n :host[data-press] {\n background-color: var(--ngp-background-active);\n }\n\n /* Size variants */\n :host[data-size='sm'] {\n height: 2rem;\n padding-left: 0.75rem;\n padding-right: 0.75rem;\n font-size: 0.875rem;\n --ng-icon__size: 0.875rem;\n }\n\n :host[data-size='md'],\n :host:not([data-size]) {\n height: 2.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n font-size: 0.875rem;\n --ng-icon__size: 0.875rem;\n }\n\n :host[data-size='lg'] {\n height: 3rem;\n padding-left: 1.25rem;\n padding-right: 1.25rem;\n font-size: 1rem;\n --ng-icon__size: 1rem;\n }\n\n :host[data-size='xl'] {\n height: 3.5rem;\n padding-left: 1.5rem;\n padding-right: 1.5rem;\n font-size: 1.125rem;\n --ng-icon__size: 1.125rem;\n }\n\n /* Variant styles */\n :host[data-variant='primary'],\n :host:not([data-variant]) {\n background-color: var(--ngp-background);\n color: var(--ngp-text-primary);\n border: none;\n }\n\n :host[data-variant='primary'][data-hover],\n :host:not([data-variant])[data-hover] {\n background-color: var(--ngp-background-hover);\n }\n\n :host[data-variant='primary'][data-press],\n :host:not([data-variant])[data-press] {\n background-color: var(--ngp-background-active);\n }\n\n :host[data-variant='secondary'] {\n background-color: var(--ngp-secondary-background, #f1f5f9);\n color: var(--ngp-secondary-text, #0f172a);\n border: none;\n }\n\n :host[data-variant='secondary'][data-hover] {\n background-color: var(--ngp-secondary-background-hover, #e2e8f0);\n }\n\n :host[data-variant='secondary'][data-press] {\n background-color: var(--ngp-secondary-background-active, #cbd5e1);\n }\n\n :host[data-variant='destructive'] {\n background-color: var(--ngp-destructive-background, #ef4444);\n color: var(--ngp-destructive-text, #ffffff);\n border: none;\n }\n\n :host[data-variant='destructive'][data-hover] {\n background-color: var(--ngp-destructive-background-hover, #dc2626);\n }\n\n :host[data-variant='destructive'][data-press] {\n background-color: var(--ngp-destructive-background-active, #b91c1c);\n }\n\n :host[data-variant='outline'] {\n background-color: transparent;\n color: var(--ngp-text-primary);\n border: 1px solid var(--ngp-outline-border, #e2e8f0);\n box-shadow: none;\n }\n\n :host[data-variant='outline'][data-hover] {\n background-color: var(--ngp-background-hover);\n border-color: var(--ngp-outline-border-hover, #cbd5e1);\n }\n\n :host[data-variant='outline'][data-press] {\n background-color: var(--ngp-outline-background-active, rgba(15, 23, 42, 0.1));\n }\n\n :host[data-variant='ghost'] {\n background-color: transparent;\n color: var(--ngp-text-primary);\n border: none;\n box-shadow: none;\n }\n\n :host[data-variant='ghost'][data-hover] {\n background-color: var(--ngp-background-hover);\n }\n\n :host[data-variant='ghost'][data-press] {\n background-color: var(--ngp-background-active);\n }\n\n :host[data-variant='link'] {\n background-color: transparent;\n color: var(--ngp-link-color, #3b82f6);\n border: none;\n box-shadow: none;\n text-decoration: underline;\n text-underline-offset: 4px;\n }\n\n :host[data-variant='link'][data-hover] {\n text-decoration-thickness: 2px;\n }\n\n :host[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n :host {\n transition-duration: 0s;\n }\n }\n `,\n})\nexport class Button {\n /**\n * The size of the button.\n */\n readonly size = input<ButtonSize>('md');\n\n /**\n * The variant of the button.\n */\n readonly variant = input<ButtonVariant>('primary');\n}\n",
|
|
285
314
|
"hasVariants": true,
|
|
286
315
|
"hasSizes": true
|
|
287
316
|
}
|
|
@@ -316,6 +345,151 @@
|
|
|
316
345
|
"hasSizes": true
|
|
317
346
|
}
|
|
318
347
|
},
|
|
348
|
+
{
|
|
349
|
+
"name": "collapsible",
|
|
350
|
+
"entryPoint": "ng-primitives/collapsible",
|
|
351
|
+
"exports": [
|
|
352
|
+
"NgpCollapsible",
|
|
353
|
+
"NgpCollapsibleStateToken",
|
|
354
|
+
"ngpCollapsible",
|
|
355
|
+
"injectCollapsibleState",
|
|
356
|
+
"provideCollapsibleState",
|
|
357
|
+
"type NgpCollapsibleState",
|
|
358
|
+
"type NgpCollapsibleProps",
|
|
359
|
+
"NgpCollapsibleTrigger",
|
|
360
|
+
"NgpCollapsibleTriggerStateToken",
|
|
361
|
+
"ngpCollapsibleTrigger",
|
|
362
|
+
"injectCollapsibleTriggerState",
|
|
363
|
+
"provideCollapsibleTriggerState",
|
|
364
|
+
"type NgpCollapsibleTriggerState",
|
|
365
|
+
"type NgpCollapsibleTriggerProps",
|
|
366
|
+
"NgpCollapsibleContent",
|
|
367
|
+
"NgpCollapsibleContentStateToken",
|
|
368
|
+
"ngpCollapsibleContent",
|
|
369
|
+
"injectCollapsibleContentState",
|
|
370
|
+
"provideCollapsibleContentState",
|
|
371
|
+
"type NgpCollapsibleContentState",
|
|
372
|
+
"type NgpCollapsibleContentProps"
|
|
373
|
+
],
|
|
374
|
+
"hasSecondaryEntryPoint": true,
|
|
375
|
+
"category": "utility",
|
|
376
|
+
"description": "A collapsible shows and hides an associated section of content, following the WAI-ARIA disclosure pattern.",
|
|
377
|
+
"accessibility": [
|
|
378
|
+
"ARIA disclosure",
|
|
379
|
+
"ARIA design"
|
|
380
|
+
],
|
|
381
|
+
"examples": [
|
|
382
|
+
{
|
|
383
|
+
"name": "example-0",
|
|
384
|
+
"code": "import {\n NgpCollapsible,\n NgpCollapsibleTrigger,\n NgpCollapsibleContent,\n} from 'ng-primitives/collapsible';",
|
|
385
|
+
"description": "Import"
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
"name": "example-1",
|
|
389
|
+
"code": "<div ngpCollapsible>\n <button ngpCollapsibleTrigger>Show details</button>\n <div ngpCollapsibleContent>The content that is shown and hidden.</div>\n</div>",
|
|
390
|
+
"description": "Usage"
|
|
391
|
+
}
|
|
392
|
+
]
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
"name": "color",
|
|
396
|
+
"entryPoint": "ng-primitives/color",
|
|
397
|
+
"exports": [
|
|
398
|
+
"Color",
|
|
399
|
+
"ColorSpace",
|
|
400
|
+
"ColorChannel",
|
|
401
|
+
"ColorChannelRange",
|
|
402
|
+
"NgpColorSlider",
|
|
403
|
+
"NgpColorSliderStateToken",
|
|
404
|
+
"ngpColorSlider",
|
|
405
|
+
"injectColorSliderState",
|
|
406
|
+
"provideColorSliderState",
|
|
407
|
+
"type NgpColorSliderState",
|
|
408
|
+
"type NgpColorSliderProps",
|
|
409
|
+
"NgpColorSliderTrack",
|
|
410
|
+
"NgpColorSliderTrackStateToken",
|
|
411
|
+
"ngpColorSliderTrack",
|
|
412
|
+
"injectColorSliderTrackState",
|
|
413
|
+
"provideColorSliderTrackState",
|
|
414
|
+
"type NgpColorSliderTrackState",
|
|
415
|
+
"type NgpColorSliderTrackProps",
|
|
416
|
+
"NgpColorSliderThumb",
|
|
417
|
+
"NgpColorSliderThumbStateToken",
|
|
418
|
+
"ngpColorSliderThumb",
|
|
419
|
+
"injectColorSliderThumbState",
|
|
420
|
+
"provideColorSliderThumbState",
|
|
421
|
+
"type NgpColorSliderThumbState",
|
|
422
|
+
"type NgpColorSliderThumbProps",
|
|
423
|
+
"NgpColorArea",
|
|
424
|
+
"NgpColorAreaStateToken",
|
|
425
|
+
"ngpColorArea",
|
|
426
|
+
"injectColorAreaState",
|
|
427
|
+
"provideColorAreaState",
|
|
428
|
+
"type NgpColorAreaState",
|
|
429
|
+
"type NgpColorAreaProps",
|
|
430
|
+
"NgpColorAreaThumb",
|
|
431
|
+
"NgpColorAreaThumbStateToken",
|
|
432
|
+
"ngpColorAreaThumb",
|
|
433
|
+
"injectColorAreaThumbState",
|
|
434
|
+
"provideColorAreaThumbState",
|
|
435
|
+
"type NgpColorAreaThumbState",
|
|
436
|
+
"type NgpColorAreaThumbProps",
|
|
437
|
+
"NgpColorField",
|
|
438
|
+
"NgpColorFieldStateToken",
|
|
439
|
+
"ngpColorField",
|
|
440
|
+
"injectColorFieldState",
|
|
441
|
+
"provideColorFieldState",
|
|
442
|
+
"type NgpColorFieldState",
|
|
443
|
+
"type NgpColorFieldProps",
|
|
444
|
+
"NgpColorPicker",
|
|
445
|
+
"NgpColorPickerStateToken",
|
|
446
|
+
"ngpColorPicker",
|
|
447
|
+
"injectColorPickerState",
|
|
448
|
+
"provideColorPickerState",
|
|
449
|
+
"type NgpColorPickerState",
|
|
450
|
+
"type NgpColorPickerProps",
|
|
451
|
+
"NgpColorSwatch",
|
|
452
|
+
"NgpColorSwatchStateToken",
|
|
453
|
+
"ngpColorSwatch",
|
|
454
|
+
"injectColorSwatchState",
|
|
455
|
+
"provideColorSwatchState",
|
|
456
|
+
"type NgpColorSwatchState",
|
|
457
|
+
"type NgpColorSwatchProps",
|
|
458
|
+
"NgpColorWheel",
|
|
459
|
+
"NgpColorWheelStateToken",
|
|
460
|
+
"ngpColorWheel",
|
|
461
|
+
"injectColorWheelState",
|
|
462
|
+
"provideColorWheelState",
|
|
463
|
+
"type NgpColorWheelState",
|
|
464
|
+
"type NgpColorWheelProps",
|
|
465
|
+
"NgpColorWheelThumb",
|
|
466
|
+
"NgpColorWheelThumbStateToken",
|
|
467
|
+
"ngpColorWheelThumb",
|
|
468
|
+
"injectColorWheelThumbState",
|
|
469
|
+
"provideColorWheelThumbState",
|
|
470
|
+
"type NgpColorWheelThumbState",
|
|
471
|
+
"type NgpColorWheelThumbProps",
|
|
472
|
+
"NgpColorSwatchPicker",
|
|
473
|
+
"NgpColorSwatchPickerStateToken",
|
|
474
|
+
"ngpColorSwatchPicker",
|
|
475
|
+
"injectColorSwatchPickerState",
|
|
476
|
+
"provideColorSwatchPickerState",
|
|
477
|
+
"type NgpColorSwatchPickerState",
|
|
478
|
+
"type NgpColorSwatchPickerProps",
|
|
479
|
+
"NgpColorSwatchPickerItem",
|
|
480
|
+
"NgpColorSwatchPickerItemStateToken",
|
|
481
|
+
"ngpColorSwatchPickerItem",
|
|
482
|
+
"injectColorSwatchPickerItemState",
|
|
483
|
+
"provideColorSwatchPickerItemState",
|
|
484
|
+
"type NgpColorSwatchPickerItemState",
|
|
485
|
+
"type NgpColorSwatchPickerItemProps"
|
|
486
|
+
],
|
|
487
|
+
"hasSecondaryEntryPoint": true,
|
|
488
|
+
"category": "utility",
|
|
489
|
+
"description": "color primitive component",
|
|
490
|
+
"accessibility": [],
|
|
491
|
+
"examples": []
|
|
492
|
+
},
|
|
319
493
|
{
|
|
320
494
|
"name": "combobox",
|
|
321
495
|
"entryPoint": "ng-primitives/combobox",
|
|
@@ -373,7 +547,7 @@
|
|
|
373
547
|
}
|
|
374
548
|
],
|
|
375
549
|
"reusableComponent": {
|
|
376
|
-
"code": "import { BooleanInput } from '@angular/cdk/coercion';\nimport { booleanAttribute, Component, computed, input, model, signal } from '@angular/core';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport { heroChevronDown } from '@ng-icons/heroicons/outline';\nimport {\n NgpCombobox,\n NgpComboboxButton,\n NgpComboboxDropdown,\n NgpComboboxInput,\n NgpComboboxOption,\n NgpComboboxPortal,\n} from 'ng-primitives/combobox';\nimport { ChangeFn, provideValueAccessor, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'app-combobox',\n imports: [\n NgpCombobox,\n NgpComboboxDropdown,\n NgpComboboxOption,\n NgpComboboxInput,\n NgpComboboxPortal,\n NgpComboboxButton,\n NgIcon,\n ],\n providers: [provideIcons({ heroChevronDown }), provideValueAccessor(Combobox)],\n template: `\n <div\n [(ngpComboboxValue)]=\"value\"\n [ngpComboboxDisabled]=\"disabled() || formDisabled()\"\n (ngpComboboxOpenChange)=\"resetOnClose($event)\"\n (ngpComboboxValueChange)=\"onValueChange($event)\"\n ngpCombobox\n >\n <input\n [value]=\"filter()\"\n [placeholder]=\"placeholder()\"\n (input)=\"onFilterChange($event)\"\n (blur)=\"onTouched?.()\"\n ngpComboboxInput\n />\n\n <button ngpComboboxButton aria-label=\"Toggle dropdown\">\n <ng-icon name=\"heroChevronDown\" />\n </button>\n\n <div *ngpComboboxPortal ngpComboboxDropdown>\n @for (option of filteredOptions(); track option) {\n <div [ngpComboboxOptionValue]=\"option\" ngpComboboxOption>\n {{ option }}\n </div>\n } @empty {\n <div class=\"empty-message\">No options found</div>\n }\n </div>\n </div>\n `,\n styles: `\n [ngpCombobox] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n height: 36px;\n width: 300px;\n border-radius: 8px;\n border: none;\n background-color: var(--ngp-background);\n box-shadow: var(--ngp-input-shadow);\n box-sizing: border-box;\n }\n\n [ngpCombobox][data-focus] {\n outline: 2px solid var(--ngp-focus-ring);\n outline-offset: 2px;\n }\n\n [ngpComboboxInput] {\n flex: 1;\n padding: 0 16px;\n border: none;\n background-color: transparent;\n color: var(--ngp-text);\n font-family: inherit;\n font-size: 14px;\n padding: 0 16px;\n outline: none;\n height: 100%;\n }\n\n [ngpComboboxButton] {\n display: inline-flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n width: 36px;\n background-color: transparent;\n border: none;\n color: var(--ngp-text);\n cursor: pointer;\n box-sizing: border-box;\n }\n\n [ngpComboboxDropdown] {\n background-color: var(--ngp-background);\n border: 1px solid var(--ngp-border);\n padding: 0.25rem;\n border-radius: 0.75rem;\n outline: none;\n position: absolute;\n animation: popover-show 0.1s ease-out;\n width: var(--ngp-combobox-width);\n box-shadow: var(--ngp-shadow-lg);\n box-sizing: border-box;\n margin-top: 4px;\n max-height: 240px;\n overflow-y: auto;\n transform-origin: var(--ngp-combobox-transform-origin);\n }\n\n [ngpComboboxDropdown][data-enter] {\n animation: combobox-show 0.1s ease-out;\n }\n\n [ngpComboboxDropdown][data-exit] {\n animation: combobox-hide 0.1s ease-out;\n }\n\n [ngpComboboxOption] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.375rem 0.75rem;\n cursor: pointer;\n border-radius: 0.5rem;\n width: 100%;\n height: 36px;\n font-size: 14px;\n color: var(--ngp-text-primary);\n box-sizing: border-box;\n }\n\n [ngpComboboxOption][data-hover] {\n background-color: var(--ngp-background-hover);\n }\n\n [ngpComboboxOption][data-press] {\n background-color: var(--ngp-background-active);\n }\n\n [ngpComboboxOption][data-active] {\n background-color: var(--ngp-background-active);\n }\n\n .empty-message {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0.5rem;\n color: var(--ngp-text-secondary);\n font-size: 14px;\n font-weight: 500;\n text-align: center;\n }\n\n @keyframes combobox-show {\n 0% {\n opacity: 0;\n transform: translateY(-10px) scale(0.9);\n }\n 100% {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n\n @keyframes combobox-hide {\n 0% {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n 100% {\n opacity: 0;\n transform: translateY(-10px) scale(0.9);\n }\n }\n `,\n})\nexport class Combobox implements ControlValueAccessor {\n /** The options for the combobox. */\n readonly options = input<string[]>([]);\n\n /** The selected value. */\n readonly value = model<string | undefined>();\n\n /** The placeholder for the input. */\n readonly placeholder = input<string>('');\n\n /** The disabled state of the combobox. */\n readonly disabled = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /** The filter value. */\n protected readonly filter = signal<string>('');\n\n /** Get the filtered options. */\n protected readonly filteredOptions = computed(() =>\n this.options().filter(option => option.toLowerCase().includes(this.filter().toLowerCase())),\n );\n\n /** Store the form disabled state */\n protected readonly formDisabled = signal(false);\n\n /** The on change callback */\n private onChange?: ChangeFn<string | undefined>;\n\n /** The on touch callback */\n protected onTouched?: TouchedFn;\n\n onFilterChange(event: Event): void {\n const input = event.target as HTMLInputElement;\n this.filter.set(input.value);\n }\n\n writeValue(value: string | undefined): void {\n this.value.set(value);\n this.filter.set(value ?? '');\n }\n\n registerOnChange(fn: ChangeFn<string | undefined>): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: TouchedFn): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n\n protected onValueChange(value: string): void {\n this.onChange?.(value);\n // update the filter value\n this.filter.set(value);\n }\n\n protected resetOnClose(open: boolean): void {\n // if the dropdown is closed, reset the filter value\n if (open) {\n return;\n }\n\n // if the filter value is empty, clear the value and notify the form control\n if (this.filter() === '') {\n this.value.set(undefined);\n this.onChange?.(undefined);\n } else {\n // otherwise set the filter value to the selected value\n this.filter.set(this.value() ?? '');\n }\n }\n}\n",
|
|
550
|
+
"code": "import { BooleanInput } from '@angular/cdk/coercion';\nimport { booleanAttribute, Component, computed, input, model, signal } from '@angular/core';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport { heroChevronDown } from '@ng-icons/heroicons/outline';\nimport {\n NgpCombobox,\n NgpComboboxButton,\n NgpComboboxDropdown,\n NgpComboboxInput,\n NgpComboboxOption,\n NgpComboboxPortal,\n} from 'ng-primitives/combobox';\nimport { ChangeFn, provideValueAccessor, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'app-combobox',\n imports: [\n NgpCombobox,\n NgpComboboxDropdown,\n NgpComboboxOption,\n NgpComboboxInput,\n NgpComboboxPortal,\n NgpComboboxButton,\n NgIcon,\n ],\n providers: [provideIcons({ heroChevronDown }), provideValueAccessor(Combobox)],\n template: `\n <div\n [(ngpComboboxValue)]=\"value\"\n [ngpComboboxDisabled]=\"disabled() || formDisabled()\"\n (ngpComboboxOpenChange)=\"resetOnClose($event)\"\n (ngpComboboxValueChange)=\"onValueChange($event)\"\n ngpCombobox\n >\n <input\n [value]=\"filter()\"\n [placeholder]=\"placeholder()\"\n (input)=\"onFilterChange($event)\"\n (blur)=\"onTouched?.()\"\n ngpComboboxInput\n />\n\n <button ngpComboboxButton aria-label=\"Toggle dropdown\">\n <ng-icon name=\"heroChevronDown\" />\n </button>\n\n <div *ngpComboboxPortal ngpComboboxDropdown>\n @for (option of filteredOptions(); track option) {\n <div [ngpComboboxOptionValue]=\"option\" ngpComboboxOption>\n {{ option }}\n </div>\n } @empty {\n <div class=\"empty-message\">No options found</div>\n }\n </div>\n </div>\n `,\n styles: `\n [ngpCombobox] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n height: 36px;\n width: 300px;\n border-radius: 8px;\n border: none;\n background-color: var(--ngp-background);\n box-shadow: var(--ngp-input-shadow);\n box-sizing: border-box;\n }\n\n [ngpCombobox][data-focus] {\n outline: 2px solid var(--ngp-focus-ring);\n outline-offset: 2px;\n }\n\n [ngpComboboxInput] {\n flex: 1;\n padding: 0 16px;\n border: none;\n background-color: transparent;\n color: var(--ngp-text);\n font-family: inherit;\n font-size: 14px;\n padding: 0 16px;\n outline: none;\n height: 100%;\n }\n\n [ngpComboboxButton] {\n display: inline-flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n width: 36px;\n background-color: transparent;\n border: none;\n color: var(--ngp-text);\n cursor: pointer;\n box-sizing: border-box;\n }\n\n [ngpComboboxDropdown] {\n background-color: var(--ngp-background);\n border: 1px solid var(--ngp-border);\n padding: 0.25rem;\n border-radius: 0.75rem;\n outline: none;\n position: absolute;\n animation: popover-show 0.1s ease-out;\n width: var(--ngp-combobox-width);\n box-shadow: var(--ngp-shadow-lg);\n box-sizing: border-box;\n margin-top: 4px;\n max-height: 240px;\n overflow-y: auto;\n transform-origin: var(--ngp-combobox-transform-origin);\n }\n\n [ngpComboboxDropdown][data-enter] {\n animation: combobox-show 0.1s ease-out;\n }\n\n [ngpComboboxDropdown][data-exit] {\n animation: combobox-hide 0.1s ease-out;\n }\n\n [ngpComboboxOption] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.375rem 0.75rem;\n cursor: pointer;\n border-radius: 0.5rem;\n width: 100%;\n height: 36px;\n font-size: 14px;\n color: var(--ngp-text-primary);\n box-sizing: border-box;\n }\n\n [ngpComboboxOption][data-hover] {\n background-color: var(--ngp-background-hover);\n }\n\n [ngpComboboxOption][data-press] {\n background-color: var(--ngp-background-active);\n }\n\n [ngpComboboxOption][data-active] {\n background-color: var(--ngp-background-active);\n }\n\n .empty-message {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0.5rem;\n color: var(--ngp-text-secondary);\n font-size: 14px;\n font-weight: 500;\n text-align: center;\n }\n\n @keyframes combobox-show {\n 0% {\n opacity: 0;\n transform: translateY(-10px) scale(0.9);\n }\n 100% {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n\n @keyframes combobox-hide {\n 0% {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n 100% {\n opacity: 0;\n transform: translateY(-10px) scale(0.9);\n }\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n [ngpComboboxDropdown],\n [ngpComboboxDropdown][data-enter],\n [ngpComboboxDropdown][data-exit] {\n animation-duration: 0s;\n }\n }\n `,\n})\nexport class Combobox implements ControlValueAccessor {\n /** The options for the combobox. */\n readonly options = input<string[]>([]);\n\n /** The selected value. */\n readonly value = model<string | undefined>();\n\n /** The placeholder for the input. */\n readonly placeholder = input<string>('');\n\n /** The disabled state of the combobox. */\n readonly disabled = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /** The filter value. */\n protected readonly filter = signal<string>('');\n\n /** Get the filtered options. */\n protected readonly filteredOptions = computed(() =>\n this.options().filter(option => option.toLowerCase().includes(this.filter().toLowerCase())),\n );\n\n /** Store the form disabled state */\n protected readonly formDisabled = signal(false);\n\n /** The on change callback */\n private onChange?: ChangeFn<string | undefined>;\n\n /** The on touch callback */\n protected onTouched?: TouchedFn;\n\n onFilterChange(event: Event): void {\n const input = event.target as HTMLInputElement;\n this.filter.set(input.value);\n }\n\n writeValue(value: string | undefined): void {\n this.value.set(value);\n this.filter.set(value ?? '');\n }\n\n registerOnChange(fn: ChangeFn<string | undefined>): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: TouchedFn): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n\n protected onValueChange(value: string): void {\n this.onChange?.(value);\n // update the filter value\n this.filter.set(value);\n }\n\n protected resetOnClose(open: boolean): void {\n // if the dropdown is closed, reset the filter value\n if (open) {\n return;\n }\n\n // if the filter value is empty, clear the value and notify the form control\n if (this.filter() === '') {\n this.value.set(undefined);\n this.onChange?.(undefined);\n } else {\n // otherwise set the filter value to the selected value\n this.filter.set(this.value() ?? '');\n }\n }\n}\n",
|
|
377
551
|
"hasVariants": false,
|
|
378
552
|
"hasSizes": true
|
|
379
553
|
}
|
|
@@ -422,7 +596,7 @@
|
|
|
422
596
|
}
|
|
423
597
|
],
|
|
424
598
|
"reusableComponent": {
|
|
425
|
-
"code": "import { Component } from '@angular/core';\nimport { NgpContextMenuItem } from 'ng-primitives/context-menu';\n\n@Component({\n selector: 'button[app-context-menu-item]',\n hostDirectives: [NgpContextMenuItem],\n host: { type: 'button' },\n template: `\n <ng-content />\n `,\n styles: `\n :host {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 8px 6px 14px;\n border: none;\n background: none;\n cursor: pointer;\n transition: background-color 0.2s;\n border-radius: 4px;\n min-width: 120px;\n text-align: start;\n outline: none;\n font-size: 14px;\n font-weight: 400;\n }\n\n :host[data-hover] {\n background-color: var(--ngp-background-active);\n }\n\n :host[data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n z-index: 1;\n }\n `,\n})\nexport class ContextMenuItem {}\n",
|
|
599
|
+
"code": "import { Component } from '@angular/core';\nimport { NgpContextMenuItem } from 'ng-primitives/context-menu';\n\n@Component({\n selector: 'button[app-context-menu-item]',\n hostDirectives: [NgpContextMenuItem],\n host: { type: 'button' },\n template: `\n <ng-content />\n `,\n styles: `\n :host {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 8px 6px 14px;\n border: none;\n background: none;\n cursor: pointer;\n transition: background-color 0.2s;\n border-radius: 4px;\n min-width: 120px;\n text-align: start;\n outline: none;\n font-size: 14px;\n font-weight: 400;\n }\n\n :host[data-hover] {\n background-color: var(--ngp-background-active);\n }\n\n :host[data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n z-index: 1;\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n :host {\n transition-duration: 0s;\n }\n }\n `,\n})\nexport class ContextMenuItem {}\n",
|
|
426
600
|
"hasVariants": false,
|
|
427
601
|
"hasSizes": true
|
|
428
602
|
}
|
|
@@ -456,11 +630,56 @@
|
|
|
456
630
|
"transformToFirstDayOfWeekNumber",
|
|
457
631
|
"type NgpDatePickerFirstDayOfWeekNumber",
|
|
458
632
|
"type NgpDatePickerFirstDayOfWeekNumberInput",
|
|
633
|
+
"NgpDatePickerStateToken",
|
|
634
|
+
"ngpDatePicker",
|
|
459
635
|
"injectDatePickerState",
|
|
460
636
|
"provideDatePickerState",
|
|
637
|
+
"type NgpDatePickerState",
|
|
638
|
+
"type NgpDatePickerProps",
|
|
639
|
+
"type NgpDateControllerState",
|
|
461
640
|
"NgpDateRangePicker",
|
|
641
|
+
"NgpDateRangePickerStateToken",
|
|
642
|
+
"ngpDateRangePicker",
|
|
643
|
+
"injectDateRangePickerState",
|
|
462
644
|
"provideDateRangePickerState",
|
|
463
|
-
"
|
|
645
|
+
"type NgpDateRangePickerState",
|
|
646
|
+
"type NgpDateRangePickerProps",
|
|
647
|
+
"NgpDatePickerDateButtonStateToken",
|
|
648
|
+
"ngpDatePickerDateButton",
|
|
649
|
+
"injectDatePickerDateButtonState",
|
|
650
|
+
"provideDatePickerDateButtonState",
|
|
651
|
+
"type NgpDatePickerDateButtonState",
|
|
652
|
+
"type NgpDatePickerDateButtonProps",
|
|
653
|
+
"NgpDatePickerGridStateToken",
|
|
654
|
+
"ngpDatePickerGrid",
|
|
655
|
+
"injectDatePickerGridState",
|
|
656
|
+
"provideDatePickerGridState",
|
|
657
|
+
"type NgpDatePickerGridState",
|
|
658
|
+
"type NgpDatePickerGridProps",
|
|
659
|
+
"NgpDatePickerLabelStateToken",
|
|
660
|
+
"ngpDatePickerLabel",
|
|
661
|
+
"injectDatePickerLabelState",
|
|
662
|
+
"provideDatePickerLabelState",
|
|
663
|
+
"type NgpDatePickerLabelState",
|
|
664
|
+
"type NgpDatePickerLabelProps",
|
|
665
|
+
"NgpDatePickerCellStateToken",
|
|
666
|
+
"ngpDatePickerCell",
|
|
667
|
+
"injectDatePickerCellState",
|
|
668
|
+
"provideDatePickerCellState",
|
|
669
|
+
"type NgpDatePickerCellState",
|
|
670
|
+
"type NgpDatePickerCellProps",
|
|
671
|
+
"NgpDatePickerNextMonthStateToken",
|
|
672
|
+
"ngpDatePickerNextMonth",
|
|
673
|
+
"injectDatePickerNextMonthState",
|
|
674
|
+
"provideDatePickerNextMonthState",
|
|
675
|
+
"type NgpDatePickerNextMonthState",
|
|
676
|
+
"type NgpDatePickerNextMonthProps",
|
|
677
|
+
"NgpDatePickerPreviousMonthStateToken",
|
|
678
|
+
"ngpDatePickerPreviousMonth",
|
|
679
|
+
"injectDatePickerPreviousMonthState",
|
|
680
|
+
"provideDatePickerPreviousMonthState",
|
|
681
|
+
"type NgpDatePickerPreviousMonthState",
|
|
682
|
+
"type NgpDatePickerPreviousMonthProps"
|
|
464
683
|
],
|
|
465
684
|
"hasSecondaryEntryPoint": true,
|
|
466
685
|
"category": "date-time",
|
|
@@ -486,7 +705,7 @@
|
|
|
486
705
|
}
|
|
487
706
|
],
|
|
488
707
|
"reusableComponent": {
|
|
489
|
-
"code": "import { Component, computed } from '@angular/core';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport { heroChevronLeftMini, heroChevronRightMini } from '@ng-icons/heroicons/mini';\nimport {\n injectDatePickerState,\n NgpDatePicker,\n NgpDatePickerCell,\n NgpDatePickerCellRender,\n NgpDatePickerDateButton,\n NgpDatePickerGrid,\n NgpDatePickerLabel,\n NgpDatePickerNextMonth,\n NgpDatePickerPreviousMonth,\n NgpDatePickerRowRender,\n} from 'ng-primitives/date-picker';\nimport { ChangeFn, provideValueAccessor, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'app-date-picker',\n hostDirectives: [\n {\n directive: NgpDatePicker,\n inputs: [\n 'ngpDatePickerDate: date',\n 'ngpDatePickerMin: min',\n 'ngpDatePickerMax: max',\n 'ngpDatePickerDisabled: disabled',\n ],\n outputs: ['ngpDatePickerDateChange: dateChange'],\n },\n ],\n imports: [\n NgIcon,\n NgpDatePickerLabel,\n NgpDatePickerNextMonth,\n NgpDatePickerPreviousMonth,\n NgpDatePickerGrid,\n NgpDatePickerCell,\n NgpDatePickerRowRender,\n NgpDatePickerCellRender,\n NgpDatePickerDateButton,\n ],\n providers: [\n provideIcons({ heroChevronRightMini, heroChevronLeftMini }),\n provideValueAccessor(DatePicker),\n ],\n template: `\n <div class=\"date-picker-header\">\n <button ngpDatePickerPreviousMonth aria-label=\"previous month\">\n <ng-icon name=\"heroChevronLeftMini\" />\n </button>\n <h2 ngpDatePickerLabel>{{ label() }}</h2>\n <button ngpDatePickerNextMonth aria-label=\"next month\">\n <ng-icon name=\"heroChevronRightMini\" />\n </button>\n </div>\n <table ngpDatePickerGrid>\n <thead>\n <tr>\n <th scope=\"col\" abbr=\"Sunday\">S</th>\n <th scope=\"col\" abbr=\"Monday\">M</th>\n <th scope=\"col\" abbr=\"Tuesday\">T</th>\n <th scope=\"col\" abbr=\"Wednesday\">W</th>\n <th scope=\"col\" abbr=\"Thursday\">T</th>\n <th scope=\"col\" abbr=\"Friday\">F</th>\n <th scope=\"col\" abbr=\"Saturday\">S</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngpDatePickerRowRender>\n <td *ngpDatePickerCellRender=\"let date\" ngpDatePickerCell>\n <button ngpDatePickerDateButton>\n {{ date.getDate() }}\n </button>\n </td>\n </tr>\n </tbody>\n </table>\n `,\n styles: `\n :host {\n display: inline-block;\n background-color: var(--ngp-background);\n border-radius: 12px;\n padding: 16px;\n box-shadow: var(--ngp-shadow);\n border: 1px solid var(--ngp-border);\n }\n\n .date-picker-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n height: 36px;\n margin-bottom: 16px;\n }\n\n th {\n font-size: 14px;\n font-weight: 500;\n width: 40px;\n height: 40px;\n text-align: center;\n color: var(--ngp-text-secondary);\n }\n\n [ngpDatePickerLabel] {\n font-size: 14px;\n font-weight: 500;\n color: var(--ngp-text-primary);\n }\n\n [ngpDatePickerPreviousMonth],\n [ngpDatePickerNextMonth] {\n all: unset;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 8px;\n font-size: 20px;\n border: 1px solid var(--ngp-border);\n cursor: pointer;\n }\n\n [ngpDatePickerPreviousMonth][data-hover],\n [ngpDatePickerNextMonth][data-hover] {\n background-color: var(--ngp-background-hover);\n }\n\n [ngpDatePickerPreviousMonth][data-focus-visible],\n [ngpDatePickerNextMonth][data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n }\n\n [ngpDatePickerPreviousMonth][data-press],\n [ngpDatePickerNextMonth][data-press] {\n background-color: var(--ngp-background-active);\n }\n\n [ngpDatePickerPreviousMonth][data-disabled],\n [ngpDatePickerNextMonth][data-disabled] {\n cursor: not-allowed;\n color: var(--ngp-text-disabled);\n }\n\n [ngpDatePickerDateButton] {\n all: unset;\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 8px;\n cursor: pointer;\n }\n\n [ngpDatePickerDateButton][data-today] {\n color: var(--ngp-text-blue);\n }\n\n [ngpDatePickerDateButton][data-hover] {\n background-color: var(--ngp-background-hover);\n }\n\n [ngpDatePickerDateButton][data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n outline-offset: 2px;\n }\n\n [ngpDatePickerDateButton][data-press] {\n background-color: var(--ngp-background-active);\n }\n\n [ngpDatePickerDateButton][data-outside-month] {\n color: var(--ngp-text-disabled);\n }\n\n [ngpDatePickerDateButton][data-selected] {\n background-color: var(--ngp-background-inverse);\n color: var(--ngp-text-inverse);\n }\n\n [ngpDatePickerDateButton][data-selected][data-outside-month] {\n background-color: var(--ngp-background-disabled);\n color: var(--ngp-text-disabled);\n }\n\n [ngpDatePickerDateButton][data-disabled] {\n cursor: not-allowed;\n color: var(--ngp-text-disabled);\n }\n `,\n host: {\n '(focusout)': 'onTouched?.()',\n },\n})\nexport class DatePicker implements ControlValueAccessor {\n /** Access the date picker host directive */\n private readonly state = injectDatePickerState<Date>();\n\n /**\n * Get the current focused date in string format.\n * @returns The focused date in \"February 2024\" format.\n */\n readonly label = computed(\n () =>\n `${this.state().focusedDate().toLocaleString('default', { month: 'long' })} ${this.state().focusedDate().getFullYear()}`,\n );\n\n /**\n * The onChange callback function for the date picker.\n */\n protected onChange?: ChangeFn<Date | undefined>;\n\n /**\n * The onTouched callback function for the date picker.\n */\n protected onTouched?: TouchedFn;\n\n constructor() {\n // Whenever the user interacts with the date picker, call the onChange function with the new value.\n this.state().dateChange.subscribe(date => this.onChange?.(date));\n }\n\n writeValue(date: Date): void {\n // writing a value from the model must not re-emit through onChange\n this.state().select(date, false, { emit: false });\n }\n\n registerOnChange(fn: ChangeFn<Date | undefined>): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: TouchedFn): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.state().
|
|
708
|
+
"code": "import { Component, computed } from '@angular/core';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport { heroChevronLeftMini, heroChevronRightMini } from '@ng-icons/heroicons/mini';\nimport {\n injectDatePickerState,\n NgpDatePicker,\n NgpDatePickerCell,\n NgpDatePickerCellRender,\n NgpDatePickerDateButton,\n NgpDatePickerGrid,\n NgpDatePickerLabel,\n NgpDatePickerNextMonth,\n NgpDatePickerPreviousMonth,\n NgpDatePickerRowRender,\n} from 'ng-primitives/date-picker';\nimport { ChangeFn, provideValueAccessor, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'app-date-picker',\n hostDirectives: [\n {\n directive: NgpDatePicker,\n inputs: [\n 'ngpDatePickerDate: date',\n 'ngpDatePickerMin: min',\n 'ngpDatePickerMax: max',\n 'ngpDatePickerDisabled: disabled',\n ],\n outputs: ['ngpDatePickerDateChange: dateChange'],\n },\n ],\n imports: [\n NgIcon,\n NgpDatePickerLabel,\n NgpDatePickerNextMonth,\n NgpDatePickerPreviousMonth,\n NgpDatePickerGrid,\n NgpDatePickerCell,\n NgpDatePickerRowRender,\n NgpDatePickerCellRender,\n NgpDatePickerDateButton,\n ],\n providers: [\n provideIcons({ heroChevronRightMini, heroChevronLeftMini }),\n provideValueAccessor(DatePicker),\n ],\n template: `\n <div class=\"date-picker-header\">\n <button ngpDatePickerPreviousMonth aria-label=\"previous month\">\n <ng-icon name=\"heroChevronLeftMini\" />\n </button>\n <h2 ngpDatePickerLabel>{{ label() }}</h2>\n <button ngpDatePickerNextMonth aria-label=\"next month\">\n <ng-icon name=\"heroChevronRightMini\" />\n </button>\n </div>\n <table ngpDatePickerGrid>\n <thead>\n <tr>\n <th scope=\"col\" abbr=\"Sunday\">S</th>\n <th scope=\"col\" abbr=\"Monday\">M</th>\n <th scope=\"col\" abbr=\"Tuesday\">T</th>\n <th scope=\"col\" abbr=\"Wednesday\">W</th>\n <th scope=\"col\" abbr=\"Thursday\">T</th>\n <th scope=\"col\" abbr=\"Friday\">F</th>\n <th scope=\"col\" abbr=\"Saturday\">S</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngpDatePickerRowRender>\n <td *ngpDatePickerCellRender=\"let date\" ngpDatePickerCell>\n <button ngpDatePickerDateButton>\n {{ date.getDate() }}\n </button>\n </td>\n </tr>\n </tbody>\n </table>\n `,\n styles: `\n :host {\n display: inline-block;\n background-color: var(--ngp-background);\n border-radius: 12px;\n padding: 16px;\n box-shadow: var(--ngp-shadow);\n border: 1px solid var(--ngp-border);\n }\n\n .date-picker-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n height: 36px;\n margin-bottom: 16px;\n }\n\n th {\n font-size: 14px;\n font-weight: 500;\n width: 40px;\n height: 40px;\n text-align: center;\n color: var(--ngp-text-secondary);\n }\n\n [ngpDatePickerLabel] {\n font-size: 14px;\n font-weight: 500;\n color: var(--ngp-text-primary);\n }\n\n [ngpDatePickerPreviousMonth],\n [ngpDatePickerNextMonth] {\n all: unset;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 8px;\n font-size: 20px;\n border: 1px solid var(--ngp-border);\n cursor: pointer;\n }\n\n [ngpDatePickerPreviousMonth][data-hover],\n [ngpDatePickerNextMonth][data-hover] {\n background-color: var(--ngp-background-hover);\n }\n\n [ngpDatePickerPreviousMonth][data-focus-visible],\n [ngpDatePickerNextMonth][data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n }\n\n [ngpDatePickerPreviousMonth][data-press],\n [ngpDatePickerNextMonth][data-press] {\n background-color: var(--ngp-background-active);\n }\n\n [ngpDatePickerPreviousMonth][data-disabled],\n [ngpDatePickerNextMonth][data-disabled] {\n cursor: not-allowed;\n color: var(--ngp-text-disabled);\n }\n\n [ngpDatePickerDateButton] {\n all: unset;\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 8px;\n cursor: pointer;\n }\n\n [ngpDatePickerDateButton][data-today] {\n color: var(--ngp-text-blue);\n }\n\n [ngpDatePickerDateButton][data-hover] {\n background-color: var(--ngp-background-hover);\n }\n\n [ngpDatePickerDateButton][data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n outline-offset: 2px;\n }\n\n [ngpDatePickerDateButton][data-press] {\n background-color: var(--ngp-background-active);\n }\n\n [ngpDatePickerDateButton][data-outside-month] {\n color: var(--ngp-text-disabled);\n }\n\n [ngpDatePickerDateButton][data-selected] {\n background-color: var(--ngp-background-inverse);\n color: var(--ngp-text-inverse);\n }\n\n [ngpDatePickerDateButton][data-selected][data-outside-month] {\n background-color: var(--ngp-background-disabled);\n color: var(--ngp-text-disabled);\n }\n\n [ngpDatePickerDateButton][data-disabled] {\n cursor: not-allowed;\n color: var(--ngp-text-disabled);\n }\n `,\n host: {\n '(focusout)': 'onTouched?.()',\n },\n})\nexport class DatePicker implements ControlValueAccessor {\n /** Access the date picker host directive */\n private readonly state = injectDatePickerState<Date>();\n\n /**\n * Get the current focused date in string format.\n * @returns The focused date in \"February 2024\" format.\n */\n readonly label = computed(\n () =>\n `${this.state().focusedDate().toLocaleString('default', { month: 'long' })} ${this.state().focusedDate().getFullYear()}`,\n );\n\n /**\n * The onChange callback function for the date picker.\n */\n protected onChange?: ChangeFn<Date | undefined>;\n\n /**\n * The onTouched callback function for the date picker.\n */\n protected onTouched?: TouchedFn;\n\n constructor() {\n // Whenever the user interacts with the date picker, call the onChange function with the new value.\n this.state().dateChange.subscribe(date => this.onChange?.(date));\n }\n\n writeValue(date: Date): void {\n // writing a value from the model must not re-emit through onChange\n this.state().select(date, false, { emit: false });\n }\n\n registerOnChange(fn: ChangeFn<Date | undefined>): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: TouchedFn): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.state().setDisabled(isDisabled);\n }\n}\n",
|
|
490
709
|
"hasVariants": false,
|
|
491
710
|
"hasSizes": true
|
|
492
711
|
}
|
|
@@ -541,10 +760,44 @@
|
|
|
541
760
|
"NgpDialog",
|
|
542
761
|
"injectDialogRef",
|
|
543
762
|
"NgpDialogRef",
|
|
763
|
+
"NgpDialogContext",
|
|
764
|
+
"NgpDialogManager",
|
|
765
|
+
"NgpDialogDescriptionStateToken",
|
|
766
|
+
"ngpDialogDescription",
|
|
767
|
+
"injectDialogDescriptionState",
|
|
768
|
+
"provideDialogDescriptionState",
|
|
769
|
+
"type NgpDialogDescriptionState",
|
|
770
|
+
"type NgpDialogDescriptionProps",
|
|
771
|
+
"NgpDialogOverlayStateToken",
|
|
772
|
+
"ngpDialogOverlay",
|
|
773
|
+
"injectDialogOverlayState",
|
|
774
|
+
"provideDialogOverlayState",
|
|
775
|
+
"type NgpDialogOverlayState",
|
|
776
|
+
"type NgpDialogOverlayProps",
|
|
777
|
+
"NgpDialogPanelStateToken",
|
|
778
|
+
"ngpDialogPanel",
|
|
779
|
+
"injectDialogPanelState",
|
|
780
|
+
"provideDialogPanelState",
|
|
781
|
+
"type NgpDialogPanelState",
|
|
782
|
+
"type NgpDialogPanelProps",
|
|
783
|
+
"NgpDialogTitleStateToken",
|
|
784
|
+
"ngpDialogTitle",
|
|
785
|
+
"injectDialogTitleState",
|
|
786
|
+
"provideDialogTitleState",
|
|
787
|
+
"type NgpDialogTitleState",
|
|
788
|
+
"type NgpDialogTitleProps",
|
|
789
|
+
"NgpDialogTriggerStateToken",
|
|
790
|
+
"ngpDialogTrigger",
|
|
791
|
+
"injectDialogTriggerState",
|
|
792
|
+
"provideDialogTriggerState",
|
|
793
|
+
"type NgpDialogTriggerState",
|
|
794
|
+
"type NgpDialogTriggerProps",
|
|
795
|
+
"NgpDialogStateToken",
|
|
796
|
+
"ngpDialog",
|
|
544
797
|
"injectDialogState",
|
|
545
798
|
"provideDialogState",
|
|
546
|
-
"
|
|
547
|
-
"
|
|
799
|
+
"type NgpDialogState",
|
|
800
|
+
"type NgpDialogProps"
|
|
548
801
|
],
|
|
549
802
|
"hasSecondaryEntryPoint": true,
|
|
550
803
|
"category": "feedback",
|
|
@@ -565,7 +818,7 @@
|
|
|
565
818
|
}
|
|
566
819
|
],
|
|
567
820
|
"reusableComponent": {
|
|
568
|
-
"code": "import { Component, input } from '@angular/core';\nimport {\n NgpDialog,\n NgpDialogDescription,\n NgpDialogOverlay,\n NgpDialogTitle,\n provideDialogState,\n} from 'ng-primitives/dialog';\n\n@Component({\n selector: 'app-dialog',\n hostDirectives: [NgpDialogOverlay],\n imports: [NgpDialog, NgpDialogTitle, NgpDialogDescription],\n providers: [\n // We need to hoist the dialog state to the host component so that it can be used\n // within ng-content\n provideDialogState(),\n ],\n template: `\n <div ngpDialog>\n <h2 ngpDialogTitle>{{ header() }}</h2>\n <p ngpDialogDescription>\n <ng-content />\n </p>\n </div>\n `,\n styles: `\n :host {\n background-color: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n position: fixed;\n inset: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n animation: fadeIn 300ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n :host[data-exit] {\n animation: fadeOut 300ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n [ngpDialog] {\n background-color: var(--ngp-background);\n padding: 24px;\n border-radius: 8px;\n box-shadow: var(--ngp-shadow);\n animation: slideIn 300ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n [ngpDialog][data-exit] {\n animation: slideOut 300ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n [ngpDialogTitle] {\n font-size: 18px;\n line-height: 28px;\n font-weight: 600;\n color: var(--ngp-text-primary);\n margin: 0 0 4px;\n }\n\n [ngpDialogDescription] {\n font-size: 14px;\n line-height: 20px;\n color: var(--ngp-text-secondary);\n margin: 0;\n }\n\n @keyframes fadeIn {\n 0% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n }\n\n @keyframes fadeOut {\n 0% {\n opacity: 1;\n }\n 100% {\n opacity: 0;\n }\n }\n\n @keyframes slideIn {\n 0% {\n transform: translateY(-20px);\n opacity: 0;\n }\n 100% {\n transform: translateY(0);\n opacity: 1;\n }\n }\n @keyframes slideOut {\n 0% {\n transform: translateY(0);\n opacity: 1;\n }\n\n 100% {\n transform: translateY(-20px);\n opacity: 0;\n }\n }\n `,\n})\nexport class Dialog {\n /** The dialog title */\n readonly header = input.required<string>();\n}\n",
|
|
821
|
+
"code": "import { Component, input } from '@angular/core';\nimport {\n NgpDialog,\n NgpDialogDescription,\n NgpDialogOverlay,\n NgpDialogTitle,\n provideDialogState,\n} from 'ng-primitives/dialog';\n\n@Component({\n selector: 'app-dialog',\n hostDirectives: [NgpDialogOverlay],\n imports: [NgpDialog, NgpDialogTitle, NgpDialogDescription],\n providers: [\n // We need to hoist the dialog state to the host component so that it can be used\n // within ng-content\n provideDialogState(),\n ],\n template: `\n <div ngpDialog>\n <h2 ngpDialogTitle>{{ header() }}</h2>\n <p ngpDialogDescription>\n <ng-content />\n </p>\n </div>\n `,\n styles: `\n :host {\n background-color: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n position: fixed;\n inset: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n animation: fadeIn 300ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n :host[data-exit] {\n animation: fadeOut 300ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n [ngpDialog] {\n background-color: var(--ngp-background);\n padding: 24px;\n border-radius: 8px;\n box-shadow: var(--ngp-shadow);\n animation: slideIn 300ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n [ngpDialog][data-exit] {\n animation: slideOut 300ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n [ngpDialogTitle] {\n font-size: 18px;\n line-height: 28px;\n font-weight: 600;\n color: var(--ngp-text-primary);\n margin: 0 0 4px;\n }\n\n [ngpDialogDescription] {\n font-size: 14px;\n line-height: 20px;\n color: var(--ngp-text-secondary);\n margin: 0;\n }\n\n @keyframes fadeIn {\n 0% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n }\n\n @keyframes fadeOut {\n 0% {\n opacity: 1;\n }\n 100% {\n opacity: 0;\n }\n }\n\n @keyframes slideIn {\n 0% {\n transform: translateY(-20px);\n opacity: 0;\n }\n 100% {\n transform: translateY(0);\n opacity: 1;\n }\n }\n @keyframes slideOut {\n 0% {\n transform: translateY(0);\n opacity: 1;\n }\n\n 100% {\n transform: translateY(-20px);\n opacity: 0;\n }\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n :host,\n :host[data-exit],\n [ngpDialog],\n [ngpDialog][data-exit] {\n animation-duration: 0s;\n }\n }\n `,\n})\nexport class Dialog {\n /** The dialog title */\n readonly header = input.required<string>();\n}\n",
|
|
569
822
|
"hasVariants": false,
|
|
570
823
|
"hasSizes": true
|
|
571
824
|
}
|
|
@@ -671,7 +924,7 @@
|
|
|
671
924
|
}
|
|
672
925
|
],
|
|
673
926
|
"reusableComponent": {
|
|
674
|
-
"code": "import { Component } from '@angular/core';\nimport { NgpFormField } from 'ng-primitives/form-field';\n\n@Component({\n selector: 'app-
|
|
927
|
+
"code": "import { Component } from '@angular/core';\nimport { NgpFormField } from 'ng-primitives/form-field';\n\n@Component({\n selector: 'app-field',\n hostDirectives: [NgpFormField],\n template: `\n <ng-content />\n `,\n styles: `\n :host {\n display: flex;\n flex-direction: column;\n gap: 6px;\n width: 90%;\n }\n `,\n})\nexport class Field {}\n",
|
|
675
928
|
"hasVariants": false,
|
|
676
929
|
"hasSizes": false
|
|
677
930
|
}
|
|
@@ -708,12 +961,19 @@
|
|
|
708
961
|
"name": "input-otp",
|
|
709
962
|
"entryPoint": "ng-primitives/input-otp",
|
|
710
963
|
"exports": [
|
|
711
|
-
"NgpInputOtpInput",
|
|
712
|
-
"NgpInputOtpSlot",
|
|
713
964
|
"NgpInputOtp",
|
|
714
|
-
"
|
|
965
|
+
"NgpInputOtpInputMode",
|
|
715
966
|
"injectInputOtpState",
|
|
716
|
-
"
|
|
967
|
+
"ngpInputOtp",
|
|
968
|
+
"NgpInputOtpProps",
|
|
969
|
+
"NgpInputOtpState",
|
|
970
|
+
"provideInputOtpState",
|
|
971
|
+
"NgpInputOtpInput",
|
|
972
|
+
"ngpInputOtpInput",
|
|
973
|
+
"NgpInputOtpInputStateToken",
|
|
974
|
+
"NgpInputOtpSlot",
|
|
975
|
+
"ngpInputOtpSlot",
|
|
976
|
+
"NgpInputOtpSlotStateToken"
|
|
717
977
|
],
|
|
718
978
|
"hasSecondaryEntryPoint": true,
|
|
719
979
|
"category": "form",
|
|
@@ -729,7 +989,7 @@
|
|
|
729
989
|
}
|
|
730
990
|
],
|
|
731
991
|
"reusableComponent": {
|
|
732
|
-
"code": "import { BooleanInput, NumberInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n Component,\n computed,\n forwardRef,\n input,\n model,\n numberAttribute,\n signal,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { NgpInputOtp, NgpInputOtpInput, NgpInputOtpSlot } from 'ng-primitives/input-otp';\nimport { ChangeFn, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'app-input-otp',\n imports: [NgpInputOtp, NgpInputOtpInput, NgpInputOtpSlot],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => InputOtp),\n multi: true,\n },\n ],\n template: `\n <div\n [ngpInputOtpValue]=\"value()\"\n [ngpInputOtpDisabled]=\"disabled() || formDisabled()\"\n [ngpInputOtpPattern]=\"pattern()\"\n [ngpInputOtpPlaceholder]=\"placeholder()\"\n [ngpInputOtpInputMode]=\"inputMode()\"\n (ngpInputOtpValueChange)=\"onValueChange($event)\"\n (ngpInputOtpComplete)=\"onComplete()\"\n ngpInputOtp\n >\n <input [attr.aria-label]=\"ariaLabel()\" ngpInputOtpInput />\n <div class=\"slots\">\n @for (_ of slots(); track $index) {\n <div class=\"slot\" ngpInputOtpSlot></div>\n }\n </div>\n </div>\n `,\n styles: `\n :host {\n display: inline-flex;\n flex-direction: column;\n gap: 12px;\n max-width: 100%;\n }\n\n .slots {\n display: flex;\n gap: 8px;\n }\n\n .slot {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border: 2px solid var(--ngp-border);\n border-radius: 8px;\n background: var(--ngp-background);\n font-size: 18px;\n font-weight: 600;\n color: var(--ngp-text-primary);\n transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1);\n cursor: pointer;\n position: relative;\n }\n\n .slot[data-filled] {\n border-color: var(--ngp-border-strong);\n background: var(--ngp-background-accent);\n }\n\n .slot[data-active] {\n border-color: var(--ngp-focus-ring);\n box-shadow: 0 0 0 1px var(--ngp-focus-ring);\n }\n\n .slot[data-placeholder] {\n color: var(--ngp-text-placeholder);\n }\n\n .slot[data-caret]::after {\n content: '';\n position: absolute;\n width: 2px;\n height: 20px;\n background: var(--ngp-focus-ring);\n animation: blink 1s infinite;\n }\n\n @keyframes blink {\n 0%,\n 50% {\n opacity: 1;\n }\n 51%,\n 100% {\n opacity: 0;\n }\n }\n\n :host([data-disabled]) .slot {\n background: var(--ngp-background-disabled);\n color: var(--ngp-text-disabled);\n border-color: var(--ngp-border-disabled);\n cursor: not-allowed;\n }\n `,\n})\nexport class InputOtp implements ControlValueAccessor {\n /**\n * The number of slots to display.\n */\n readonly length = input<number, NumberInput>(6, {\n transform: numberAttribute,\n });\n\n /**\n * Whether the input is disabled.\n */\n readonly disabled = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /**\n * The pattern for allowed characters.\n */\n readonly pattern = input('[0-9]');\n\n /**\n * The placeholder character for empty slots.\n */\n readonly placeholder = input('');\n\n /**\n * The accessible label for the hidden input.\n */\n readonly ariaLabel = input('');\n\n /**\n * The input mode for the hidden input.\n */\n readonly inputMode = input<'numeric' | 'text' | 'decimal' | 'tel' | 'search' | 'email' | 'url'>(\n 'numeric',\n );\n\n /**\n * Create an array for tracking slots.\n */\n protected readonly slots = computed(() => Array.from({ length: this.length() }, (_, i) => i));\n\n /**\n * The current value.\n */\n readonly value = model<string>('');\n\n private onChange: ChangeFn<string> = () => {};\n private onTouched: TouchedFn = () => {};\n\n protected readonly formDisabled = signal(false);\n\n /**\n * Handle value changes from the input-otp directive.\n */\n onValueChange(value: string): void {\n this.value.set(value);\n this.onChange(value);\n }\n\n /**\n * Handle completion events from the input-otp directive.\n */\n onComplete(): void {\n this.onTouched();\n }\n\n // ControlValueAccessor implementation\n writeValue(value: string): void {\n this.value.set(value);\n }\n\n registerOnChange(fn: ChangeFn<string>): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: TouchedFn): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n}\n",
|
|
992
|
+
"code": "import { BooleanInput, NumberInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n Component,\n computed,\n forwardRef,\n input,\n model,\n numberAttribute,\n signal,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { NgpInputOtp, NgpInputOtpInput, NgpInputOtpSlot } from 'ng-primitives/input-otp';\nimport { ChangeFn, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'app-input-otp',\n imports: [NgpInputOtp, NgpInputOtpInput, NgpInputOtpSlot],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => InputOtp),\n multi: true,\n },\n ],\n template: `\n <div\n [ngpInputOtpValue]=\"value()\"\n [ngpInputOtpDisabled]=\"disabled() || formDisabled()\"\n [ngpInputOtpPattern]=\"pattern()\"\n [ngpInputOtpPlaceholder]=\"placeholder()\"\n [ngpInputOtpInputMode]=\"inputMode()\"\n (ngpInputOtpValueChange)=\"onValueChange($event)\"\n (ngpInputOtpComplete)=\"onComplete()\"\n ngpInputOtp\n >\n <input [attr.aria-label]=\"ariaLabel()\" ngpInputOtpInput />\n <div class=\"slots\">\n @for (_ of slots(); track $index) {\n <div class=\"slot\" ngpInputOtpSlot></div>\n }\n </div>\n </div>\n `,\n styles: `\n :host {\n display: inline-flex;\n flex-direction: column;\n gap: 12px;\n max-width: 100%;\n }\n\n .slots {\n display: flex;\n gap: 8px;\n }\n\n .slot {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border: 2px solid var(--ngp-border);\n border-radius: 8px;\n background: var(--ngp-background);\n font-size: 18px;\n font-weight: 600;\n color: var(--ngp-text-primary);\n transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1);\n cursor: pointer;\n position: relative;\n }\n\n .slot[data-filled] {\n border-color: var(--ngp-border-strong);\n background: var(--ngp-background-accent);\n }\n\n .slot[data-active] {\n border-color: var(--ngp-focus-ring);\n box-shadow: 0 0 0 1px var(--ngp-focus-ring);\n }\n\n .slot[data-placeholder] {\n color: var(--ngp-text-placeholder);\n }\n\n .slot[data-caret]::after {\n content: '';\n position: absolute;\n width: 2px;\n height: 20px;\n background: var(--ngp-focus-ring);\n animation: blink 1s infinite;\n }\n\n @keyframes blink {\n 0%,\n 50% {\n opacity: 1;\n }\n 51%,\n 100% {\n opacity: 0;\n }\n }\n\n :host([data-disabled]) .slot {\n background: var(--ngp-background-disabled);\n color: var(--ngp-text-disabled);\n border-color: var(--ngp-border-disabled);\n cursor: not-allowed;\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n .slot[data-caret]::after {\n animation: none;\n }\n\n .slot {\n transition-duration: 0s;\n }\n }\n `,\n})\nexport class InputOtp implements ControlValueAccessor {\n /**\n * The number of slots to display.\n */\n readonly length = input<number, NumberInput>(6, {\n transform: numberAttribute,\n });\n\n /**\n * Whether the input is disabled.\n */\n readonly disabled = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /**\n * The pattern for allowed characters.\n */\n readonly pattern = input('[0-9]');\n\n /**\n * The placeholder character for empty slots.\n */\n readonly placeholder = input('');\n\n /**\n * The accessible label for the hidden input.\n */\n readonly ariaLabel = input('');\n\n /**\n * The input mode for the hidden input.\n */\n readonly inputMode = input<'numeric' | 'text' | 'decimal' | 'tel' | 'search' | 'email' | 'url'>(\n 'numeric',\n );\n\n /**\n * Create an array for tracking slots.\n */\n protected readonly slots = computed(() => Array.from({ length: this.length() }, (_, i) => i));\n\n /**\n * The current value.\n */\n readonly value = model<string>('');\n\n private onChange: ChangeFn<string> = () => {};\n private onTouched: TouchedFn = () => {};\n\n protected readonly formDisabled = signal(false);\n\n /**\n * Handle value changes from the input-otp directive.\n */\n onValueChange(value: string): void {\n this.value.set(value);\n this.onChange(value);\n }\n\n /**\n * Handle completion events from the input-otp directive.\n */\n onComplete(): void {\n this.onTouched();\n }\n\n // ControlValueAccessor implementation\n writeValue(value: string): void {\n this.value.set(value);\n }\n\n registerOnChange(fn: ChangeFn<string>): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: TouchedFn): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n}\n",
|
|
733
993
|
"hasVariants": false,
|
|
734
994
|
"hasSizes": true
|
|
735
995
|
}
|
|
@@ -767,14 +1027,43 @@
|
|
|
767
1027
|
"NgpListboxSection",
|
|
768
1028
|
"NgpListboxTrigger",
|
|
769
1029
|
"NgpListbox",
|
|
1030
|
+
"NgpListboxTriggerStateToken",
|
|
1031
|
+
"ngpListboxTrigger",
|
|
1032
|
+
"injectListboxTriggerState",
|
|
1033
|
+
"provideListboxTriggerState",
|
|
1034
|
+
"type NgpListboxTriggerState",
|
|
1035
|
+
"type NgpListboxTriggerProps",
|
|
1036
|
+
"NgpListboxSectionStateToken",
|
|
1037
|
+
"ngpListboxSection",
|
|
1038
|
+
"injectListboxSectionState",
|
|
1039
|
+
"provideListboxSectionState",
|
|
1040
|
+
"type NgpListboxSectionState",
|
|
1041
|
+
"type NgpListboxSectionProps",
|
|
1042
|
+
"NgpListboxOptionStateToken",
|
|
1043
|
+
"ngpListboxOption",
|
|
1044
|
+
"injectListboxOptionState",
|
|
1045
|
+
"provideListboxOptionState",
|
|
1046
|
+
"type NgpListboxOptionState",
|
|
1047
|
+
"type NgpListboxOptionProps",
|
|
1048
|
+
"NgpListboxHeaderStateToken",
|
|
1049
|
+
"ngpListboxHeader",
|
|
1050
|
+
"injectListboxHeaderState",
|
|
1051
|
+
"provideListboxHeaderState",
|
|
1052
|
+
"type NgpListboxHeaderState",
|
|
1053
|
+
"type NgpListboxHeaderProps",
|
|
1054
|
+
"NgpListboxStateToken",
|
|
1055
|
+
"ngpListbox",
|
|
770
1056
|
"injectListboxState",
|
|
771
|
-
"provideListboxState"
|
|
1057
|
+
"provideListboxState",
|
|
1058
|
+
"type NgpListboxState",
|
|
1059
|
+
"type NgpListboxProps"
|
|
772
1060
|
],
|
|
773
1061
|
"hasSecondaryEntryPoint": true,
|
|
774
1062
|
"category": "form",
|
|
775
1063
|
"description": "A listbox presents a set of choices and lets users select one or multiple options.",
|
|
776
1064
|
"accessibility": [
|
|
777
|
-
"ARIA Listbox"
|
|
1065
|
+
"ARIA Listbox",
|
|
1066
|
+
"keyboard navigation"
|
|
778
1067
|
],
|
|
779
1068
|
"examples": [
|
|
780
1069
|
{
|
|
@@ -796,6 +1085,7 @@
|
|
|
796
1085
|
"injectOverlayContext as injectMenuContext",
|
|
797
1086
|
"NgpMenuConfig",
|
|
798
1087
|
"provideMenuConfig",
|
|
1088
|
+
"type NgpMenuTriggerType",
|
|
799
1089
|
"NgpMenuItem",
|
|
800
1090
|
"ngpMenuItem",
|
|
801
1091
|
"injectMenuItemState",
|
|
@@ -873,17 +1163,27 @@
|
|
|
873
1163
|
},
|
|
874
1164
|
{
|
|
875
1165
|
"name": "example-4",
|
|
1166
|
+
"code": "<!-- Keep the menu inside a container element -->\n<div #boundary>\n <button [ngpMenuTrigger]=\"menu\" [ngpMenuTriggerFlip]=\"{boundary: boundary}\">\n Menu constrained to a container\n </button>\n</div>\n\n<!-- Keep the menu inside whatever scrolls the trigger -->\n<div style=\"overflow: auto\">\n <button [ngpMenuTrigger]=\"menu\" [ngpMenuTriggerFlip]=\"{altBoundary: true}\">\n Menu constrained to the trigger's scroll container\n </button>\n</div>\n\n<!-- Measure against the whole document rather than the viewport -->\n<button [ngpMenuTrigger]=\"menu\" [ngpMenuTriggerFlip]=\"{rootBoundary: 'document'}\">\n Menu that does not flip while off-screen\n</button>",
|
|
1167
|
+
"description": "Overflow Boundary"
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
"name": "example-5",
|
|
1171
|
+
"code": "<button [ngpMenuTrigger]=\"menu\" [ngpMenuTriggerFlip]=\"{crossAxis: false}\">\n Keep the alignment\n</button>\n<button [ngpMenuTrigger]=\"menu\" [ngpMenuTriggerShift]=\"{crossAxis: true}\">\n Shift on both axes\n</button>",
|
|
1172
|
+
"description": "Overflow Boundary"
|
|
1173
|
+
},
|
|
1174
|
+
{
|
|
1175
|
+
"name": "example-6",
|
|
876
1176
|
"code": "<!-- Enable Enter key to toggle menu -->\n<button [ngpMenuTrigger]=\"menu\" [ngpMenuTriggerOpenTriggers]=\"['click', 'enter']\">Menu</button>\n\n<!-- Enable arrow keys (placement-aware) -->\n<button\n [ngpMenuTrigger]=\"menu\"\n [ngpMenuTriggerOpenTriggers]=\"['arrowkey']\"\n ngpMenuTriggerPlacement=\"right-start\"\n>\n Sidebar Menu\n</button>\n\n<!-- Combine triggers for best UX -->\n<button\n [ngpMenuTrigger]=\"menu\"\n [ngpMenuTriggerOpenTriggers]=\"['hover', 'arrowkey', 'enter']\"\n ngpMenuTriggerPlacement=\"right-start\"\n>\n Navigation Item\n</button>",
|
|
877
1177
|
"description": "Keyboard Triggers"
|
|
878
1178
|
},
|
|
879
1179
|
{
|
|
880
|
-
"name": "example-
|
|
1180
|
+
"name": "example-7",
|
|
881
1181
|
"code": "import { provideMenuConfig } from 'ng-primitives/menu';\n\nbootstrapApplication(AppComponent, {\n providers: [\n provideMenuConfig({\n offset: 4,\n placement: 'top',\n flip: true,\n container: document.body,\n scrollBehavior: 'reposition',\n cooldown: 0,\n }),\n ],\n});",
|
|
882
1182
|
"description": "Global Configuration"
|
|
883
1183
|
}
|
|
884
1184
|
],
|
|
885
1185
|
"reusableComponent": {
|
|
886
|
-
"code": "import { Component } from '@angular/core';\nimport { NgpMenuItem } from 'ng-primitives/menu';\n\n@Component({\n selector: 'button[app-menu-item]',\n hostDirectives: [NgpMenuItem],\n template: `\n <ng-content />\n `,\n styles: `\n :host {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 8px 6px 14px;\n border: none;\n background: none;\n cursor: pointer;\n transition: background-color 0.2s;\n border-radius: 4px;\n min-width: 120px;\n text-align: start;\n outline: none;\n font-size: 14px;\n font-weight: 400;\n }\n\n :host[data-hover] {\n background-color: var(--ngp-background-active);\n }\n\n :host[data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n z-index: 1;\n }\n `,\n})\nexport class MenuItem {}\n",
|
|
1186
|
+
"code": "import { Component } from '@angular/core';\nimport { NgpMenuItem } from 'ng-primitives/menu';\n\n@Component({\n selector: 'button[app-menu-item]',\n hostDirectives: [NgpMenuItem],\n template: `\n <ng-content />\n `,\n styles: `\n :host {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 8px 6px 14px;\n border: none;\n background: none;\n cursor: pointer;\n transition: background-color 0.2s;\n border-radius: 4px;\n min-width: 120px;\n text-align: start;\n outline: none;\n font-size: 14px;\n font-weight: 400;\n }\n\n :host[data-hover] {\n background-color: var(--ngp-background-active);\n }\n\n :host[data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n z-index: 1;\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n :host {\n transition-duration: 0s;\n }\n }\n `,\n})\nexport class MenuItem {}\n",
|
|
887
1187
|
"hasVariants": false,
|
|
888
1188
|
"hasSizes": true
|
|
889
1189
|
}
|
|
@@ -893,12 +1193,22 @@
|
|
|
893
1193
|
"entryPoint": "ng-primitives/meter",
|
|
894
1194
|
"exports": [
|
|
895
1195
|
"NgpMeter",
|
|
896
|
-
"
|
|
1196
|
+
"NgpMeterValueTextFn",
|
|
897
1197
|
"injectMeterState",
|
|
898
|
-
"
|
|
1198
|
+
"ngpMeter",
|
|
1199
|
+
"NgpMeterProps",
|
|
1200
|
+
"NgpMeterState",
|
|
1201
|
+
"provideMeterState",
|
|
899
1202
|
"NgpMeterIndicator",
|
|
1203
|
+
"ngpMeterIndicator",
|
|
1204
|
+
"NgpMeterIndicatorStateToken",
|
|
1205
|
+
"NgpMeterLabel",
|
|
1206
|
+
"ngpMeterLabel",
|
|
1207
|
+
"NgpMeterLabelStateToken",
|
|
1208
|
+
"NgpMeterTrack",
|
|
900
1209
|
"NgpMeterValue",
|
|
901
|
-
"
|
|
1210
|
+
"ngpMeterValue",
|
|
1211
|
+
"NgpMeterValueStateToken"
|
|
902
1212
|
],
|
|
903
1213
|
"hasSecondaryEntryPoint": true,
|
|
904
1214
|
"category": "feedback",
|
|
@@ -917,7 +1227,7 @@
|
|
|
917
1227
|
}
|
|
918
1228
|
],
|
|
919
1229
|
"reusableComponent": {
|
|
920
|
-
"code": "import { NumberInput } from '@angular/cdk/coercion';\nimport { Component, input, numberAttribute } from '@angular/core';\nimport {\n NgpMeter,\n NgpMeterIndicator,\n NgpMeterLabel,\n NgpMeterTrack,\n NgpMeterValue,\n} from 'ng-primitives/meter';\n\n@Component({\n selector: 'app-meter',\n hostDirectives: [\n { directive: NgpMeter, inputs: ['ngpMeterValue:value', 'ngpMeterMin:min', 'ngpMeterMax:max'] },\n ],\n imports: [NgpMeterIndicator, NgpMeterLabel, NgpMeterValue, NgpMeterTrack],\n template: `\n <span ngpMeterLabel>{{ label() }}</span>\n <span ngpMeterValue>{{ value() }}%</span>\n\n <div ngpMeterTrack>\n <div ngpMeterIndicator></div>\n </div>\n `,\n styles: `\n :host {\n display: grid;\n grid-template-columns: 1fr 1fr;\n grid-row-gap: 0.5rem;\n width: 200px;\n box-sizing: border-box;\n padding: 0.5rem;\n }\n\n [ngpMeterLabel] {\n color: var(--ngp-text-emphasis);\n font-size: 14px;\n font-weight: 600;\n }\n\n [ngpMeterValue] {\n color: var(--ngp-text-secondary);\n font-size: 14px;\n font-weight: 500;\n text-align: right;\n grid-column-start: 2;\n text-align: end;\n }\n\n [ngpMeterTrack] {\n grid-column: 1 / 3;\n overflow: hidden;\n background-color: var(--ngp-background);\n box-shadow: var(--ngp-shadow-border);\n border-radius: 4px;\n height: 8px;\n }\n\n [ngpMeterIndicator] {\n background-color: var(--ngp-background-success);\n height: 100%;\n transition: width 0.2s ease-in-out;\n inset-inline-start: 0px;\n border-radius: 4px;\n }\n `,\n})\nexport class Meter {\n /** The value of the meter. */\n readonly value = input<number, NumberInput>(0, {\n transform: numberAttribute,\n });\n\n /** The label of the meter. */\n readonly label = input.required<string>();\n}\n",
|
|
1230
|
+
"code": "import { NumberInput } from '@angular/cdk/coercion';\nimport { Component, input, numberAttribute } from '@angular/core';\nimport {\n NgpMeter,\n NgpMeterIndicator,\n NgpMeterLabel,\n NgpMeterTrack,\n NgpMeterValue,\n} from 'ng-primitives/meter';\n\n@Component({\n selector: 'app-meter',\n hostDirectives: [\n { directive: NgpMeter, inputs: ['ngpMeterValue:value', 'ngpMeterMin:min', 'ngpMeterMax:max'] },\n ],\n imports: [NgpMeterIndicator, NgpMeterLabel, NgpMeterValue, NgpMeterTrack],\n template: `\n <span ngpMeterLabel>{{ label() }}</span>\n <span ngpMeterValue>{{ value() }}%</span>\n\n <div ngpMeterTrack>\n <div ngpMeterIndicator></div>\n </div>\n `,\n styles: `\n :host {\n display: grid;\n grid-template-columns: 1fr 1fr;\n grid-row-gap: 0.5rem;\n width: 200px;\n box-sizing: border-box;\n padding: 0.5rem;\n }\n\n [ngpMeterLabel] {\n color: var(--ngp-text-emphasis);\n font-size: 14px;\n font-weight: 600;\n }\n\n [ngpMeterValue] {\n color: var(--ngp-text-secondary);\n font-size: 14px;\n font-weight: 500;\n text-align: right;\n grid-column-start: 2;\n text-align: end;\n }\n\n [ngpMeterTrack] {\n grid-column: 1 / 3;\n overflow: hidden;\n background-color: var(--ngp-background);\n box-shadow: var(--ngp-shadow-border);\n border-radius: 4px;\n height: 8px;\n }\n\n [ngpMeterIndicator] {\n background-color: var(--ngp-background-success);\n height: 100%;\n transition: width 0.2s ease-in-out;\n inset-inline-start: 0px;\n border-radius: 4px;\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n [ngpMeterIndicator] {\n transition-duration: 0s;\n }\n }\n `,\n})\nexport class Meter {\n /** The value of the meter. */\n readonly value = input<number, NumberInput>(0, {\n transform: numberAttribute,\n });\n\n /** The label of the meter. */\n readonly label = input.required<string>();\n}\n",
|
|
921
1231
|
"hasVariants": false,
|
|
922
1232
|
"hasSizes": true
|
|
923
1233
|
}
|
|
@@ -1058,7 +1368,7 @@
|
|
|
1058
1368
|
}
|
|
1059
1369
|
],
|
|
1060
1370
|
"reusableComponent": {
|
|
1061
|
-
"code": "import { Component } from '@angular/core';\nimport {\n NgpNumberField,\n NgpNumberFieldDecrement,\n NgpNumberFieldIncrement,\n NgpNumberFieldInput,\n} from 'ng-primitives/number-field';\n\n@Component({\n selector: 'app-number-field',\n hostDirectives: [\n {\n directive: NgpNumberField,\n inputs: [\n 'ngpNumberFieldValue:value',\n 'ngpNumberFieldMin:min',\n 'ngpNumberFieldMax:max',\n 'ngpNumberFieldStep:step',\n 'ngpNumberFieldLargeStep:largeStep',\n 'ngpNumberFieldDisabled:disabled',\n 'ngpNumberFieldReadonly:readonly',\n ],\n outputs: ['ngpNumberFieldValueChange:valueChange'],\n },\n ],\n imports: [NgpNumberFieldInput, NgpNumberFieldIncrement, NgpNumberFieldDecrement],\n template: `\n <button ngpNumberFieldDecrement aria-label=\"Decrement\">−</button>\n <input ngpNumberFieldInput />\n <button ngpNumberFieldIncrement aria-label=\"Increment\">+</button>\n `,\n styles: `\n :host {\n display: inline-flex;\n align-items: center;\n border-radius: 8px;\n box-shadow: var(--ngp-input-shadow);\n background-color: var(--ngp-background);\n overflow: hidden;\n }\n\n :host:focus-within {\n outline: 2px solid var(--ngp-focus-ring);\n outline-offset: 2px;\n }\n\n [ngpNumberFieldInput] {\n width: 64px;\n height: 36px;\n border: none;\n outline: none;\n text-align: center;\n font-size: 0.875rem;\n color: var(--ngp-text-primary);\n background: transparent;\n box-sizing: border-box;\n padding: 0;\n }\n\n [ngpNumberFieldInput]::placeholder {\n color: var(--ngp-text-placeholder);\n }\n\n [ngpNumberFieldIncrement],\n [ngpNumberFieldDecrement] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border: none;\n background: transparent;\n color: var(--ngp-text-secondary);\n cursor: pointer;\n font-size: 1rem;\n user-select: none;\n -webkit-touch-callout: none;\n transition:\n color 150ms ease,\n background-color 150ms ease;\n }\n\n [ngpNumberFieldIncrement][data-hover],\n [ngpNumberFieldDecrement][data-hover] {\n background-color: var(--ngp-background-hover);\n color: var(--ngp-text-primary);\n }\n\n [ngpNumberFieldIncrement][data-press],\n [ngpNumberFieldDecrement][data-press] {\n background-color: var(--ngp-background-active);\n }\n\n [ngpNumberFieldIncrement][data-disabled],\n [ngpNumberFieldDecrement][data-disabled] {\n opacity: 0.4;\n cursor: not-allowed;\n }\n\n [ngpNumberFieldIncrement][data-disabled][data-hover],\n [ngpNumberFieldDecrement][data-disabled][data-hover] {\n background: transparent;\n color: var(--ngp-text-secondary);\n }\n `,\n})\nexport class NumberField {}\n",
|
|
1371
|
+
"code": "import { Component } from '@angular/core';\nimport {\n NgpNumberField,\n NgpNumberFieldDecrement,\n NgpNumberFieldIncrement,\n NgpNumberFieldInput,\n} from 'ng-primitives/number-field';\n\n@Component({\n selector: 'app-number-field',\n hostDirectives: [\n {\n directive: NgpNumberField,\n inputs: [\n 'ngpNumberFieldValue:value',\n 'ngpNumberFieldMin:min',\n 'ngpNumberFieldMax:max',\n 'ngpNumberFieldStep:step',\n 'ngpNumberFieldLargeStep:largeStep',\n 'ngpNumberFieldDisabled:disabled',\n 'ngpNumberFieldReadonly:readonly',\n ],\n outputs: ['ngpNumberFieldValueChange:valueChange'],\n },\n ],\n imports: [NgpNumberFieldInput, NgpNumberFieldIncrement, NgpNumberFieldDecrement],\n template: `\n <button ngpNumberFieldDecrement aria-label=\"Decrement\">−</button>\n <input ngpNumberFieldInput />\n <button ngpNumberFieldIncrement aria-label=\"Increment\">+</button>\n `,\n styles: `\n :host {\n display: inline-flex;\n align-items: center;\n border-radius: 8px;\n box-shadow: var(--ngp-input-shadow);\n background-color: var(--ngp-background);\n overflow: hidden;\n }\n\n :host:focus-within {\n outline: 2px solid var(--ngp-focus-ring);\n outline-offset: 2px;\n }\n\n [ngpNumberFieldInput] {\n width: 64px;\n height: 36px;\n border: none;\n outline: none;\n text-align: center;\n font-size: 0.875rem;\n color: var(--ngp-text-primary);\n background: transparent;\n box-sizing: border-box;\n padding: 0;\n }\n\n [ngpNumberFieldInput]::placeholder {\n color: var(--ngp-text-placeholder);\n }\n\n [ngpNumberFieldIncrement],\n [ngpNumberFieldDecrement] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border: none;\n background: transparent;\n color: var(--ngp-text-secondary);\n cursor: pointer;\n font-size: 1rem;\n user-select: none;\n -webkit-touch-callout: none;\n transition:\n color 150ms ease,\n background-color 150ms ease;\n }\n\n [ngpNumberFieldIncrement][data-hover],\n [ngpNumberFieldDecrement][data-hover] {\n background-color: var(--ngp-background-hover);\n color: var(--ngp-text-primary);\n }\n\n [ngpNumberFieldIncrement][data-press],\n [ngpNumberFieldDecrement][data-press] {\n background-color: var(--ngp-background-active);\n }\n\n [ngpNumberFieldIncrement][data-disabled],\n [ngpNumberFieldDecrement][data-disabled] {\n opacity: 0.4;\n cursor: not-allowed;\n }\n\n [ngpNumberFieldIncrement][data-disabled][data-hover],\n [ngpNumberFieldDecrement][data-disabled][data-hover] {\n background: transparent;\n color: var(--ngp-text-secondary);\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n [ngpNumberFieldIncrement],\n [ngpNumberFieldDecrement] {\n transition-duration: 0s;\n }\n }\n `,\n})\nexport class NumberField {}\n",
|
|
1062
1372
|
"hasVariants": false,
|
|
1063
1373
|
"hasSizes": true
|
|
1064
1374
|
}
|
|
@@ -1129,7 +1439,7 @@
|
|
|
1129
1439
|
}
|
|
1130
1440
|
],
|
|
1131
1441
|
"reusableComponent": {
|
|
1132
|
-
"code": "import { Component, computed } from '@angular/core';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport {\n heroChevronDoubleLeft,\n heroChevronDoubleRight,\n heroChevronLeft,\n heroChevronRight,\n} from '@ng-icons/heroicons/outline';\nimport {\n injectPaginationState,\n NgpPagination,\n NgpPaginationButton,\n NgpPaginationFirst,\n NgpPaginationLast,\n NgpPaginationNext,\n NgpPaginationPrevious,\n} from 'ng-primitives/pagination';\nimport { ChangeFn, provideValueAccessor, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'app-pagination',\n hostDirectives: [\n {\n directive: NgpPagination,\n inputs: [\n 'ngpPaginationPage:page',\n 'ngpPaginationPageCount:pageCount',\n 'ngpPaginationDisabled:disabled',\n ],\n outputs: ['ngpPaginationPageChange:pageChange'],\n },\n ],\n imports: [\n NgpPaginationButton,\n NgpPaginationFirst,\n NgpPaginationLast,\n NgpPaginationNext,\n NgpPaginationPrevious,\n NgIcon,\n ],\n providers: [\n provideValueAccessor(
|
|
1442
|
+
"code": "import { Component, computed } from '@angular/core';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport {\n heroChevronDoubleLeft,\n heroChevronDoubleRight,\n heroChevronLeft,\n heroChevronRight,\n} from '@ng-icons/heroicons/outline';\nimport {\n injectPaginationState,\n NgpPagination,\n NgpPaginationButton,\n NgpPaginationFirst,\n NgpPaginationLast,\n NgpPaginationNext,\n NgpPaginationPrevious,\n} from 'ng-primitives/pagination';\nimport { ChangeFn, provideValueAccessor, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'app-pagination',\n hostDirectives: [\n {\n directive: NgpPagination,\n inputs: [\n 'ngpPaginationPage:page',\n 'ngpPaginationPageCount:pageCount',\n 'ngpPaginationDisabled:disabled',\n ],\n outputs: ['ngpPaginationPageChange:pageChange'],\n },\n ],\n imports: [\n NgpPaginationButton,\n NgpPaginationFirst,\n NgpPaginationLast,\n NgpPaginationNext,\n NgpPaginationPrevious,\n NgIcon,\n ],\n providers: [\n provideValueAccessor(Pagination),\n provideIcons({\n heroChevronDoubleLeft,\n heroChevronDoubleRight,\n heroChevronLeft,\n heroChevronRight,\n }),\n ],\n template: `\n <ul>\n <li>\n <button ngpPaginationFirst aria-label=\"First Page\">\n <ng-icon name=\"heroChevronDoubleLeft\" />\n </button>\n </li>\n\n <li>\n <button ngpPaginationPrevious aria-label=\"Previous Page\">\n <ng-icon name=\"heroChevronLeft\" />\n </button>\n </li>\n\n @for (page of pages(); track page) {\n <li>\n <button\n [ngpPaginationButtonPage]=\"page\"\n [attr.aria-label]=\"'Page ' + page\"\n ngpPaginationButton\n >\n {{ page }}\n </button>\n </li>\n }\n\n <li>\n <button ngpPaginationNext aria-label=\"Next Page\">\n <ng-icon name=\"heroChevronRight\" />\n </button>\n </li>\n\n <li>\n <button ngpPaginationLast aria-label=\"Last Page\">\n <ng-icon name=\"heroChevronDoubleRight\" />\n </button>\n </li>\n </ul>\n `,\n styles: `\n ul {\n display: flex;\n align-items: center;\n column-gap: 0.5rem;\n list-style: none;\n }\n\n [ngpPaginationFirst],\n [ngpPaginationPrevious],\n [ngpPaginationButton],\n [ngpPaginationNext],\n [ngpPaginationLast] {\n all: unset;\n display: flex;\n justify-content: center;\n align-items: center;\n width: 2rem;\n height: 2rem;\n border-radius: 0.5rem;\n color: var(--ngp-text-primary);\n outline: none;\n font-size: 14px;\n font-weight: 500;\n background-color: var(--ngp-background);\n box-shadow: var(--ngp-button-shadow);\n cursor: pointer;\n transition: background-color 0.2s;\n\n &[data-hover]:not([data-disabled]):not([data-selected]) {\n background-color: var(--ngp-background-hover);\n }\n\n &[data-focus-visible]:not([data-disabled]) {\n outline: 2px solid var(--ngp-focus-ring);\n }\n\n &[data-press]:not([data-disabled]):not([data-selected]) {\n background-color: var(--ngp-background-active);\n }\n\n &[data-disabled] {\n color: rgb(210 210 210);\n background-color: var(--ngp-background-disabled);\n cursor: not-allowed;\n box-shadow: none;\n }\n\n &[data-selected] {\n background-color: var(--ngp-background-inverse);\n color: var(--ngp-text-inverse);\n }\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n [ngpPaginationFirst],\n [ngpPaginationPrevious],\n [ngpPaginationButton],\n [ngpPaginationNext],\n [ngpPaginationLast] {\n transition-duration: 0s;\n }\n }\n `,\n host: {\n '(focusout)': 'onTouched?.()',\n },\n})\nexport class Pagination implements ControlValueAccessor {\n /** Access the pagination state */\n protected readonly state = injectPaginationState();\n\n /** Get the pages as an array we can iterate over */\n protected readonly pages = computed(() =>\n Array.from({ length: this.state().pageCount() }).map((_, i) => i + 1),\n );\n\n /** The onChange callback */\n private onChange?: ChangeFn<number>;\n\n /** The onTouched callback */\n protected onTouched?: TouchedFn;\n\n constructor() {\n this.state().pageChange.subscribe(value => this.onChange?.(value));\n }\n\n /** Write a new value to the control */\n writeValue(value: number): void {\n // writing a value from the model must not re-emit through onChange\n this.state().setPage(value, { emit: false });\n }\n\n /** Register a callback to be called when the value changes */\n registerOnChange(fn: ChangeFn<number>): void {\n this.onChange = fn;\n }\n\n /** Register a callback to be called when the control is touched */\n registerOnTouched(fn: TouchedFn): void {\n this.onTouched = fn;\n }\n\n /** Reflect the form control's disabled state onto the pagination. */\n setDisabledState(isDisabled: boolean): void {\n this.state().setDisabled(isDisabled);\n }\n}\n",
|
|
1133
1443
|
"hasVariants": false,
|
|
1134
1444
|
"hasSizes": true
|
|
1135
1445
|
}
|
|
@@ -1181,7 +1491,7 @@
|
|
|
1181
1491
|
}
|
|
1182
1492
|
],
|
|
1183
1493
|
"reusableComponent": {
|
|
1184
|
-
"code": "import { Component, input, signal } from '@angular/core';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport { lucideEye, lucideEyeOff } from '@ng-icons/lucide';\nimport { NgpButton } from 'ng-primitives/button';\nimport {\n injectPasswordState,\n NgpPassword,\n NgpPasswordInput,\n NgpPasswordToggle,\n} from 'ng-primitives/password';\nimport { ChangeFn, provideValueAccessor, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'app-password',\n hostDirectives: [NgpPassword],\n imports: [NgIcon, NgpPasswordInput, NgpPasswordToggle, NgpButton],\n providers: [provideValueAccessor(Password), provideIcons({ lucideEye, lucideEyeOff })],\n template: `\n <input\n [value]=\"value()\"\n [placeholder]=\"placeholder()\"\n (input)=\"onValueChange($event)\"\n ngpPasswordInput\n type=\"password\"\n autocomplete=\"current-password\"\n />\n <button ngpButton ngpPasswordToggle>\n <ng-icon [name]=\"state().visible() ? 'lucideEyeOff' : 'lucideEye'\" />\n </button>\n `,\n styles: `\n :host {\n display: block;\n position: relative;\n }\n\n [ngpPasswordInput] {\n height: 2.125rem;\n width: 100%;\n border-radius: 0.5rem;\n padding: 0 40px 0 12px;\n border: none;\n background: var(--ngp-background);\n color: var(--ngp-text-primary);\n font-size: 0.875rem;\n letter-spacing: -0.006em;\n box-shadow: var(--ngp-input-shadow);\n outline: none;\n }\n\n [ngpPasswordInput][data-focus] {\n outline: 2px solid var(--ngp-focus-ring);\n outline-offset: 2px;\n }\n\n [ngpPasswordInput]::placeholder {\n color: var(--ngp-text-placeholder);\n }\n\n [ngpPasswordToggle] {\n position: absolute;\n top: 0;\n right: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n height: 2.125rem;\n width: 2.125rem;\n border: none;\n border-radius: 0.5rem;\n background-color: transparent;\n color: var(--ngp-text-tertiary);\n font-size: 1.125rem;\n cursor: pointer;\n outline: none;\n transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n [ngpPasswordToggle][data-hover] {\n color: var(--ngp-text-secondary);\n }\n\n [ngpPasswordToggle][data-visible] {\n color: var(--ngp-primary);\n }\n\n [ngpPasswordToggle][data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n outline-offset: -2px;\n }\n `,\n host: {\n '(focusout)': 'onFocusOut($event)',\n },\n})\nexport class Password implements ControlValueAccessor {\n /** Access the password state to read the visibility. */\n protected readonly state = injectPasswordState();\n\n /** The placeholder text */\n readonly placeholder = input<string>('');\n\n /** The current value */\n protected value = signal<string>('');\n\n /** The function to call when the value changes */\n private onChange?: ChangeFn<string>;\n\n /** The function to call when the control is touched */\n protected onTouched?: TouchedFn;\n\n writeValue(value: string): void {\n this.value.set(value);\n }\n\n registerOnChange(fn: ChangeFn<string>): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: TouchedFn): void {\n this.onTouched = fn;\n }\n\n protected onValueChange(event: Event): void {\n const input = event.target as HTMLInputElement;\n this.value.set(input.value);\n this.onChange?.(input.value);\n }\n\n protected onFocusOut(event: FocusEvent): void {\n // only mark as touched when focus leaves the component, not when moving\n // between the input and the toggle button\n const host = event.currentTarget as HTMLElement;\n if (!host.contains(event.relatedTarget as Node | null)) {\n this.onTouched?.();\n }\n }\n}\n",
|
|
1494
|
+
"code": "import { Component, input, signal } from '@angular/core';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport { lucideEye, lucideEyeOff } from '@ng-icons/lucide';\nimport { NgpButton } from 'ng-primitives/button';\nimport {\n injectPasswordState,\n NgpPassword,\n NgpPasswordInput,\n NgpPasswordToggle,\n} from 'ng-primitives/password';\nimport { ChangeFn, provideValueAccessor, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'app-password',\n hostDirectives: [NgpPassword],\n imports: [NgIcon, NgpPasswordInput, NgpPasswordToggle, NgpButton],\n providers: [provideValueAccessor(Password), provideIcons({ lucideEye, lucideEyeOff })],\n template: `\n <input\n [value]=\"value()\"\n [placeholder]=\"placeholder()\"\n (input)=\"onValueChange($event)\"\n ngpPasswordInput\n type=\"password\"\n autocomplete=\"current-password\"\n />\n <button ngpButton ngpPasswordToggle>\n <ng-icon [name]=\"state().visible() ? 'lucideEyeOff' : 'lucideEye'\" />\n </button>\n `,\n styles: `\n :host {\n display: block;\n position: relative;\n }\n\n [ngpPasswordInput] {\n height: 2.125rem;\n width: 100%;\n border-radius: 0.5rem;\n padding: 0 40px 0 12px;\n border: none;\n background: var(--ngp-background);\n color: var(--ngp-text-primary);\n font-size: 0.875rem;\n letter-spacing: -0.006em;\n box-shadow: var(--ngp-input-shadow);\n outline: none;\n }\n\n [ngpPasswordInput][data-focus] {\n outline: 2px solid var(--ngp-focus-ring);\n outline-offset: 2px;\n }\n\n [ngpPasswordInput]::placeholder {\n color: var(--ngp-text-placeholder);\n }\n\n [ngpPasswordToggle] {\n position: absolute;\n top: 0;\n right: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n height: 2.125rem;\n width: 2.125rem;\n border: none;\n border-radius: 0.5rem;\n background-color: transparent;\n color: var(--ngp-text-tertiary);\n font-size: 1.125rem;\n cursor: pointer;\n outline: none;\n transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n [ngpPasswordToggle][data-hover] {\n color: var(--ngp-text-secondary);\n }\n\n [ngpPasswordToggle][data-visible] {\n color: var(--ngp-primary);\n }\n\n [ngpPasswordToggle][data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n outline-offset: -2px;\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n [ngpPasswordToggle] {\n transition-duration: 0s;\n }\n }\n `,\n host: {\n '(focusout)': 'onFocusOut($event)',\n },\n})\nexport class Password implements ControlValueAccessor {\n /** Access the password state to read the visibility. */\n protected readonly state = injectPasswordState();\n\n /** The placeholder text */\n readonly placeholder = input<string>('');\n\n /** The current value */\n protected value = signal<string>('');\n\n /** The function to call when the value changes */\n private onChange?: ChangeFn<string>;\n\n /** The function to call when the control is touched */\n protected onTouched?: TouchedFn;\n\n writeValue(value: string): void {\n this.value.set(value);\n }\n\n registerOnChange(fn: ChangeFn<string>): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: TouchedFn): void {\n this.onTouched = fn;\n }\n\n protected onValueChange(event: Event): void {\n const input = event.target as HTMLInputElement;\n this.value.set(input.value);\n this.onChange?.(input.value);\n }\n\n protected onFocusOut(event: FocusEvent): void {\n // only mark as touched when focus leaves the component, not when moving\n // between the input and the toggle button\n const host = event.currentTarget as HTMLElement;\n if (!host.contains(event.relatedTarget as Node | null)) {\n this.onTouched?.();\n }\n }\n}\n",
|
|
1185
1495
|
"hasVariants": false,
|
|
1186
1496
|
"hasSizes": true
|
|
1187
1497
|
}
|
|
@@ -1238,6 +1548,16 @@
|
|
|
1238
1548
|
},
|
|
1239
1549
|
{
|
|
1240
1550
|
"name": "example-3",
|
|
1551
|
+
"code": "<!-- Keep the popover inside a container element -->\n<div #boundary>\n <button [ngpPopoverTrigger]=\"popover\" [ngpPopoverTriggerFlip]=\"{boundary: boundary}\">\n Popover constrained to a container\n </button>\n</div>\n\n<!-- Keep the popover inside whatever scrolls the trigger -->\n<div style=\"overflow: auto\">\n <button [ngpPopoverTrigger]=\"popover\" [ngpPopoverTriggerFlip]=\"{altBoundary: true}\">\n Popover constrained to the trigger's scroll container\n </button>\n</div>\n\n<!-- Measure against the whole document rather than the viewport -->\n<button [ngpPopoverTrigger]=\"popover\" [ngpPopoverTriggerFlip]=\"{rootBoundary: 'document'}\">\n Popover that does not flip while off-screen\n</button>",
|
|
1552
|
+
"description": "Overflow Boundary"
|
|
1553
|
+
},
|
|
1554
|
+
{
|
|
1555
|
+
"name": "example-4",
|
|
1556
|
+
"code": "<button [ngpPopoverTrigger]=\"popover\" [ngpPopoverTriggerFlip]=\"{crossAxis: false}\">\n Keep the alignment\n</button>\n<button [ngpPopoverTrigger]=\"popover\" [ngpPopoverTriggerShift]=\"{crossAxis: true}\">\n Shift on both axes\n</button>",
|
|
1557
|
+
"description": "Overflow Boundary"
|
|
1558
|
+
},
|
|
1559
|
+
{
|
|
1560
|
+
"name": "example-5",
|
|
1241
1561
|
"code": "import { providePopoverConfig } from 'ng-primitives/popover';\n\nbootstrapApplication(AppComponent, {\n providers: [\n providePopoverConfig({\n offset: 4,\n placement: 'top',\n showDelay: 0,\n hideDelay: 0,\n flip: true,\n container: document.body,\n closeOnOutsideClick: true,\n scrollBehavior: 'reposition',\n cooldown: 0,\n }),\n ],\n});",
|
|
1242
1562
|
"description": "Global Configuration"
|
|
1243
1563
|
}
|
|
@@ -1288,6 +1608,11 @@
|
|
|
1288
1608
|
"NgpPortal",
|
|
1289
1609
|
"NgpTemplatePortal",
|
|
1290
1610
|
"NgpPosition",
|
|
1611
|
+
"NgpBoundary",
|
|
1612
|
+
"NgpMiddleware",
|
|
1613
|
+
"NgpPlacement",
|
|
1614
|
+
"NgpPositioningStrategy",
|
|
1615
|
+
"NgpRootBoundary",
|
|
1291
1616
|
"BlockScrollStrategy",
|
|
1292
1617
|
"CloseScrollStrategy",
|
|
1293
1618
|
"NoopScrollStrategy",
|
|
@@ -1346,7 +1671,7 @@
|
|
|
1346
1671
|
}
|
|
1347
1672
|
],
|
|
1348
1673
|
"reusableComponent": {
|
|
1349
|
-
"code": "import { NumberInput } from '@angular/cdk/coercion';\nimport { Component, input, numberAttribute } from '@angular/core';\nimport {\n NgpProgress,\n NgpProgressIndicator,\n NgpProgressLabel,\n NgpProgressTrack,\n NgpProgressValue,\n} from 'ng-primitives/progress';\n\n@Component({\n selector: 'app-progress',\n hostDirectives: [\n {\n directive: NgpProgress,\n inputs: ['ngpProgressValue:value', 'ngpProgressMax:max', 'ngpProgressValueLabel:valueLabel'],\n },\n ],\n imports: [NgpProgressIndicator, NgpProgressTrack, NgpProgressLabel, NgpProgressValue],\n template: `\n <label ngpProgressLabel>{{ label() }}</label>\n <span ngpProgressValue>{{ value() }}%</span>\n\n <div ngpProgressTrack>\n <div ngpProgressIndicator></div>\n </div>\n `,\n styles: `\n :host {\n display: grid;\n grid-template-columns: 1fr 1fr;\n grid-row-gap: 0.5rem;\n width: 200px;\n box-sizing: border-box;\n padding: 0.5rem;\n }\n\n [ngpProgressLabel] {\n color: var(--ngp-text-emphasis);\n font-size: 14px;\n font-weight: 600;\n }\n\n [ngpProgressValue] {\n color: var(--ngp-text-secondary);\n font-size: 14px;\n font-weight: 500;\n text-align: right;\n grid-column-start: 2;\n text-align: end;\n }\n\n [ngpProgressTrack] {\n grid-column: 1 / 3;\n position: relative;\n height: 12px;\n width: 100%;\n max-width: 320px;\n overflow: hidden;\n border-radius: 0.5rem;\n border: 1px solid var(--ngp-border);\n background-color: var(--ngp-background);\n }\n\n [ngpProgressIndicator] {\n height: 100%;\n border-radius: 0.5rem;\n background-color: var(--ngp-background-inverse);\n transition: width 150ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n `,\n})\nexport class Progress {\n /** The value of the progress. */\n readonly value = input<number, NumberInput>(0, {\n transform: numberAttribute,\n });\n\n /** The label of the progress. */\n readonly label = input.required<string>();\n}\n",
|
|
1674
|
+
"code": "import { NumberInput } from '@angular/cdk/coercion';\nimport { Component, input, numberAttribute } from '@angular/core';\nimport {\n NgpProgress,\n NgpProgressIndicator,\n NgpProgressLabel,\n NgpProgressTrack,\n NgpProgressValue,\n} from 'ng-primitives/progress';\n\n@Component({\n selector: 'app-progress',\n hostDirectives: [\n {\n directive: NgpProgress,\n inputs: ['ngpProgressValue:value', 'ngpProgressMax:max', 'ngpProgressValueLabel:valueLabel'],\n },\n ],\n imports: [NgpProgressIndicator, NgpProgressTrack, NgpProgressLabel, NgpProgressValue],\n template: `\n <label ngpProgressLabel>{{ label() }}</label>\n <span ngpProgressValue>{{ value() }}%</span>\n\n <div ngpProgressTrack>\n <div ngpProgressIndicator></div>\n </div>\n `,\n styles: `\n :host {\n display: grid;\n grid-template-columns: 1fr 1fr;\n grid-row-gap: 0.5rem;\n width: 200px;\n box-sizing: border-box;\n padding: 0.5rem;\n }\n\n [ngpProgressLabel] {\n color: var(--ngp-text-emphasis);\n font-size: 14px;\n font-weight: 600;\n }\n\n [ngpProgressValue] {\n color: var(--ngp-text-secondary);\n font-size: 14px;\n font-weight: 500;\n text-align: right;\n grid-column-start: 2;\n text-align: end;\n }\n\n [ngpProgressTrack] {\n grid-column: 1 / 3;\n position: relative;\n height: 12px;\n width: 100%;\n max-width: 320px;\n overflow: hidden;\n border-radius: 0.5rem;\n border: 1px solid var(--ngp-border);\n background-color: var(--ngp-background);\n }\n\n [ngpProgressIndicator] {\n height: 100%;\n border-radius: 0.5rem;\n background-color: var(--ngp-background-inverse);\n transition: width 150ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n [ngpProgressIndicator] {\n transition-duration: 0s;\n }\n }\n `,\n})\nexport class Progress {\n /** The value of the progress. */\n readonly value = input<number, NumberInput>(0, {\n transform: numberAttribute,\n });\n\n /** The label of the progress. */\n readonly label = input.required<string>();\n}\n",
|
|
1350
1675
|
"hasVariants": false,
|
|
1351
1676
|
"hasSizes": true
|
|
1352
1677
|
}
|
|
@@ -1388,6 +1713,52 @@
|
|
|
1388
1713
|
"hasSizes": false
|
|
1389
1714
|
}
|
|
1390
1715
|
},
|
|
1716
|
+
{
|
|
1717
|
+
"name": "rating",
|
|
1718
|
+
"entryPoint": "ng-primitives/rating",
|
|
1719
|
+
"exports": [
|
|
1720
|
+
"NgpRating",
|
|
1721
|
+
"NgpRatingStateToken",
|
|
1722
|
+
"ngpRating",
|
|
1723
|
+
"injectRatingState",
|
|
1724
|
+
"provideRatingState",
|
|
1725
|
+
"type NgpRatingState",
|
|
1726
|
+
"type NgpRatingProps",
|
|
1727
|
+
"type NgpRatingItemState",
|
|
1728
|
+
"NgpRatingConfig",
|
|
1729
|
+
"provideRatingConfig",
|
|
1730
|
+
"NgpRatingItem",
|
|
1731
|
+
"type NgpRatingItemContext",
|
|
1732
|
+
"NgpRatingItemStateToken",
|
|
1733
|
+
"ngpRatingItem",
|
|
1734
|
+
"injectRatingItemState",
|
|
1735
|
+
"provideRatingItemState",
|
|
1736
|
+
"type NgpRatingItemProps"
|
|
1737
|
+
],
|
|
1738
|
+
"hasSecondaryEntryPoint": true,
|
|
1739
|
+
"category": "utility",
|
|
1740
|
+
"description": "Capture a star rating within a range.",
|
|
1741
|
+
"accessibility": [
|
|
1742
|
+
"ARIA design"
|
|
1743
|
+
],
|
|
1744
|
+
"examples": [
|
|
1745
|
+
{
|
|
1746
|
+
"name": "example-0",
|
|
1747
|
+
"code": "<div ngpRating [(ngpRatingValue)]=\"value\" [ngpRatingCount]=\"5\">\n <span *ngpRatingItem=\"let star\">{{ star.checked ? '★' : '☆' }}</span>\n</div>",
|
|
1748
|
+
"description": "Usage"
|
|
1749
|
+
},
|
|
1750
|
+
{
|
|
1751
|
+
"name": "example-1",
|
|
1752
|
+
"code": "import { provideRatingConfig } from 'ng-primitives/rating';\n\nbootstrapApplication(AppComponent, {\n providers: [\n provideRatingConfig({\n count: 5,\n clearable: false,\n valueText: (value, count) => `${value} out of ${count} stars`,\n }),\n ],\n});",
|
|
1753
|
+
"description": "Global Configuration"
|
|
1754
|
+
}
|
|
1755
|
+
],
|
|
1756
|
+
"reusableComponent": {
|
|
1757
|
+
"code": "import { Component } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport { heroStarSolid } from '@ng-icons/heroicons/solid';\nimport { injectRatingState, NgpRating, NgpRatingItem } from 'ng-primitives/rating';\nimport { ChangeFn, provideValueAccessor, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'app-rating',\n hostDirectives: [\n {\n directive: NgpRating,\n inputs: [\n 'ngpRatingValue:value',\n 'ngpRatingCount:count',\n 'ngpRatingAllowHalf:allowHalf',\n 'ngpRatingDisabled:disabled',\n 'ngpRatingReadonly:readonly',\n 'ngpRatingClearable:clearable',\n ],\n outputs: ['ngpRatingValueChange:valueChange'],\n },\n ],\n imports: [NgIcon, NgpRatingItem],\n providers: [provideIcons({ heroStarSolid }), provideValueAccessor(Rating)],\n template: `\n <span class=\"star\" *ngpRatingItem=\"let star\">\n <ng-icon class=\"star-background\" name=\"heroStarSolid\" />\n <span class=\"star-fill\" [style.width.%]=\"star.fraction * 100\">\n <ng-icon name=\"heroStarSolid\" />\n </span>\n </span>\n `,\n styles: `\n :host {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n outline: none;\n border-radius: 0.5rem;\n cursor: pointer;\n }\n\n :host[data-disabled] {\n cursor: not-allowed;\n opacity: 0.5;\n }\n\n :host[data-readonly] {\n cursor: default;\n }\n\n :host[data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n outline-offset: 2px;\n }\n\n .star {\n position: relative;\n display: inline-flex;\n font-size: 1.5rem;\n line-height: 1;\n color: var(--ngp-background-secondary);\n transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .star-fill {\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: 0;\n display: inline-flex;\n overflow: hidden;\n color: var(--ngp-primary);\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n .star {\n transition-duration: 0s;\n }\n }\n `,\n host: {\n '(focusout)': 'onTouched?.()',\n },\n})\nexport class Rating implements ControlValueAccessor {\n /** Access the rating state. */\n private readonly state = injectRatingState();\n\n /** The onChange callback function. */\n private onChange?: ChangeFn<number>;\n\n /** The onTouched callback function. */\n protected onTouched?: TouchedFn;\n\n constructor() {\n // Whenever the user interacts with the rating, call onChange with the new value.\n this.state()\n .valueChange.pipe(takeUntilDestroyed())\n .subscribe(value => this.onChange?.(value));\n }\n\n writeValue(value: number): void {\n // writing a value from the model must not re-emit through onChange\n this.state().setValue(value, { emit: false });\n }\n\n registerOnChange(fn: ChangeFn<number>): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: TouchedFn): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.state().setDisabled(isDisabled);\n }\n}\n",
|
|
1758
|
+
"hasVariants": false,
|
|
1759
|
+
"hasSizes": true
|
|
1760
|
+
}
|
|
1761
|
+
},
|
|
1391
1762
|
{
|
|
1392
1763
|
"name": "resize",
|
|
1393
1764
|
"entryPoint": "ng-primitives/resize",
|
|
@@ -1448,10 +1819,16 @@
|
|
|
1448
1819
|
"name": "search",
|
|
1449
1820
|
"entryPoint": "ng-primitives/search",
|
|
1450
1821
|
"exports": [
|
|
1451
|
-
"NgpSearchClear",
|
|
1452
1822
|
"NgpSearch",
|
|
1453
1823
|
"injectSearchState",
|
|
1454
|
-
"
|
|
1824
|
+
"ngpSearch",
|
|
1825
|
+
"NgpSearchState",
|
|
1826
|
+
"provideSearchState",
|
|
1827
|
+
"NgpSearchClear",
|
|
1828
|
+
"injectSearchClearState",
|
|
1829
|
+
"ngpSearchClear",
|
|
1830
|
+
"NgpSearchClearState",
|
|
1831
|
+
"provideSearchClearState"
|
|
1455
1832
|
],
|
|
1456
1833
|
"hasSecondaryEntryPoint": true,
|
|
1457
1834
|
"category": "form",
|
|
@@ -1533,7 +1910,7 @@
|
|
|
1533
1910
|
}
|
|
1534
1911
|
],
|
|
1535
1912
|
"reusableComponent": {
|
|
1536
|
-
"code": "import { Component, input } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport { heroChevronDown } from '@ng-icons/heroicons/outline';\nimport {\n injectSelectState,\n NgpSelect,\n NgpSelectDropdown,\n NgpSelectOption,\n NgpSelectPortal,\n} from 'ng-primitives/select';\nimport { ChangeFn, provideValueAccessor, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'app-select',\n hostDirectives: [\n {\n directive: NgpSelect,\n inputs: ['ngpSelectValue:value', 'ngpSelectMultiple:multiple', 'ngpSelectDisabled:disabled'],\n outputs: ['ngpSelectValueChange:valueChange'],\n },\n ],\n providers: [provideIcons({ heroChevronDown }), provideValueAccessor(Select)],\n imports: [NgpSelectDropdown, NgpSelectOption, NgpSelectPortal, NgIcon],\n template: `\n @if (state().value(); as value) {\n <span class=\"select-value\">{{ value }}</span>\n } @else {\n <span class=\"select-placeholder\">{{ placeholder() }}</span>\n }\n\n <ng-icon name=\"heroChevronDown\" />\n\n <div *ngpSelectPortal ngpSelectDropdown>\n @for (option of options(); track option) {\n <div [ngpSelectOptionValue]=\"option\" ngpSelectOption>\n {{ option }}\n </div>\n } @empty {\n <div class=\"empty-message\">No options found</div>\n }\n </div>\n `,\n styles: `\n :host {\n display: flex;\n justify-content: space-between;\n align-items: center;\n height: 36px;\n width: 300px;\n border-radius: 8px;\n border: none;\n background-color: var(--ngp-background);\n box-shadow: var(--ngp-input-shadow);\n box-sizing: border-box;\n }\n\n :host[data-focus] {\n outline: 2px solid var(--ngp-focus-ring);\n outline-offset: 2px;\n }\n\n .select-value,\n .select-placeholder {\n display: flex;\n align-items: center;\n flex: 1;\n padding: 0 16px;\n background-color: transparent;\n color: var(--ngp-text-primary);\n font-family: inherit;\n font-size: 14px;\n padding: 0 16px;\n height: 100%;\n }\n\n .select-placeholder {\n color: var(--ngp-text-secondary);\n }\n\n ng-icon {\n display: inline-flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n margin-inline: 8px;\n font-size: 14px;\n }\n\n [ngpSelectDropdown] {\n background-color: var(--ngp-background);\n border: 1px solid var(--ngp-border);\n padding: 0.25rem;\n border-radius: 0.75rem;\n outline: none;\n position: absolute;\n animation: popover-show 0.1s ease-out;\n width: var(--ngp-select-width);\n box-shadow: var(--ngp-shadow-lg);\n box-sizing: border-box;\n margin-top: 4px;\n max-height: 240px;\n overflow-y: auto;\n transform-origin: var(--ngp-select-transform-origin);\n }\n\n [ngpSelectDropdown][data-enter] {\n animation: select-show 0.1s ease-out;\n }\n\n [ngpSelectDropdown][data-exit] {\n animation: select-hide 0.1s ease-out;\n }\n\n [ngpSelectOption] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.375rem 0.75rem;\n cursor: pointer;\n border-radius: 0.5rem;\n width: 100%;\n height: 36px;\n font-size: 14px;\n color: var(--ngp-text-primary);\n box-sizing: border-box;\n }\n\n [ngpSelectOption][data-hover] {\n background-color: var(--ngp-background-hover);\n }\n\n [ngpSelectOption][data-press] {\n background-color: var(--ngp-background-active);\n }\n\n [ngpSelectOption][data-active] {\n background-color: var(--ngp-background-active);\n }\n\n .empty-message {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0.5rem;\n color: var(--ngp-text-secondary);\n font-size: 14px;\n font-weight: 500;\n text-align: center;\n }\n\n @keyframes select-show {\n 0% {\n opacity: 0;\n transform: translateY(-10px) scale(0.9);\n }\n 100% {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n\n @keyframes select-hide {\n 0% {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n 100% {\n opacity: 0;\n transform: translateY(-10px) scale(0.9);\n }\n }\n `,\n host: {\n '[attr.aria-label]': 'ariaLabel() || null',\n '(focusout)': 'onTouched?.()',\n },\n})\nexport class Select implements ControlValueAccessor {\n /** Access the underlying select primitive state. */\n protected readonly state = injectSelectState<string>();\n\n /** The options for the select. */\n readonly options = input<string[]>([]);\n\n /** The placeholder for the input. */\n readonly placeholder = input<string>('');\n\n /** The accessible label for the select trigger. */\n readonly ariaLabel = input<string>('');\n\n /** Form change callback. */\n private onChange?: ChangeFn<string | undefined>;\n\n /** Form touched callback. */\n protected onTouched?: TouchedFn;\n\n constructor() {\n // Forward primitive value changes (user-driven selections) to the form.\n this.state()\n .valueChange.pipe(takeUntilDestroyed())\n .subscribe(value => this.onChange?.(value as string | undefined));\n }\n\n writeValue(value: string | undefined): void {\n // Pass { emit: false } so writeValue doesn't bounce back through onChange.\n this.state().setValue(value, { emit: false });\n }\n\n registerOnChange(fn: ChangeFn<string | undefined>): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: TouchedFn): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.state().setDisabled(isDisabled);\n }\n}\n",
|
|
1913
|
+
"code": "import { Component, input } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport { heroChevronDown } from '@ng-icons/heroicons/outline';\nimport {\n injectSelectState,\n NgpSelect,\n NgpSelectDropdown,\n NgpSelectOption,\n NgpSelectPortal,\n} from 'ng-primitives/select';\nimport { ChangeFn, provideValueAccessor, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'app-select',\n hostDirectives: [\n {\n directive: NgpSelect,\n inputs: ['ngpSelectValue:value', 'ngpSelectMultiple:multiple', 'ngpSelectDisabled:disabled'],\n outputs: ['ngpSelectValueChange:valueChange'],\n },\n ],\n providers: [provideIcons({ heroChevronDown }), provideValueAccessor(Select)],\n imports: [NgpSelectDropdown, NgpSelectOption, NgpSelectPortal, NgIcon],\n template: `\n @if (state().value(); as value) {\n <span class=\"select-value\">{{ value }}</span>\n } @else {\n <span class=\"select-placeholder\">{{ placeholder() }}</span>\n }\n\n <ng-icon name=\"heroChevronDown\" />\n\n <div *ngpSelectPortal ngpSelectDropdown>\n @for (option of options(); track option) {\n <div [ngpSelectOptionValue]=\"option\" ngpSelectOption>\n {{ option }}\n </div>\n } @empty {\n <div class=\"empty-message\">No options found</div>\n }\n </div>\n `,\n styles: `\n :host {\n display: flex;\n justify-content: space-between;\n align-items: center;\n height: 36px;\n width: 300px;\n border-radius: 8px;\n border: none;\n background-color: var(--ngp-background);\n box-shadow: var(--ngp-input-shadow);\n box-sizing: border-box;\n }\n\n :host[data-focus] {\n outline: 2px solid var(--ngp-focus-ring);\n outline-offset: 2px;\n }\n\n .select-value,\n .select-placeholder {\n display: flex;\n align-items: center;\n flex: 1;\n padding: 0 16px;\n background-color: transparent;\n color: var(--ngp-text-primary);\n font-family: inherit;\n font-size: 14px;\n padding: 0 16px;\n height: 100%;\n }\n\n .select-placeholder {\n color: var(--ngp-text-secondary);\n }\n\n ng-icon {\n display: inline-flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n margin-inline: 8px;\n font-size: 14px;\n }\n\n [ngpSelectDropdown] {\n background-color: var(--ngp-background);\n border: 1px solid var(--ngp-border);\n padding: 0.25rem;\n border-radius: 0.75rem;\n outline: none;\n position: absolute;\n animation: popover-show 0.1s ease-out;\n width: var(--ngp-select-width);\n box-shadow: var(--ngp-shadow-lg);\n box-sizing: border-box;\n margin-top: 4px;\n max-height: 240px;\n overflow-y: auto;\n transform-origin: var(--ngp-select-transform-origin);\n }\n\n [ngpSelectDropdown][data-enter] {\n animation: select-show 0.1s ease-out;\n }\n\n [ngpSelectDropdown][data-exit] {\n animation: select-hide 0.1s ease-out;\n }\n\n [ngpSelectOption] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.375rem 0.75rem;\n cursor: pointer;\n border-radius: 0.5rem;\n width: 100%;\n height: 36px;\n font-size: 14px;\n color: var(--ngp-text-primary);\n box-sizing: border-box;\n }\n\n [ngpSelectOption][data-hover] {\n background-color: var(--ngp-background-hover);\n }\n\n [ngpSelectOption][data-press] {\n background-color: var(--ngp-background-active);\n }\n\n [ngpSelectOption][data-active] {\n background-color: var(--ngp-background-active);\n }\n\n .empty-message {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0.5rem;\n color: var(--ngp-text-secondary);\n font-size: 14px;\n font-weight: 500;\n text-align: center;\n }\n\n @keyframes select-show {\n 0% {\n opacity: 0;\n transform: translateY(-10px) scale(0.9);\n }\n 100% {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n\n @keyframes select-hide {\n 0% {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n 100% {\n opacity: 0;\n transform: translateY(-10px) scale(0.9);\n }\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n [ngpSelectDropdown],\n [ngpSelectDropdown][data-enter],\n [ngpSelectDropdown][data-exit] {\n animation-duration: 0s;\n }\n }\n `,\n host: {\n '[attr.aria-label]': 'ariaLabel() || null',\n '(focusout)': 'onTouched?.()',\n },\n})\nexport class Select implements ControlValueAccessor {\n /** Access the underlying select primitive state. */\n protected readonly state = injectSelectState<string>();\n\n /** The options for the select. */\n readonly options = input<string[]>([]);\n\n /** The placeholder for the input. */\n readonly placeholder = input<string>('');\n\n /** The accessible label for the select trigger. */\n readonly ariaLabel = input<string>('');\n\n /** Form change callback. */\n private onChange?: ChangeFn<string | undefined>;\n\n /** Form touched callback. */\n protected onTouched?: TouchedFn;\n\n constructor() {\n // Forward primitive value changes (user-driven selections) to the form.\n this.state()\n .valueChange.pipe(takeUntilDestroyed())\n .subscribe(value => this.onChange?.(value as string | undefined));\n }\n\n writeValue(value: string | undefined): void {\n // Pass { emit: false } so writeValue doesn't bounce back through onChange.\n this.state().setValue(value, { emit: false });\n }\n\n registerOnChange(fn: ChangeFn<string | undefined>): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: TouchedFn): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.state().setDisabled(isDisabled);\n }\n}\n",
|
|
1537
1914
|
"hasVariants": false,
|
|
1538
1915
|
"hasSizes": true
|
|
1539
1916
|
}
|
|
@@ -1688,7 +2065,7 @@
|
|
|
1688
2065
|
}
|
|
1689
2066
|
],
|
|
1690
2067
|
"reusableComponent": {
|
|
1691
|
-
"code": "import { Component } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { injectSwitchState, NgpSwitch, NgpSwitchThumb } from 'ng-primitives/switch';\nimport { ChangeFn, provideValueAccessor, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'app-switch',\n hostDirectives: [\n {\n directive: NgpSwitch,\n inputs: ['ngpSwitchChecked:checked', 'ngpSwitchDisabled:disabled'],\n outputs: ['ngpSwitchCheckedChange:checkedChange'],\n },\n ],\n imports: [NgpSwitchThumb],\n template: `\n <span ngpSwitchThumb></span>\n `,\n styles: `\n :host {\n display: inline-flex;\n align-items: center;\n position: relative;\n width: 2.5rem;\n height: 1.5rem;\n border-radius: 999px;\n background-color: var(--ngp-background-secondary);\n border: 1px solid var(--ngp-border);\n padding: 0;\n outline: none;\n transition-property:\n color, background-color, border-color, text-decoration-color, fill, stroke;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n box-sizing: border-box;\n }\n\n :host[data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n }\n\n :host[data-checked] {\n background-color: var(--ngp-background-blue);\n border-color: var(--ngp-border-blue);\n }\n\n [ngpSwitchThumb] {\n display: block;\n height: 1.25rem;\n width: 1.25rem;\n border-radius: 999px;\n background-color: white;\n box-shadow: var(--ngp-button-shadow);\n outline: none;\n transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1);\n transform: translateX(1px);\n box-sizing: border-box;\n }\n\n [ngpSwitchThumb][data-checked] {\n transform: translateX(17px);\n }\n `,\n providers: [provideValueAccessor(Switch)],\n host: {\n '(focusout)': 'onTouched?.()',\n },\n})\nexport class Switch implements ControlValueAccessor {\n /** Access the switch state. */\n private readonly switch = injectSwitchState();\n\n /** The on change callback */\n private onChange?: ChangeFn<boolean>;\n\n /** The on touched callback */\n protected onTouched?: TouchedFn;\n\n constructor() {\n // Any time the switch changes, update the form value.\n this.switch()\n .checkedChange.pipe(takeUntilDestroyed())\n .subscribe(value => this.onChange?.(value));\n }\n\n /** Write a new value to the switch. */\n writeValue(value: boolean): void {\n // writing a value from the model must not re-emit through onChange\n this.switch().setChecked(value, { emit: false });\n }\n\n /** Register a callback function to be called when the value changes. */\n registerOnChange(fn: ChangeFn<boolean>): void {\n this.onChange = fn;\n }\n\n /** Register a callback function to be called when the switch is touched. */\n registerOnTouched(fn: TouchedFn): void {\n this.onTouched = fn;\n }\n\n /** Set the disabled state of the switch. */\n setDisabledState(isDisabled: boolean): void {\n this.switch().setDisabled(isDisabled);\n }\n}\n",
|
|
2068
|
+
"code": "import { Component } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { injectSwitchState, NgpSwitch, NgpSwitchThumb } from 'ng-primitives/switch';\nimport { ChangeFn, provideValueAccessor, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'app-switch',\n hostDirectives: [\n {\n directive: NgpSwitch,\n inputs: ['ngpSwitchChecked:checked', 'ngpSwitchDisabled:disabled'],\n outputs: ['ngpSwitchCheckedChange:checkedChange'],\n },\n ],\n imports: [NgpSwitchThumb],\n template: `\n <span ngpSwitchThumb></span>\n `,\n styles: `\n :host {\n display: inline-flex;\n align-items: center;\n position: relative;\n width: 2.5rem;\n height: 1.5rem;\n border-radius: 999px;\n background-color: var(--ngp-background-secondary);\n border: 1px solid var(--ngp-border);\n padding: 0;\n outline: none;\n transition-property:\n color, background-color, border-color, text-decoration-color, fill, stroke;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n box-sizing: border-box;\n }\n\n :host[data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n }\n\n :host[data-checked] {\n background-color: var(--ngp-background-blue);\n border-color: var(--ngp-border-blue);\n }\n\n [ngpSwitchThumb] {\n display: block;\n height: 1.25rem;\n width: 1.25rem;\n border-radius: 999px;\n background-color: white;\n box-shadow: var(--ngp-button-shadow);\n outline: none;\n transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1);\n transform: translateX(1px);\n box-sizing: border-box;\n }\n\n [ngpSwitchThumb][data-checked] {\n transform: translateX(17px);\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n :host,\n [ngpSwitchThumb] {\n transition-duration: 0s;\n }\n }\n `,\n providers: [provideValueAccessor(Switch)],\n host: {\n '(focusout)': 'onTouched?.()',\n },\n})\nexport class Switch implements ControlValueAccessor {\n /** Access the switch state. */\n private readonly switch = injectSwitchState();\n\n /** The on change callback */\n private onChange?: ChangeFn<boolean>;\n\n /** The on touched callback */\n protected onTouched?: TouchedFn;\n\n constructor() {\n // Any time the switch changes, update the form value.\n this.switch()\n .checkedChange.pipe(takeUntilDestroyed())\n .subscribe(value => this.onChange?.(value));\n }\n\n /** Write a new value to the switch. */\n writeValue(value: boolean): void {\n // writing a value from the model must not re-emit through onChange\n this.switch().setChecked(value, { emit: false });\n }\n\n /** Register a callback function to be called when the value changes. */\n registerOnChange(fn: ChangeFn<boolean>): void {\n this.onChange = fn;\n }\n\n /** Register a callback function to be called when the switch is touched. */\n registerOnTouched(fn: TouchedFn): void {\n this.onTouched = fn;\n }\n\n /** Set the disabled state of the switch. */\n setDisabledState(isDisabled: boolean): void {\n this.switch().setDisabled(isDisabled);\n }\n}\n",
|
|
1692
2069
|
"hasVariants": false,
|
|
1693
2070
|
"hasSizes": false
|
|
1694
2071
|
}
|
|
@@ -1816,7 +2193,7 @@
|
|
|
1816
2193
|
}
|
|
1817
2194
|
],
|
|
1818
2195
|
"reusableComponent": {
|
|
1819
|
-
"code": "import { Component, inject } from '@angular/core';\nimport { NgpButton } from 'ng-primitives/button';\nimport { injectToastContext, NgpToast, NgpToastManager } from 'ng-primitives/toast';\n\n@Component({\n selector: 'app-toast',\n imports: [NgpButton],\n hostDirectives: [NgpToast],\n host: {\n 'animate.enter': 'toast-enter',\n 'animate.leave': 'toast-leave',\n },\n template: `\n <p class=\"toast-title\">{{ context.header }}</p>\n <p class=\"toast-description\">{{ context.description }}</p>\n <button class=\"toast-dismiss\" (click)=\"dismiss()\" ngpButton>Dismiss</button>\n `,\n styles: `\n :host {\n position: absolute;\n touch-action: none;\n box-sizing: border-box;\n align-items: center;\n gap: 6px;\n display: inline-grid;\n background: var(--ngp-background);\n box-shadow: var(--ngp-shadow);\n border: 1px solid var(--ngp-border);\n padding: 12px 16px;\n opacity: 0;\n border-radius: 8px;\n z-index: var(--ngp-toast-z-index);\n grid-template-columns: 1fr auto;\n grid-template-rows: min-content min-content;\n column-gap: 12px;\n align-items: center;\n width: 350px;\n height: fit-content;\n transform: var(--y);\n transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);\n }\n\n .toast-title {\n color: var(--ngp-text-primary);\n font-size: 0.75rem;\n font-weight: 600;\n margin: 0;\n grid-column: 1 / 2;\n grid-row: 1;\n line-height: 16px;\n user-select: none;\n }\n\n .toast-description {\n font-size: 0.75rem;\n margin: 0;\n color: var(--ngp-text-secondary);\n grid-column: 1 / 2;\n grid-row: 2;\n line-height: 16px;\n user-select: none;\n }\n\n .toast-dismiss {\n background: var(--ngp-background-inverse);\n color: var(--ngp-text-inverse);\n border: none;\n border-radius: 8px;\n padding: 4px 8px;\n font-weight: 600;\n font-size: 12px;\n cursor: pointer;\n grid-column: 2 / 3;\n grid-row: 1 / 3;\n max-height: 27px;\n }\n\n :host[data-position-x='end'] {\n right: 0;\n }\n\n :host[data-position-x='start'] {\n left: 0;\n }\n\n :host[data-position-y='top'] {\n top: 0;\n --lift: 1;\n --lift-amount: calc(var(--lift) * var(--ngp-toast-gap));\n --y: translateY(-100%);\n }\n\n :host[data-position-y='bottom'] {\n bottom: 0;\n --lift: -1;\n --lift-amount: calc(var(--lift) * var(--ngp-toast-gap));\n --y: translateY(100%);\n }\n\n :host[data-enter] {\n opacity: 1;\n --y: translateY(0);\n }\n\n :host[data-exit] {\n opacity: 0;\n --y: translateY(calc(calc(var(--lift) * var(--ngp-toast-gap)) * -1));\n }\n\n :host[data-visible='false'] {\n opacity: 0;\n pointer-events: none;\n }\n\n :host[data-expanded='true']::after {\n content: '';\n position: absolute;\n left: 0;\n height: calc(var(--ngp-toast-gap) + 1px);\n bottom: 100%;\n width: 100%;\n }\n\n :host[data-expanded='false'][data-front='false'] {\n --scale: var(--ngp-toasts-before) * 0.05 + 1;\n --y: translateY(calc(var(--lift-amount) * var(--ngp-toasts-before)))\n scale(calc(-1 * var(--scale)));\n height: var(--ngp-toast-front-height);\n }\n\n :host[data-expanded='true'] {\n --y: translateY(calc(var(--lift) * var(--ngp-toast-offset)));\n height: auto;\n }\n\n :host[data-swiping='true'] {\n transform: var(--y) translateY(var(--ngp-toast-swipe-amount-y, 0))\n translateX(var(--ngp-toast-swipe-amount-x, 0));\n transition: none;\n }\n\n :host[data-swiping='true'][data-swipe-direction='left'] {\n /* Fade out from -45px to -100px swipe */\n opacity: calc(1 - clamp(0, ((-1 * var(--ngp-toast-swipe-x, 0px)) - 45) / 55, 1));\n }\n\n :host[data-swiping='true'][data-swipe-direction='right'] {\n /* Fade out from 45px to 100px swipe */\n opacity: calc(1 - clamp(0, (var(--ngp-toast-swipe-x, 0px) - 45) / 55, 1));\n }\n\n :host[data-swiping='true'][data-swipe-direction='top'] {\n /* Fade out from -45px to -100px swipe */\n opacity: calc(1 - clamp(0, ((-1 * var(--ngp-toast-swipe-y, 0px)) - 45) / 55, 1));\n }\n\n :host[data-swiping='true'][data-swipe-direction='bottom'] {\n /* Fade out from 45px to 100px swipe */\n opacity: calc(1 - clamp(0, (var(--ngp-toast-swipe-y, 0px) - 45) / 55, 1));\n }\n\n /* Truncate text only when toast is not front AND not expanded */\n :host[data-front='false'][data-expanded='false'] .toast-title {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n :host[data-front='false'][data-expanded='false'] .toast-description {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* Angular animations based on position */\n\n /* Bottom position animations */\n :host[data-position-y='bottom'].toast-enter {\n animation: toast-slide-in-bottom 400ms cubic-bezier(0.215, 0.61, 0.355, 1);\n }\n\n :host[data-position-y='bottom'].toast-leave {\n opacity: 0;\n transform: translateY(100%);\n transition:\n opacity 400ms cubic-bezier(0.215, 0.61, 0.355, 1),\n transform 400ms cubic-bezier(0.215, 0.61, 0.355, 1);\n }\n\n /* Top position animations */\n :host[data-position-y='top'].toast-enter {\n animation: toast-slide-in-top 400ms cubic-bezier(0.215, 0.61, 0.355, 1);\n }\n\n :host[data-position-y='top'].toast-leave {\n opacity: 0;\n transform: translateY(-100%);\n transition:\n opacity 400ms cubic-bezier(0.215, 0.61, 0.355, 1),\n transform 400ms cubic-bezier(0.215, 0.61, 0.355, 1);\n }\n\n /* Keyframes for bottom position */\n @keyframes toast-slide-in-bottom {\n from {\n opacity: 0;\n transform: translateY(100%);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n /* Keyframes for top position */\n @keyframes toast-slide-in-top {\n from {\n opacity: 0;\n transform: translateY(-100%);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n `,\n})\nexport class Toast {\n private readonly toastManager = inject(NgpToastManager);\n private readonly toast = inject(NgpToast);\n protected readonly context = injectToastContext<ToastContext>();\n\n dismiss(): void {\n this.toastManager.dismiss(this.toast);\n }\n}\n\ninterface ToastContext {\n header: string;\n description: string;\n}\n",
|
|
2196
|
+
"code": "import { Component, inject } from '@angular/core';\nimport { NgpButton } from 'ng-primitives/button';\nimport { injectToastContext, NgpToast, NgpToastManager } from 'ng-primitives/toast';\n\n@Component({\n selector: 'app-toast',\n imports: [NgpButton],\n hostDirectives: [NgpToast],\n host: {\n 'animate.enter': 'toast-enter',\n 'animate.leave': 'toast-leave',\n },\n template: `\n <p class=\"toast-title\">{{ context.header }}</p>\n <p class=\"toast-description\">{{ context.description }}</p>\n <button class=\"toast-dismiss\" (click)=\"dismiss()\" ngpButton>Dismiss</button>\n `,\n styles: `\n :host {\n position: absolute;\n touch-action: none;\n box-sizing: border-box;\n align-items: center;\n gap: 6px;\n display: inline-grid;\n background: var(--ngp-background);\n box-shadow: var(--ngp-shadow);\n border: 1px solid var(--ngp-border);\n padding: 12px 16px;\n opacity: 0;\n border-radius: 8px;\n z-index: var(--ngp-toast-z-index);\n grid-template-columns: 1fr auto;\n grid-template-rows: min-content min-content;\n column-gap: 12px;\n align-items: center;\n width: 350px;\n height: fit-content;\n transform: var(--y);\n transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);\n }\n\n .toast-title {\n color: var(--ngp-text-primary);\n font-size: 0.75rem;\n font-weight: 600;\n margin: 0;\n grid-column: 1 / 2;\n grid-row: 1;\n line-height: 16px;\n user-select: none;\n }\n\n .toast-description {\n font-size: 0.75rem;\n margin: 0;\n color: var(--ngp-text-secondary);\n grid-column: 1 / 2;\n grid-row: 2;\n line-height: 16px;\n user-select: none;\n }\n\n .toast-dismiss {\n background: var(--ngp-background-inverse);\n color: var(--ngp-text-inverse);\n border: none;\n border-radius: 8px;\n padding: 4px 8px;\n font-weight: 600;\n font-size: 12px;\n cursor: pointer;\n grid-column: 2 / 3;\n grid-row: 1 / 3;\n max-height: 27px;\n }\n\n :host[data-position-x='end'] {\n right: 0;\n }\n\n :host[data-position-x='start'] {\n left: 0;\n }\n\n :host[data-position-y='top'] {\n top: 0;\n --lift: 1;\n --lift-amount: calc(var(--lift) * var(--ngp-toast-gap));\n --y: translateY(-100%);\n }\n\n :host[data-position-y='bottom'] {\n bottom: 0;\n --lift: -1;\n --lift-amount: calc(var(--lift) * var(--ngp-toast-gap));\n --y: translateY(100%);\n }\n\n :host[data-enter] {\n opacity: 1;\n --y: translateY(0);\n }\n\n :host[data-exit] {\n opacity: 0;\n --y: translateY(calc(calc(var(--lift) * var(--ngp-toast-gap)) * -1));\n }\n\n :host[data-visible='false'] {\n opacity: 0;\n pointer-events: none;\n }\n\n :host[data-expanded='true']::after {\n content: '';\n position: absolute;\n left: 0;\n height: calc(var(--ngp-toast-gap) + 1px);\n bottom: 100%;\n width: 100%;\n }\n\n :host[data-expanded='false'][data-front='false'] {\n --scale: var(--ngp-toasts-before) * 0.05 + 1;\n --y: translateY(calc(var(--lift-amount) * var(--ngp-toasts-before)))\n scale(calc(-1 * var(--scale)));\n height: var(--ngp-toast-front-height);\n }\n\n :host[data-expanded='true'] {\n --y: translateY(calc(var(--lift) * var(--ngp-toast-offset)));\n height: auto;\n }\n\n :host[data-swiping='true'] {\n transform: var(--y) translateY(var(--ngp-toast-swipe-amount-y, 0))\n translateX(var(--ngp-toast-swipe-amount-x, 0));\n transition: none;\n }\n\n :host[data-swiping='true'][data-swipe-direction='left'] {\n /* Fade out from -45px to -100px swipe */\n opacity: calc(1 - clamp(0, ((-1 * var(--ngp-toast-swipe-x, 0px)) - 45) / 55, 1));\n }\n\n :host[data-swiping='true'][data-swipe-direction='right'] {\n /* Fade out from 45px to 100px swipe */\n opacity: calc(1 - clamp(0, (var(--ngp-toast-swipe-x, 0px) - 45) / 55, 1));\n }\n\n :host[data-swiping='true'][data-swipe-direction='top'] {\n /* Fade out from -45px to -100px swipe */\n opacity: calc(1 - clamp(0, ((-1 * var(--ngp-toast-swipe-y, 0px)) - 45) / 55, 1));\n }\n\n :host[data-swiping='true'][data-swipe-direction='bottom'] {\n /* Fade out from 45px to 100px swipe */\n opacity: calc(1 - clamp(0, (var(--ngp-toast-swipe-y, 0px) - 45) / 55, 1));\n }\n\n /* Truncate text only when toast is not front AND not expanded */\n :host[data-front='false'][data-expanded='false'] .toast-title {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n :host[data-front='false'][data-expanded='false'] .toast-description {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* Angular animations based on position */\n\n /* Bottom position animations */\n :host[data-position-y='bottom'].toast-enter {\n animation: toast-slide-in-bottom 400ms cubic-bezier(0.215, 0.61, 0.355, 1);\n }\n\n :host[data-position-y='bottom'].toast-leave {\n opacity: 0;\n transform: translateY(100%);\n transition:\n opacity 400ms cubic-bezier(0.215, 0.61, 0.355, 1),\n transform 400ms cubic-bezier(0.215, 0.61, 0.355, 1);\n }\n\n /* Top position animations */\n :host[data-position-y='top'].toast-enter {\n animation: toast-slide-in-top 400ms cubic-bezier(0.215, 0.61, 0.355, 1);\n }\n\n :host[data-position-y='top'].toast-leave {\n opacity: 0;\n transform: translateY(-100%);\n transition:\n opacity 400ms cubic-bezier(0.215, 0.61, 0.355, 1),\n transform 400ms cubic-bezier(0.215, 0.61, 0.355, 1);\n }\n\n /* Keyframes for bottom position */\n @keyframes toast-slide-in-bottom {\n from {\n opacity: 0;\n transform: translateY(100%);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n /* Keyframes for top position */\n @keyframes toast-slide-in-top {\n from {\n opacity: 0;\n transform: translateY(-100%);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n :host[data-position-y='bottom'].toast-enter,\n :host[data-position-y='top'].toast-enter {\n animation-duration: 0s;\n }\n\n :host,\n :host[data-position-y='bottom'].toast-leave,\n :host[data-position-y='top'].toast-leave {\n transition-duration: 0s;\n }\n }\n `,\n})\nexport class Toast {\n private readonly toastManager = inject(NgpToastManager);\n private readonly toast = inject(NgpToast);\n protected readonly context = injectToastContext<ToastContext>();\n\n dismiss(): void {\n this.toastManager.dismiss(this.toast);\n }\n}\n\ninterface ToastContext {\n header: string;\n description: string;\n}\n",
|
|
1820
2197
|
"hasVariants": false,
|
|
1821
2198
|
"hasSizes": true
|
|
1822
2199
|
}
|
|
@@ -1844,7 +2221,7 @@
|
|
|
1844
2221
|
}
|
|
1845
2222
|
],
|
|
1846
2223
|
"reusableComponent": {
|
|
1847
|
-
"code": "import { Component } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { NgpButton } from 'ng-primitives/button';\nimport { injectToggleState, NgpToggle } from 'ng-primitives/toggle';\nimport { ChangeFn, provideValueAccessor, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'button[app-toggle]',\n hostDirectives: [\n {\n directive: NgpToggle,\n inputs: ['ngpToggleSelected:selected', 'ngpToggleDisabled:disabled'],\n outputs: ['ngpToggleSelectedChange:selectedChange'],\n },\n { directive: NgpButton, inputs: ['disabled'] },\n ],\n template: `\n <ng-content />\n `,\n styles: `\n :host {\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n color: var(--ngp-text-primary);\n border: none;\n outline: none;\n height: 2.5rem;\n font-weight: 500;\n background-color: var(--ngp-background);\n transition: background-color 300ms cubic-bezier(0.4, 0, 0.2, 1);\n box-shadow: var(--ngp-button-shadow);\n }\n\n :host[data-hover] {\n background-color: var(--ngp-background-hover);\n }\n\n :host[data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n outline-offset: 2px;\n }\n\n :host[data-press] {\n background-color: var(--ngp-background-active);\n }\n\n :host[data-selected] {\n background-color: var(--ngp-background-inverse);\n color: var(--ngp-text-inverse);\n }\n `,\n providers: [provideValueAccessor(Toggle)],\n host: {\n '(focusout)': 'onTouched?.()',\n },\n})\nexport class Toggle implements ControlValueAccessor {\n /** Access the toggle state. */\n private readonly toggle = injectToggleState();\n\n /** The on change callback */\n private onChange?: ChangeFn<boolean>;\n\n /** The on touched callback */\n protected onTouched?: TouchedFn;\n\n constructor() {\n // Any time the toggle changes, update the form value.\n this.toggle()\n .selectedChange.pipe(takeUntilDestroyed())\n .subscribe(value => this.onChange?.(value));\n }\n\n /** Write a new value to the toggle. */\n writeValue(value: boolean): void {\n this.toggle().setSelected(value, { emit: false });\n }\n\n /** Register a callback function to be called when the value changes. */\n registerOnChange(fn: ChangeFn<boolean>): void {\n this.onChange = fn;\n }\n\n /** Register a callback function to be called when the toggle is touched. */\n registerOnTouched(fn: TouchedFn): void {\n this.onTouched = fn;\n }\n\n /** Set the disabled state of the toggle. */\n setDisabledState(isDisabled: boolean): void {\n this.toggle().setDisabled(isDisabled);\n }\n}\n",
|
|
2224
|
+
"code": "import { Component } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { NgpButton } from 'ng-primitives/button';\nimport { injectToggleState, NgpToggle } from 'ng-primitives/toggle';\nimport { ChangeFn, provideValueAccessor, TouchedFn } from 'ng-primitives/utils';\n\n@Component({\n selector: 'button[app-toggle]',\n hostDirectives: [\n {\n directive: NgpToggle,\n inputs: ['ngpToggleSelected:selected', 'ngpToggleDisabled:disabled'],\n outputs: ['ngpToggleSelectedChange:selectedChange'],\n },\n { directive: NgpButton, inputs: ['disabled'] },\n ],\n template: `\n <ng-content />\n `,\n styles: `\n :host {\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n color: var(--ngp-text-primary);\n border: none;\n outline: none;\n height: 2.5rem;\n font-weight: 500;\n background-color: var(--ngp-background);\n transition: background-color 300ms cubic-bezier(0.4, 0, 0.2, 1);\n box-shadow: var(--ngp-button-shadow);\n }\n\n :host[data-hover] {\n background-color: var(--ngp-background-hover);\n }\n\n :host[data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n outline-offset: 2px;\n }\n\n :host[data-press] {\n background-color: var(--ngp-background-active);\n }\n\n :host[data-selected] {\n background-color: var(--ngp-background-inverse);\n color: var(--ngp-text-inverse);\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n :host {\n transition-duration: 0s;\n }\n }\n `,\n providers: [provideValueAccessor(Toggle)],\n host: {\n '(focusout)': 'onTouched?.()',\n },\n})\nexport class Toggle implements ControlValueAccessor {\n /** Access the toggle state. */\n private readonly toggle = injectToggleState();\n\n /** The on change callback */\n private onChange?: ChangeFn<boolean>;\n\n /** The on touched callback */\n protected onTouched?: TouchedFn;\n\n constructor() {\n // Any time the toggle changes, update the form value.\n this.toggle()\n .selectedChange.pipe(takeUntilDestroyed())\n .subscribe(value => this.onChange?.(value));\n }\n\n /** Write a new value to the toggle. */\n writeValue(value: boolean): void {\n this.toggle().setSelected(value, { emit: false });\n }\n\n /** Register a callback function to be called when the value changes. */\n registerOnChange(fn: ChangeFn<boolean>): void {\n this.onChange = fn;\n }\n\n /** Register a callback function to be called when the toggle is touched. */\n registerOnTouched(fn: TouchedFn): void {\n this.onTouched = fn;\n }\n\n /** Set the disabled state of the toggle. */\n setDisabledState(isDisabled: boolean): void {\n this.toggle().setDisabled(isDisabled);\n }\n}\n",
|
|
1848
2225
|
"hasVariants": false,
|
|
1849
2226
|
"hasSizes": false
|
|
1850
2227
|
}
|
|
@@ -1887,7 +2264,7 @@
|
|
|
1887
2264
|
}
|
|
1888
2265
|
],
|
|
1889
2266
|
"reusableComponent": {
|
|
1890
|
-
"code": "import { Component } from '@angular/core';\nimport { NgpButton } from 'ng-primitives/button';\nimport { NgpToggleGroupItem } from 'ng-primitives/toggle-group';\n\n@Component({\n selector: 'button[app-toggle-group-item]',\n hostDirectives: [\n {\n directive: NgpToggleGroupItem,\n inputs: ['ngpToggleGroupItemValue:value', 'ngpToggleGroupItemDisabled:disabled'],\n },\n {\n directive: NgpButton,\n inputs: ['disabled'],\n },\n ],\n template: `\n <ng-content />\n `,\n styles: `\n :host {\n display: flex;\n width: 2rem;\n height: 2rem;\n align-items: center;\n justify-content: center;\n border-radius: 0.25rem;\n border: 1px solid transparent;\n background: transparent;\n outline: none;\n transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1);\n box-sizing: border-box;\n color: var(--ngp-text-primary);\n font-size: 1.125rem;\n }\n\n :host[data-hover] {\n background-color: var(--ngp-background-hover);\n border-color: var(--ngp-border);\n }\n\n :host[data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n }\n\n :host[data-press] {\n background-color: var(--ngp-background-active);\n }\n\n :host[data-selected] {\n background-color: var(--ngp-background-inverse);\n color: var(--ngp-text-inverse);\n border-color: transparent;\n }\n `,\n})\nexport class ToggleGroupItem {}\n",
|
|
2267
|
+
"code": "import { Component } from '@angular/core';\nimport { NgpButton } from 'ng-primitives/button';\nimport { NgpToggleGroupItem } from 'ng-primitives/toggle-group';\n\n@Component({\n selector: 'button[app-toggle-group-item]',\n hostDirectives: [\n {\n directive: NgpToggleGroupItem,\n inputs: ['ngpToggleGroupItemValue:value', 'ngpToggleGroupItemDisabled:disabled'],\n },\n {\n directive: NgpButton,\n inputs: ['disabled'],\n },\n ],\n template: `\n <ng-content />\n `,\n styles: `\n :host {\n display: flex;\n width: 2rem;\n height: 2rem;\n align-items: center;\n justify-content: center;\n border-radius: 0.25rem;\n border: 1px solid transparent;\n background: transparent;\n outline: none;\n transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1);\n box-sizing: border-box;\n color: var(--ngp-text-primary);\n font-size: 1.125rem;\n }\n\n :host[data-hover] {\n background-color: var(--ngp-background-hover);\n border-color: var(--ngp-border);\n }\n\n :host[data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n }\n\n :host[data-press] {\n background-color: var(--ngp-background-active);\n }\n\n :host[data-selected] {\n background-color: var(--ngp-background-inverse);\n color: var(--ngp-text-inverse);\n border-color: transparent;\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n :host {\n transition-duration: 0s;\n }\n }\n `,\n})\nexport class ToggleGroupItem {}\n",
|
|
1891
2268
|
"hasVariants": false,
|
|
1892
2269
|
"hasSizes": true
|
|
1893
2270
|
}
|
|
@@ -1917,7 +2294,7 @@
|
|
|
1917
2294
|
}
|
|
1918
2295
|
],
|
|
1919
2296
|
"reusableComponent": {
|
|
1920
|
-
"code": "import { Component } from '@angular/core';\nimport { NgpButton } from 'ng-primitives/button';\nimport { NgpRovingFocusItem } from 'ng-primitives/roving-focus';\n\n@Component({\n selector: 'button[app-toolbar-button]',\n hostDirectives: [\n { directive: NgpButton, inputs: ['disabled'] },\n {\n directive: NgpRovingFocusItem,\n inputs: ['ngpRovingFocusItemDisabled:disabled'],\n },\n ],\n host: {\n type: 'button',\n },\n template: `\n <ng-content />\n `,\n styles: `\n :host {\n display: flex;\n width: 2rem;\n height: 2rem;\n align-items: center;\n justify-content: center;\n border-radius: 0.25rem;\n border: 1px solid transparent;\n background: transparent;\n outline: none;\n transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1);\n box-sizing: border-box;\n color: var(--ngp-text-primary);\n cursor: pointer;\n }\n\n :host[data-hover] {\n background-color: var(--ngp-background-hover);\n border-color: var(--ngp-border);\n }\n\n :host[data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n }\n\n :host[data-press] {\n background-color: var(--ngp-background-active);\n }\n\n :host[data-selected] {\n background-color: var(--ngp-background-inverse);\n color: var(--ngp-text-inverse);\n }\n `,\n})\nexport class ToolbarButton {}\n",
|
|
2297
|
+
"code": "import { Component } from '@angular/core';\nimport { NgpButton } from 'ng-primitives/button';\nimport { NgpRovingFocusItem } from 'ng-primitives/roving-focus';\n\n@Component({\n selector: 'button[app-toolbar-button]',\n hostDirectives: [\n { directive: NgpButton, inputs: ['disabled'] },\n {\n directive: NgpRovingFocusItem,\n inputs: ['ngpRovingFocusItemDisabled:disabled'],\n },\n ],\n host: {\n type: 'button',\n },\n template: `\n <ng-content />\n `,\n styles: `\n :host {\n display: flex;\n width: 2rem;\n height: 2rem;\n align-items: center;\n justify-content: center;\n border-radius: 0.25rem;\n border: 1px solid transparent;\n background: transparent;\n outline: none;\n transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1);\n box-sizing: border-box;\n color: var(--ngp-text-primary);\n cursor: pointer;\n }\n\n :host[data-hover] {\n background-color: var(--ngp-background-hover);\n border-color: var(--ngp-border);\n }\n\n :host[data-focus-visible] {\n outline: 2px solid var(--ngp-focus-ring);\n }\n\n :host[data-press] {\n background-color: var(--ngp-background-active);\n }\n\n :host[data-selected] {\n background-color: var(--ngp-background-inverse);\n color: var(--ngp-text-inverse);\n }\n\n /* Reduce motion for users who prefer reduced motion. */\n @media (prefers-reduced-motion: reduce) {\n :host {\n transition-duration: 0s;\n }\n }\n `,\n})\nexport class ToolbarButton {}\n",
|
|
1921
2298
|
"hasVariants": false,
|
|
1922
2299
|
"hasSizes": false
|
|
1923
2300
|
}
|
|
@@ -1982,21 +2359,31 @@
|
|
|
1982
2359
|
},
|
|
1983
2360
|
{
|
|
1984
2361
|
"name": "example-3",
|
|
2362
|
+
"code": "<!-- Keep the tooltip inside a container element -->\n<div #boundary>\n <button [ngpTooltipTrigger]=\"tooltip\" [ngpTooltipTriggerFlip]=\"{boundary: boundary}\">\n Tooltip constrained to a container\n </button>\n</div>\n\n<!-- Keep the tooltip inside whatever scrolls the trigger -->\n<div style=\"overflow: auto\">\n <button [ngpTooltipTrigger]=\"tooltip\" [ngpTooltipTriggerFlip]=\"{altBoundary: true}\">\n Tooltip constrained to the trigger's scroll container\n </button>\n</div>\n\n<!-- Measure against the whole document rather than the viewport -->\n<button [ngpTooltipTrigger]=\"tooltip\" [ngpTooltipTriggerFlip]=\"{rootBoundary: 'document'}\">\n Tooltip that does not flip while off-screen\n</button>",
|
|
2363
|
+
"description": "Overflow Boundary"
|
|
2364
|
+
},
|
|
2365
|
+
{
|
|
2366
|
+
"name": "example-4",
|
|
2367
|
+
"code": "<button [ngpTooltipTrigger]=\"tooltip\" [ngpTooltipTriggerFlip]=\"{crossAxis: false}\">\n Keep the alignment\n</button>\n<button [ngpTooltipTrigger]=\"tooltip\" [ngpTooltipTriggerShift]=\"{crossAxis: true}\">\n Shift on both axes\n</button>",
|
|
2368
|
+
"description": "Overflow Boundary"
|
|
2369
|
+
},
|
|
2370
|
+
{
|
|
2371
|
+
"name": "example-5",
|
|
1985
2372
|
"code": "<!-- Simple usage - uses text content automatically -->\n<div class=\"truncated-text\" ngpTooltipTrigger>\n This text might be truncated with ellipsis and show the full content in the tooltip\n</div>\n\n<!-- Passing content directly takes precedence -->\n<button [ngpTooltipTrigger]=\"myToolip\">This won't show a tooltip unless content is provided</button>",
|
|
1986
2373
|
"description": "Using Text Content as Tooltip"
|
|
1987
2374
|
},
|
|
1988
2375
|
{
|
|
1989
|
-
"name": "example-
|
|
2376
|
+
"name": "example-6",
|
|
1990
2377
|
"code": "<div\n class=\"truncated-text\"\n appTooltipTrigger=\"This tooltip only shows when text overflows\"\n ngpTooltipTriggerShowOnOverflow\n>\n This text might be truncated\n</div>",
|
|
1991
2378
|
"description": "Conditional Tooltips"
|
|
1992
2379
|
},
|
|
1993
2380
|
{
|
|
1994
|
-
"name": "example-
|
|
2381
|
+
"name": "example-7",
|
|
1995
2382
|
"code": "<!-- Default: closes when pointer leaves trigger -->\n<button [ngpTooltipTrigger]=\"tooltip\">Default behavior</button>\n\n<!-- Opt in: stays open while pointer moves into tooltip content -->\n<button [ngpTooltipTrigger]=\"tooltip\" ngpTooltipTriggerHoverableContent=\"true\">\n Hover bridge enabled\n</button>",
|
|
1996
2383
|
"description": "Hoverable Tooltip Content"
|
|
1997
2384
|
},
|
|
1998
2385
|
{
|
|
1999
|
-
"name": "example-
|
|
2386
|
+
"name": "example-8",
|
|
2000
2387
|
"code": "import { provideTooltipConfig } from 'ng-primitives/tooltip';\n\nbootstrapApplication(AppComponent, {\n providers: [\n provideTooltipConfig({\n offset: 4,\n placement: 'top',\n showDelay: 0,\n hideDelay: 500,\n flip: true,\n container: document.body,\n showOnOverflow: false,\n useTextContent: true,\n scrollBehavior: 'reposition',\n cooldown: 300,\n hoverableContent: false,\n }),\n ],\n});",
|
|
2001
2388
|
"description": "Global Configuration"
|
|
2002
2389
|
}
|