@ng-primitives/mcp 0.96.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.96.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,13 +763,23 @@
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"
778
+ },
779
+ {
780
+ "name": "example-5",
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 }",
782
+ "description": "NgpMenuConfig"
773
783
  }
774
784
  ],
775
785
  "reusableComponent": {
@@ -878,13 +888,23 @@
878
888
  },
879
889
  {
880
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",
881
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});",
882
897
  "description": "Global Configuration"
883
898
  },
884
899
  {
885
- "name": "example-3",
900
+ "name": "example-4",
886
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 }",
887
902
  "description": "NgpPopoverConfig"
903
+ },
904
+ {
905
+ "name": "example-5",
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 }",
907
+ "description": "NgpPopoverConfig"
888
908
  }
889
909
  ],
890
910
  "reusableComponent": {
@@ -897,6 +917,10 @@
897
917
  "name": "portal",
898
918
  "entryPoint": "ng-primitives/portal",
899
919
  "exports": [
920
+ "coerceOffset",
921
+ "NgpOffset",
922
+ "NgpOffsetInput",
923
+ "NgpOffsetOptions",
900
924
  "createOverlay",
901
925
  "injectOverlay",
902
926
  "NgpOverlay",
@@ -913,10 +937,10 @@
913
937
  "BlockScrollStrategy",
914
938
  "NoopScrollStrategy",
915
939
  "ScrollStrategy",
916
- "NgpOffset",
917
- "NgpOffsetInput",
918
- "NgpOffsetOptions",
919
- "coerceOffset"
940
+ "coerceShift",
941
+ "NgpShift",
942
+ "NgpShiftInput",
943
+ "NgpShiftOptions"
920
944
  ],
921
945
  "hasSecondaryEntryPoint": true,
922
946
  "category": "layout",
@@ -1360,7 +1384,8 @@
1360
1384
  "NgpToast",
1361
1385
  "injectToastContext",
1362
1386
  "NgpToastManager",
1363
- "NgpToastRef"
1387
+ "type NgpToastRef",
1388
+ "type NgpToastOptions"
1364
1389
  ],
1365
1390
  "hasSecondaryEntryPoint": true,
1366
1391
  "category": "feedback",
@@ -1386,7 +1411,7 @@
1386
1411
  }
1387
1412
  ],
1388
1413
  "reusableComponent": {
1389
- "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",
1390
1415
  "hasVariants": false,
1391
1416
  "hasSizes": true
1392
1417
  }
@@ -1512,23 +1537,33 @@
1512
1537
  },
1513
1538
  {
1514
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",
1515
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>",
1516
1546
  "description": "Using Text Content as Tooltip"
1517
1547
  },
1518
1548
  {
1519
- "name": "example-3",
1549
+ "name": "example-4",
1520
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>",
1521
1551
  "description": "Conditional Tooltips"
1522
1552
  },
1523
1553
  {
1524
- "name": "example-4",
1554
+ "name": "example-5",
1525
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});",
1526
1556
  "description": "Global Configuration"
1527
1557
  },
1528
1558
  {
1529
- "name": "example-5",
1559
+ "name": "example-6",
1530
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}",
1531
1561
  "description": "NgpTooltipConfig"
1562
+ },
1563
+ {
1564
+ "name": "example-7",
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}",
1566
+ "description": "NgpTooltipConfig"
1532
1567
  }
1533
1568
  ],
1534
1569
  "reusableComponent": {
@@ -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