@ng-primitives/mcp 0.97.0 → 0.98.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-primitives/mcp",
3
- "version": "0.97.0",
3
+ "version": "0.98.0",
4
4
  "description": "MCP server for Angular Primitives - headless UI library",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -1442,6 +1442,51 @@
1442
1442
  "inputs": [],
1443
1443
  "outputs": []
1444
1444
  },
1445
+ "NgpSubmenuTrigger": {
1446
+ "name": "NgpSubmenuTrigger",
1447
+ "description": "",
1448
+ "selector": "[ngpSubmenuTrigger]",
1449
+ "exportAs": [
1450
+ "ngpSubmenuTrigger"
1451
+ ],
1452
+ "inputs": [
1453
+ {
1454
+ "name": "ngpSubmenuTrigger",
1455
+ "type": "NgpOverlayContent<T> | undefined",
1456
+ "description": "Access the submenu template ref.",
1457
+ "isRequired": false
1458
+ },
1459
+ {
1460
+ "name": "ngpSubmenuTriggerDisabled",
1461
+ "type": "boolean",
1462
+ "description": "Define if the trigger should be disabled.",
1463
+ "isRequired": false,
1464
+ "defaultValue": "false"
1465
+ },
1466
+ {
1467
+ "name": "ngpSubmenuTriggerPlacement",
1468
+ "type": "NgpMenuPlacement",
1469
+ "description": "Define the placement of the menu relative to the trigger.",
1470
+ "isRequired": false,
1471
+ "defaultValue": "'right-start'"
1472
+ },
1473
+ {
1474
+ "name": "ngpSubmenuTriggerOffset",
1475
+ "type": "NgpOffset",
1476
+ "description": "Define the offset of the menu relative to the trigger.\nCan be a number (applies to mainAxis) or an object with mainAxis, crossAxis, and alignmentAxis.",
1477
+ "isRequired": false,
1478
+ "defaultValue": "0"
1479
+ },
1480
+ {
1481
+ "name": "ngpSubmenuTriggerFlip",
1482
+ "type": "boolean",
1483
+ "description": "Define whether the menu should flip when there is not enough space for the menu.",
1484
+ "isRequired": false,
1485
+ "defaultValue": "true"
1486
+ }
1487
+ ],
1488
+ "outputs": []
1489
+ },
1445
1490
  "NgpMenuTrigger": {
1446
1491
  "name": "NgpMenuTrigger",
1447
1492
  "description": "The `NgpMenuTrigger` directive allows you to turn an element into a menu trigger.",
@@ -1484,6 +1529,13 @@
1484
1529
  "isRequired": false,
1485
1530
  "defaultValue": "true"
1486
1531
  },
1532
+ {
1533
+ "name": "ngpMenuTriggerShift",
1534
+ "type": "NgpShift",
1535
+ "description": "Configure shift behavior to keep the menu in view.\nCan be a boolean to enable/disable, or an object with padding and limiter options.",
1536
+ "isRequired": false,
1537
+ "defaultValue": "undefined (enabled by default in overlay)"
1538
+ },
1487
1539
  {
1488
1540
  "name": "ngpMenuTriggerContainer",
1489
1541
  "type": "string | HTMLElement | null",
@@ -1507,51 +1559,6 @@
1507
1559
  ],
1508
1560
  "outputs": []
1509
1561
  },
1510
- "NgpSubmenuTrigger": {
1511
- "name": "NgpSubmenuTrigger",
1512
- "description": "",
1513
- "selector": "[ngpSubmenuTrigger]",
1514
- "exportAs": [
1515
- "ngpSubmenuTrigger"
1516
- ],
1517
- "inputs": [
1518
- {
1519
- "name": "ngpSubmenuTrigger",
1520
- "type": "NgpOverlayContent<T> | undefined",
1521
- "description": "Access the submenu template ref.",
1522
- "isRequired": false
1523
- },
1524
- {
1525
- "name": "ngpSubmenuTriggerDisabled",
1526
- "type": "boolean",
1527
- "description": "Define if the trigger should be disabled.",
1528
- "isRequired": false,
1529
- "defaultValue": "false"
1530
- },
1531
- {
1532
- "name": "ngpSubmenuTriggerPlacement",
1533
- "type": "NgpMenuPlacement",
1534
- "description": "Define the placement of the menu relative to the trigger.",
1535
- "isRequired": false,
1536
- "defaultValue": "'right-start'"
1537
- },
1538
- {
1539
- "name": "ngpSubmenuTriggerOffset",
1540
- "type": "NgpOffset",
1541
- "description": "Define the offset of the menu relative to the trigger.\nCan be a number (applies to mainAxis) or an object with mainAxis, crossAxis, and alignmentAxis.",
1542
- "isRequired": false,
1543
- "defaultValue": "0"
1544
- },
1545
- {
1546
- "name": "ngpSubmenuTriggerFlip",
1547
- "type": "boolean",
1548
- "description": "Define whether the menu should flip when there is not enough space for the menu.",
1549
- "isRequired": false,
1550
- "defaultValue": "true"
1551
- }
1552
- ],
1553
- "outputs": []
1554
- },
1555
1562
  "NgpMeterTrack": {
1556
1563
  "name": "NgpMeterTrack",
1557
1564
  "description": "",
@@ -1829,6 +1836,13 @@
1829
1836
  "isRequired": false,
1830
1837
  "defaultValue": "true"
1831
1838
  },
1839
+ {
1840
+ "name": "ngpPopoverTriggerShift",
1841
+ "type": "NgpShift",
1842
+ "description": "Configure shift behavior to keep the popover in view.\nCan be a boolean to enable/disable, or an object with padding and limiter options.",
1843
+ "isRequired": false,
1844
+ "defaultValue": "undefined (enabled by default in overlay)"
1845
+ },
1832
1846
  {
1833
1847
  "name": "ngpPopoverTriggerContainer",
1834
1848
  "type": "string | HTMLElement | null",
@@ -2769,6 +2783,13 @@
2769
2783
  "isRequired": false,
2770
2784
  "defaultValue": "true"
2771
2785
  },
2786
+ {
2787
+ "name": "ngpTooltipTriggerShift",
2788
+ "type": "NgpShift",
2789
+ "description": "Configure shift behavior to keep the tooltip in view.\nCan be a boolean to enable/disable, or an object with padding and limiter options.",
2790
+ "isRequired": false,
2791
+ "defaultValue": "undefined (enabled by default in overlay)"
2792
+ },
2772
2793
  {
2773
2794
  "name": "ngpTooltipTriggerContainer",
2774
2795
  "type": "string | HTMLElement | null",
@@ -763,16 +763,21 @@
763
763
  },
764
764
  {
765
765
  "name": "example-2",
766
+ "code": "<!-- Disable shift -->\n<button [ngpMenuTrigger]=\"menu\" [ngpMenuTriggerShift]=\"false\">Menu without shift</button>\n\n<!-- Object shift for precise control -->\n<button [ngpMenuTrigger]=\"menu\" [ngpMenuTriggerShift]=\"{padding: 8}\">\n Menu with custom shift padding\n</button>",
767
+ "description": "Custom Shift"
768
+ },
769
+ {
770
+ "name": "example-3",
766
771
  "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 }),\n ],\n});",
767
772
  "description": "Global Configuration"
768
773
  },
769
774
  {
770
- "name": "example-3",
775
+ "name": "example-4",
771
776
  "code": "offset: {\n mainAxis: 8, // Distance between menu and trigger element\n crossAxis: 4, // Skidding along the alignment axis \n alignmentAxis: 2 // Same as crossAxis but for aligned placements\n }",
772
777
  "description": "NgpMenuConfig"
773
778
  },
774
779
  {
775
- "name": "example-4",
780
+ "name": "example-5",
776
781
  "code": "shift: {\n padding: 8, // Minimum padding between menu and viewport edges\n limiter: { // Optional limiter to control shifting behavior\n fn: limitShift,\n options: { /* limiter options */ }\n }\n }",
777
782
  "description": "NgpMenuConfig"
778
783
  }
@@ -883,16 +888,21 @@
883
888
  },
884
889
  {
885
890
  "name": "example-2",
891
+ "code": "<!-- Disable shift -->\n<button [ngpPopoverTrigger]=\"popover\" [ngpPopoverTriggerShift]=\"false\">\n Popover without shift\n</button>\n\n<!-- Object shift for precise control -->\n<button [ngpPopoverTrigger]=\"popover\" [ngpPopoverTriggerShift]=\"{padding: 8}\">\n Popover with custom shift padding\n</button>",
892
+ "description": "Custom Shift"
893
+ },
894
+ {
895
+ "name": "example-3",
886
896
  "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 }),\n ],\n});",
887
897
  "description": "Global Configuration"
888
898
  },
889
899
  {
890
- "name": "example-3",
900
+ "name": "example-4",
891
901
  "code": "offset: {\n mainAxis: 8, // Distance between popover and trigger element\n crossAxis: 4, // Skidding along the alignment axis \n alignmentAxis: 2 // Same as crossAxis but for aligned placements\n }",
892
902
  "description": "NgpPopoverConfig"
893
903
  },
894
904
  {
895
- "name": "example-4",
905
+ "name": "example-5",
896
906
  "code": "shift: {\n padding: 8, // Minimum padding between popover and viewport edges\n limiter: { // Optional limiter to control shifting behavior\n fn: limitShift,\n options: { /* limiter options */ }\n }\n }",
897
907
  "description": "NgpPopoverConfig"
898
908
  }
@@ -1374,7 +1384,8 @@
1374
1384
  "NgpToast",
1375
1385
  "injectToastContext",
1376
1386
  "NgpToastManager",
1377
- "NgpToastRef"
1387
+ "type NgpToastRef",
1388
+ "type NgpToastOptions"
1378
1389
  ],
1379
1390
  "hasSecondaryEntryPoint": true,
1380
1391
  "category": "feedback",
@@ -1400,7 +1411,7 @@
1400
1411
  }
1401
1412
  ],
1402
1413
  "reusableComponent": {
1403
- "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 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 transition:\n transform 0.4s,\n opacity 0.4s,\n height 0.4s,\n box-shadow 0.2s;\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 transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);\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: var(--ngp-toast-width);\n height: fit-content;\n transform: var(--y);\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: var(--ngp-toast-height);\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})\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",
1414
+ "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",
1404
1415
  "hasVariants": false,
1405
1416
  "hasSizes": true
1406
1417
  }
@@ -1526,26 +1537,31 @@
1526
1537
  },
1527
1538
  {
1528
1539
  "name": "example-2",
1540
+ "code": "<!-- Disable shift -->\n<button [ngpTooltipTrigger]=\"tooltip\" [ngpTooltipTriggerShift]=\"false\">\n Tooltip without shift\n</button>\n\n<!-- Object shift for precise control -->\n<button [ngpTooltipTrigger]=\"tooltip\" [ngpTooltipTriggerShift]=\"{padding: 8}\">\n Tooltip with custom shift padding\n</button>",
1541
+ "description": "Custom Shift"
1542
+ },
1543
+ {
1544
+ "name": "example-3",
1529
1545
  "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>",
1530
1546
  "description": "Using Text Content as Tooltip"
1531
1547
  },
1532
1548
  {
1533
- "name": "example-3",
1549
+ "name": "example-4",
1534
1550
  "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>",
1535
1551
  "description": "Conditional Tooltips"
1536
1552
  },
1537
1553
  {
1538
- "name": "example-4",
1554
+ "name": "example-5",
1539
1555
  "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 }),\n ],\n});",
1540
1556
  "description": "Global Configuration"
1541
1557
  },
1542
1558
  {
1543
- "name": "example-5",
1559
+ "name": "example-6",
1544
1560
  "code": "offset: {\n mainAxis: 8, // Distance between tooltip and trigger element\n crossAxis: 4, // Skidding along the alignment axis\n alignmentAxis: 2 // Same as crossAxis but for aligned placements\n}",
1545
1561
  "description": "NgpTooltipConfig"
1546
1562
  },
1547
1563
  {
1548
- "name": "example-6",
1564
+ "name": "example-7",
1549
1565
  "code": "shift: {\n padding: 8, // Minimum padding between tooltip and viewport edges\n limiter: { // Optional limiter to control shifting behavior\n fn: limitShift,\n options: { /* limiter options */ }\n }\n}",
1550
1566
  "description": "NgpTooltipConfig"
1551
1567
  }
@@ -190,7 +190,7 @@
190
190
  },
191
191
  {
192
192
  "name": "toast",
193
- "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 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 transition:\n transform 0.4s,\n opacity 0.4s,\n height 0.4s,\n box-shadow 0.2s;\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 transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);\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: var(--ngp-toast-width);\n height: fit-content;\n transform: var(--y);\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: var(--ngp-toast-height);\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})\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",
193
+ "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",
194
194
  "primitive": "toast",
195
195
  "hasVariants": false,
196
196
  "hasSizes": true