reshaped 4.2.0-canary.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/bundle.css +1 -1
  2. package/dist/bundle.d.ts +6 -0
  3. package/dist/bundle.js +2 -2
  4. package/dist/components/Accordion/AccordionContent.js +1 -1
  5. package/dist/components/Badge/Badge.js +4 -1
  6. package/dist/components/Badge/Badge.module.css +1 -1
  7. package/dist/components/Badge/tests/Badge.stories.d.ts +4 -0
  8. package/dist/components/Badge/tests/Badge.stories.js +48 -1
  9. package/dist/components/Button/Button.js +2 -0
  10. package/dist/components/Button/Button.module.css +1 -1
  11. package/dist/components/Button/tests/Button.stories.js +1 -0
  12. package/dist/components/ContentToggle/ContentToggle.d.ts +4 -0
  13. package/dist/components/ContentToggle/ContentToggle.js +12 -0
  14. package/dist/components/ContentToggle/ContentToggle.module.css +1 -0
  15. package/dist/components/ContentToggle/ContentToggle.types.d.ts +16 -0
  16. package/dist/components/ContentToggle/index.d.ts +2 -0
  17. package/dist/components/ContentToggle/index.js +1 -0
  18. package/dist/components/ContentToggle/tests/ContentToggle.stories.d.ts +10 -0
  19. package/dist/components/ContentToggle/tests/ContentToggle.stories.js +75 -0
  20. package/dist/components/ContextMenu/ContextMenu.js +5 -4
  21. package/dist/components/ContextMenu/ContextMenu.types.d.ts +5 -1
  22. package/dist/components/ContextMenu/tests/ContextMenu.stories.d.ts +11 -7
  23. package/dist/components/ContextMenu/tests/ContextMenu.stories.js +18 -1
  24. package/dist/components/DropdownMenu/DropdownMenu.context.d.ts +11 -0
  25. package/dist/components/DropdownMenu/DropdownMenu.context.js +19 -0
  26. package/dist/components/DropdownMenu/DropdownMenu.js +6 -4
  27. package/dist/components/DropdownMenu/DropdownMenu.types.d.ts +9 -0
  28. package/dist/components/DropdownMenu/tests/DropdownMenu.stories.d.ts +1 -0
  29. package/dist/components/DropdownMenu/tests/DropdownMenu.stories.js +22 -1
  30. package/dist/components/Expandable/Expandable.d.ts +4 -0
  31. package/dist/components/Expandable/Expandable.js +85 -0
  32. package/dist/components/Expandable/Expandable.module.css +1 -0
  33. package/dist/components/Expandable/Expandable.types.d.ts +18 -0
  34. package/dist/components/Expandable/Expandable.types.js +1 -0
  35. package/dist/components/Expandable/index.d.ts +2 -0
  36. package/dist/components/Expandable/tests/Expandable.stories.d.ts +12 -0
  37. package/dist/components/Expandable/tests/Expandable.stories.js +82 -0
  38. package/dist/components/IconToggle/IconToggle.d.ts +4 -0
  39. package/dist/components/IconToggle/IconToggle.js +13 -0
  40. package/dist/components/IconToggle/IconToggle.module.css +1 -0
  41. package/dist/components/IconToggle/IconToggle.types.d.ts +5 -0
  42. package/dist/components/IconToggle/IconToggle.types.js +1 -0
  43. package/dist/components/IconToggle/index.d.ts +2 -0
  44. package/dist/components/IconToggle/index.js +1 -0
  45. package/dist/components/IconToggle/tests/IconToggle.stories.d.ts +10 -0
  46. package/dist/components/IconToggle/tests/IconToggle.stories.js +24 -0
  47. package/dist/components/Image/Image.js +14 -0
  48. package/dist/components/Link/Link.d.ts +1 -1
  49. package/dist/components/Link/Link.js +2 -2
  50. package/dist/components/Link/Link.types.d.ts +1 -1
  51. package/dist/components/Link/tests/Link.stories.d.ts +2 -1
  52. package/dist/components/Link/tests/Link.stories.js +7 -1
  53. package/dist/components/MenuItem/MenuItem.js +14 -2
  54. package/dist/components/MenuItem/MenuItem.module.css +1 -1
  55. package/dist/components/Select/Select.module.css +1 -1
  56. package/dist/components/Select/SelectStartContent.js +2 -0
  57. package/dist/components/Tabs/Tabs.module.css +1 -1
  58. package/dist/components/Tabs/TabsItem.js +6 -1
  59. package/dist/components/TextField/TextField.js +2 -0
  60. package/dist/components/TextField/TextField.module.css +1 -1
  61. package/dist/components/_private/Presence/Presence.d.ts +7 -0
  62. package/dist/components/_private/Presence/Presence.js +71 -0
  63. package/dist/components/_private/Presence/Presence.module.css +1 -0
  64. package/dist/components/_private/Presence/Presence.types.d.ts +45 -0
  65. package/dist/components/_private/Presence/Presence.types.js +1 -0
  66. package/dist/components/_private/Presence/index.d.ts +1 -0
  67. package/dist/components/_private/Presence/index.js +1 -0
  68. package/dist/hooks/_internal/useSingletonHotkeys.d.ts +2 -2
  69. package/dist/hooks/_internal/useSingletonHotkeys.js +102 -76
  70. package/dist/hooks/_internal/useSingletonKeyboardMode.js +12 -0
  71. package/dist/hooks/useOnClickOutside.js +19 -11
  72. package/dist/index.d.ts +6 -0
  73. package/dist/index.js +3 -0
  74. package/package.json +3 -3
  75. package/dist/components/_private/Expandable/Expandable.d.ts +0 -4
  76. package/dist/components/_private/Expandable/Expandable.js +0 -58
  77. package/dist/components/_private/Expandable/Expandable.module.css +0 -1
  78. package/dist/components/_private/Expandable/Expandable.types.d.ts +0 -7
  79. package/dist/components/_private/Expandable/index.d.ts +0 -1
  80. /package/dist/components/{_private/Expandable/Expandable.types.js → ContentToggle/ContentToggle.types.js} +0 -0
  81. /package/dist/components/{_private/Expandable → Expandable}/index.js +0 -0
@@ -1 +1 @@
1
- .root{--rs-p-h:var(--rs-text-field-p-h);--rs-p-v:0px;--rs-text-field-start-slot-padding:var(--rs-unit-x2);--rs-text-field-end-slot-padding:var(--rs-unit-x2);--rs-text-field-focus-border-color:var(--rs-color-border-primary);display:flex;align-items:center;position:relative;z-index:0;background:var(--rs-color-background-elevation-base);border-radius:var(--rs-text-field-radius);padding:var(--rs-unit-x1) var(--rs-text-field-p-h);min-height:var(--rs-text-field-min-height)}.root.--focused,.root:has(.icon:active),.root:not(:has(button:focus,a:focus,[tabindex="0"]:focus,[role=button]:focus,select:focus)):focus-within{box-shadow:var(--rs-outline-shadow)}.root.--multiline{flex-wrap:wrap}.root.--multiline .input{width:auto}.root.--rounded{--rs-text-field-radius:var(--rs-radius-circular)}.inner{display:inline-flex;flex-grow:1}.input{border:none;outline:none;position:relative;z-index:1;background:none;flex-grow:1;box-sizing:border-box;width:100%;color:var(--rs-color-foreground-neutral);min-height:var(--rs-text-field-min-height);margin:calc(var(--rs-text-field-p-v) * -1) calc(var(--rs-text-field-gap) * -1);padding-inline:var(--rs-text-field-gap);padding-block:0;font-weight:var(--rs-font-weight-regular);font-family:var(--rs-font-family-body)}.input:-webkit-autofill{-webkit-background-clip:text;-webkit-text-fill-color:var(--rs-color-foreground-neutral)}.affix,.input{font-size:var(--rs-text-field-font-size);line-height:var(--rs-text-field-line-height);letter-spacing:var(--rs-text-field-letter-spacing)}.affix,.icon,.slot{max-width:100%;position:relative;z-index:5;display:flex;align-items:center;flex-shrink:0;min-height:calc(var(--rs-text-field-min-height) - var(--rs-unit-x2))}.affix--position-start,.icon--position-start,.slot--position-start{margin-inline-end:var(--rs-text-field-gap)}.affix--position-end,.icon--position-end,.slot--position-end{margin-inline-start:var(--rs-text-field-gap)}.icon{cursor:text}.icon.icon--position-start{margin-inline-start:calc(var(--rs-text-field-icon-align) * -1)}.icon.icon--position-end{margin-inline-end:calc(var(--rs-text-field-icon-align) * -1)}.slot.slot--position-start{--rs-text-field-slot-adjustment:calc(var(--rs-text-field-start-slot-padding) - var(--rs-text-field-p-h));margin-inline-start:var(--rs-text-field-slot-adjustment)}.icon+.slot.slot--position-start{margin-inline-start:0}.slot.slot--position-start:has(+.slot--position-start){margin-inline-end:calc(max(calc(var(--rs-text-field-slot-adjustment) * -1), 0px) + var(--rs-text-field-gap))}.slot.slot--position-end{--rs-text-field-slot-adjustment:calc(var(--rs-text-field-end-slot-padding) - var(--rs-text-field-p-h));margin-inline-end:var(--rs-text-field-slot-adjustment)}.affix{color:var(--rs-color-foreground-neutral-faded)}.affix.affix--position-start{padding-inline-end:calc(var(--rs-text-field-gap) + 1px)}.affix.affix--position-start:after{content:"";position:absolute;inset-block:var(--rs-unit-x1-5);inset-inline-end:0;border-inline-end:1px solid var(--rs-color-border-neutral-faded)}.affix.affix--position-end{padding-inline-start:calc(var(--rs-text-field-gap) + 1px)}.affix.affix--position-end:after{content:"";position:absolute;inset-block:var(--rs-unit-x1-5);inset-inline-start:0;border-inline-start:1px solid var(--rs-color-border-neutral-faded)}.input+.affix--position-end,.input+.icon--position-end,.input+.slot--position-end{margin-inline-start:auto}.root.--disabled{--rs-text-field-border-color:var(--rs-color-border-disabled);background:var(--rs-color-background-disabled-faded)}.root.--disabled,.root.--disabled .input{color:var(--rs-color-foreground-disabled);cursor:not-allowed}.--size-small{--rs-text-field-gap:var(--rs-unit-x1-5);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:calc(var(--rs-unit-x1) + (var(--rs-line-height-body-2) - var(--rs-line-height-caption-1)) / 2);--rs-text-field-p-h:var(--rs-unit-x2);--rs-text-field-icon-align:var(--rs-unit-x0-5);--rs-text-field-font-size:var(--rs-font-size-caption-1);--rs-text-field-line-height:var(--rs-line-height-caption-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-caption-1);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-text-field-p-v) * 2)}.--size-medium{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-p-h:var(--rs-unit-x3);--rs-text-field-icon-align:var(--rs-unit-x0-5);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x2) * 2)}.--size-large{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-p-h:var(--rs-unit-x4);--rs-text-field-icon-align:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-1);--rs-text-field-line-height:var(--rs-line-height-body-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-1);--rs-text-field-action-inset:var(--rs-unit-x2);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x3) * 2)}.--size-xlarge{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-p-h:var(--rs-unit-x5);--rs-text-field-icon-align:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-1);--rs-text-field-line-height:var(--rs-line-height-body-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-1);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) * 2)}.root.--variant-faded{background:var(--rs-color-background-neutral-faded)}.root.--variant-ghost{background:transparent}.root.--variant-headless{--rs-text-field-focus-border-color:transparent;background:transparent;box-shadow:none!important}.root.--status-error:not(.--variant-headless){box-shadow:0 0 0 var(--rs-outline-width) var(--rs-color-border-critical) inset}@media (--rs-viewport-s ) and (hover:none){.input{font-size:var(--rs-font-size-body-1)!important}}@media (--rs-viewport-m ){.--size-small--m{--rs-text-field-gap:var(--rs-unit-x1-5);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:calc(var(--rs-unit-x1) + (var(--rs-line-height-body-2) - var(--rs-line-height-caption-1)) / 2);--rs-text-field-p-h:var(--rs-unit-x2);--rs-text-field-icon-align:var(--rs-unit-x0-5);--rs-text-field-font-size:var(--rs-font-size-caption-1);--rs-text-field-line-height:var(--rs-line-height-caption-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-caption-1);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-text-field-p-v) * 2)}.--size-medium--m{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-p-h:var(--rs-unit-x3);--rs-text-field-icon-align:var(--rs-unit-x0-5);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x2) * 2)}.--size-large--m{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-p-h:var(--rs-unit-x4);--rs-text-field-icon-align:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-1);--rs-text-field-line-height:var(--rs-line-height-body-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-1);--rs-text-field-action-inset:var(--rs-unit-x2);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x3) * 2)}.--size-xlarge--m{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-p-h:var(--rs-unit-x5);--rs-text-field-icon-align:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-1);--rs-text-field-line-height:var(--rs-line-height-body-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-1);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) * 2)}}@media (--rs-viewport-l ){.--size-small--l{--rs-text-field-gap:var(--rs-unit-x1-5);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:calc(var(--rs-unit-x1) + (var(--rs-line-height-body-2) - var(--rs-line-height-caption-1)) / 2);--rs-text-field-p-h:var(--rs-unit-x2);--rs-text-field-icon-align:var(--rs-unit-x0-5);--rs-text-field-font-size:var(--rs-font-size-caption-1);--rs-text-field-line-height:var(--rs-line-height-caption-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-caption-1);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-text-field-p-v) * 2)}.--size-medium--l{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-p-h:var(--rs-unit-x3);--rs-text-field-icon-align:var(--rs-unit-x0-5);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x2) * 2)}.--size-large--l{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-p-h:var(--rs-unit-x4);--rs-text-field-icon-align:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-1);--rs-text-field-line-height:var(--rs-line-height-body-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-1);--rs-text-field-action-inset:var(--rs-unit-x2);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x3) * 2)}.--size-xlarge--l{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-p-h:var(--rs-unit-x5);--rs-text-field-icon-align:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-1);--rs-text-field-line-height:var(--rs-line-height-body-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-1);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) * 2)}}@media (--rs-viewport-xl ){.--size-small--xl{--rs-text-field-gap:var(--rs-unit-x1-5);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:calc(var(--rs-unit-x1) + (var(--rs-line-height-body-2) - var(--rs-line-height-caption-1)) / 2);--rs-text-field-p-h:var(--rs-unit-x2);--rs-text-field-icon-align:var(--rs-unit-x0-5);--rs-text-field-font-size:var(--rs-font-size-caption-1);--rs-text-field-line-height:var(--rs-line-height-caption-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-caption-1);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-text-field-p-v) * 2)}.--size-medium--xl{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-p-h:var(--rs-unit-x3);--rs-text-field-icon-align:var(--rs-unit-x0-5);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x2) * 2)}.--size-large--xl{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-p-h:var(--rs-unit-x4);--rs-text-field-icon-align:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-1);--rs-text-field-line-height:var(--rs-line-height-body-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-1);--rs-text-field-action-inset:var(--rs-unit-x2);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x3) * 2)}.--size-xlarge--xl{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-p-h:var(--rs-unit-x5);--rs-text-field-icon-align:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-1);--rs-text-field-line-height:var(--rs-line-height-body-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-1);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) * 2)}}
1
+ .root{--rs-p-h:var(--rs-text-field-p-h);--rs-p-v:0px;--rs-text-field-start-slot-padding:var(--rs-unit-x2);--rs-text-field-end-slot-padding:var(--rs-unit-x2);--rs-text-field-focus-border-color:var(--rs-color-border-primary);display:flex;align-items:center;position:relative;z-index:0;background:var(--rs-color-background-elevation-base);border-radius:var(--rs-text-field-radius);padding:var(--rs-unit-x1) var(--rs-text-field-p-h);min-height:var(--rs-text-field-min-height)}.root.--focused,.root:has(.icon:active),.root:not(:has(button:focus,a:focus,[tabindex="0"]:focus,[role=button]:focus,select:focus)):focus-within{box-shadow:var(--rs-outline-shadow)}.root.--multiline{flex-wrap:wrap}.root.--multiline .input{width:auto}.root.--rounded{--rs-text-field-radius:var(--rs-radius-circular)}.inner{display:inline-flex;flex-grow:1}.input{border:none;outline:none;position:relative;z-index:1;background:none;flex-grow:1;box-sizing:border-box;width:100%;color:var(--rs-color-foreground-neutral);min-height:var(--rs-text-field-min-height);margin:calc(var(--rs-text-field-p-v) * -1) calc(var(--rs-text-field-gap) * -1);padding-inline:var(--rs-text-field-gap);padding-block:0;font-weight:var(--rs-font-weight-regular);font-family:var(--rs-font-family-body)}.input:-webkit-autofill{-webkit-background-clip:text;-webkit-text-fill-color:var(--rs-color-foreground-neutral)}.affix,.input{font-size:var(--rs-text-field-font-size);line-height:var(--rs-text-field-line-height);letter-spacing:var(--rs-text-field-letter-spacing)}.affix,.icon,.slot{max-width:100%;position:relative;z-index:5;display:flex;align-items:center;flex-shrink:0;min-height:calc(var(--rs-text-field-min-height) - var(--rs-unit-x2))}.affix--position-start,.icon--position-start,.slot--position-start{margin-inline-end:var(--rs-text-field-gap)}.affix--position-end,.icon--position-end,.slot--position-end{margin-inline-start:var(--rs-text-field-gap)}.icon{cursor:text}.icon.icon--position-start{margin-inline-start:calc(var(--rs-text-field-icon-align) * -1)}.icon.icon--position-end{margin-inline-end:calc(var(--rs-text-field-icon-align) * -1)}.slot.slot--position-start{--rs-text-field-slot-adjustment:calc(var(--rs-text-field-start-slot-padding) - var(--rs-text-field-p-h));margin-inline-start:var(--rs-text-field-slot-adjustment)}.icon+.slot.slot--position-start{margin-inline-start:0}.slot.slot--position-start:has(+.slot--position-start){margin-inline-end:calc(max(calc(var(--rs-text-field-slot-adjustment) * -1), 0px) + var(--rs-text-field-gap))}.slot.slot--position-end{--rs-text-field-slot-adjustment:calc(var(--rs-text-field-end-slot-padding) - var(--rs-text-field-p-h));margin-inline-end:var(--rs-text-field-slot-adjustment)}.affix{color:var(--rs-color-foreground-neutral-faded)}.affix.affix--position-start{padding-inline-end:calc(var(--rs-text-field-gap) + 1px)}.affix.affix--position-start:after{content:"";position:absolute;inset-block:var(--rs-unit-x1-5);inset-inline-end:0;border-inline-end:1px solid var(--rs-color-border-neutral-faded)}.affix.affix--position-end{padding-inline-start:calc(var(--rs-text-field-gap) + 1px)}.affix.affix--position-end:after{content:"";position:absolute;inset-block:var(--rs-unit-x1-5);inset-inline-start:0;border-inline-start:1px solid var(--rs-color-border-neutral-faded)}.input+.affix--position-end,.input+.icon--position-end,.input+.slot--position-end{margin-inline-start:auto}.root.--disabled{--rs-text-field-border-color:var(--rs-color-border-disabled);background:var(--rs-color-background-disabled-faded)}.root.--disabled,.root.--disabled .input{color:var(--rs-color-foreground-disabled);cursor:not-allowed}.--size-small{--rs-text-field-gap:var(--rs-unit-x1);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:calc(var(--rs-unit-x1) + (var(--rs-line-height-body-2) - var(--rs-line-height-caption-1)) / 2);--rs-text-field-p-h:var(--rs-unit-x2);--rs-text-field-icon-align:var(--rs-unit-x0-5);--rs-text-field-font-size:var(--rs-font-size-caption-1);--rs-text-field-line-height:var(--rs-line-height-caption-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-caption-1);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-text-field-p-v) * 2)}.--size-medium{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-p-h:var(--rs-unit-x3);--rs-text-field-icon-align:var(--rs-unit-x0-5);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x2) * 2)}.--size-large{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-p-h:var(--rs-unit-x4);--rs-text-field-icon-align:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-1);--rs-text-field-line-height:var(--rs-line-height-body-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-1);--rs-text-field-action-inset:var(--rs-unit-x2);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x3) * 2)}.--size-xlarge{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-p-h:var(--rs-unit-x5);--rs-text-field-icon-align:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-1);--rs-text-field-line-height:var(--rs-line-height-body-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-1);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) * 2)}.root.--variant-faded{background:var(--rs-color-background-neutral-faded)}.root.--variant-ghost{background:transparent}.root.--variant-headless{--rs-text-field-focus-border-color:transparent;background:transparent;box-shadow:none!important}.root.--status-error:not(.--variant-headless){box-shadow:0 0 0 var(--rs-outline-width) var(--rs-color-border-critical) inset}@media (--rs-viewport-s ) and (hover:none){.input{font-size:var(--rs-font-size-body-1)!important}}@media (--rs-viewport-m ){.--size-small--m{--rs-text-field-gap:var(--rs-unit-x1);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:calc(var(--rs-unit-x1) + (var(--rs-line-height-body-2) - var(--rs-line-height-caption-1)) / 2);--rs-text-field-p-h:var(--rs-unit-x2);--rs-text-field-icon-align:var(--rs-unit-x0-5);--rs-text-field-font-size:var(--rs-font-size-caption-1);--rs-text-field-line-height:var(--rs-line-height-caption-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-caption-1);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-text-field-p-v) * 2)}.--size-medium--m{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-p-h:var(--rs-unit-x3);--rs-text-field-icon-align:var(--rs-unit-x0-5);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x2) * 2)}.--size-large--m{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-p-h:var(--rs-unit-x4);--rs-text-field-icon-align:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-1);--rs-text-field-line-height:var(--rs-line-height-body-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-1);--rs-text-field-action-inset:var(--rs-unit-x2);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x3) * 2)}.--size-xlarge--m{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-p-h:var(--rs-unit-x5);--rs-text-field-icon-align:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-1);--rs-text-field-line-height:var(--rs-line-height-body-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-1);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) * 2)}}@media (--rs-viewport-l ){.--size-small--l{--rs-text-field-gap:var(--rs-unit-x1);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:calc(var(--rs-unit-x1) + (var(--rs-line-height-body-2) - var(--rs-line-height-caption-1)) / 2);--rs-text-field-p-h:var(--rs-unit-x2);--rs-text-field-icon-align:var(--rs-unit-x0-5);--rs-text-field-font-size:var(--rs-font-size-caption-1);--rs-text-field-line-height:var(--rs-line-height-caption-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-caption-1);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-text-field-p-v) * 2)}.--size-medium--l{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-p-h:var(--rs-unit-x3);--rs-text-field-icon-align:var(--rs-unit-x0-5);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x2) * 2)}.--size-large--l{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-p-h:var(--rs-unit-x4);--rs-text-field-icon-align:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-1);--rs-text-field-line-height:var(--rs-line-height-body-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-1);--rs-text-field-action-inset:var(--rs-unit-x2);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x3) * 2)}.--size-xlarge--l{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-p-h:var(--rs-unit-x5);--rs-text-field-icon-align:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-1);--rs-text-field-line-height:var(--rs-line-height-body-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-1);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) * 2)}}@media (--rs-viewport-xl ){.--size-small--xl{--rs-text-field-gap:var(--rs-unit-x1);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:calc(var(--rs-unit-x1) + (var(--rs-line-height-body-2) - var(--rs-line-height-caption-1)) / 2);--rs-text-field-p-h:var(--rs-unit-x2);--rs-text-field-icon-align:var(--rs-unit-x0-5);--rs-text-field-font-size:var(--rs-font-size-caption-1);--rs-text-field-line-height:var(--rs-line-height-caption-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-caption-1);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-text-field-p-v) * 2)}.--size-medium--xl{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-p-h:var(--rs-unit-x3);--rs-text-field-icon-align:var(--rs-unit-x0-5);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x2) * 2)}.--size-large--xl{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-p-h:var(--rs-unit-x4);--rs-text-field-icon-align:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-1);--rs-text-field-line-height:var(--rs-line-height-body-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-1);--rs-text-field-action-inset:var(--rs-unit-x2);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x3) * 2)}.--size-xlarge--xl{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-p-h:var(--rs-unit-x5);--rs-text-field-icon-align:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-1);--rs-text-field-line-height:var(--rs-line-height-body-1);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-1);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) * 2)}}
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import type * as T from "./Presence.types";
3
+ declare function Presence<As extends keyof React.JSX.IntrinsicElements = "span">(props: T.Props<As>): React.JSX.Element;
4
+ declare namespace Presence {
5
+ var displayName: string;
6
+ }
7
+ export default Presence;
@@ -0,0 +1,71 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import { classNames } from "@reshaped/utilities";
5
+ import useIsomorphicLayoutEffect from "../../../hooks/useIsomorphicLayoutEffect";
6
+ import s from "./Presence.module.css";
7
+ const PresenceItem = (props) => {
8
+ const { as, entry, status, itemClassName, enterClassName, exitClassName, onExited } = props;
9
+ // Rendering a union of every intrinsic tag name blows up the JSX prop type
10
+ const TagName = as;
11
+ const elRef = React.useRef(null);
12
+ const itemClassNames = classNames(s.item, status && s[`--${status}`], itemClassName, status === "enter" && enterClassName, status === "exit" && exitClassName);
13
+ const handleAnimationEnd = (e) => {
14
+ if (status !== "exit" || e.target !== elRef.current)
15
+ return;
16
+ onExited(entry.id);
17
+ };
18
+ // Exiting items are removed when their animation ends, so the ones that never
19
+ // start it have to be released here. Reduced motion keeps the animation and only
20
+ // zeroes its duration, which makes it end on the next frame instead
21
+ useIsomorphicLayoutEffect(() => {
22
+ const el = elRef.current;
23
+ if (status !== "exit" || !el)
24
+ return;
25
+ if (el.getAnimations().length > 0)
26
+ return;
27
+ onExited(entry.id);
28
+ }, [status, entry.id, onExited]);
29
+ return (_jsx(TagName, { ref: elRef, className: itemClassNames, "aria-hidden": status === "exit" || undefined, onAnimationEnd: handleAnimationEnd, children: entry.children }));
30
+ };
31
+ const Presence = (props) => {
32
+ const { children, itemKey, as: TagName = "span", className, itemClassName, enterClassName, exitClassName, attributes, } = props;
33
+ const idRef = React.useRef(0);
34
+ // Holds the children of the last committed render, which become the exiting ones on a key change
35
+ const childrenRef = React.useRef(children);
36
+ const [state, setState] = React.useState(() => ({ id: 0, key: itemKey, exiting: [] }));
37
+ // Rendering both children at the same time has to happen in the same pass as the key change,
38
+ // otherwise the previous children get unmounted before we can animate them out
39
+ if (state.key !== itemKey) {
40
+ idRef.current += 1;
41
+ setState({
42
+ id: idRef.current,
43
+ key: itemKey,
44
+ status: "enter",
45
+ exiting: [
46
+ // An item swapped back in before it finished exiting is replaced by the new one
47
+ ...state.exiting.filter((entry) => entry.key !== itemKey),
48
+ { id: state.id, key: state.key, children: childrenRef.current },
49
+ ],
50
+ });
51
+ }
52
+ const handleExited = React.useCallback((id) => {
53
+ setState((prevState) => ({
54
+ ...prevState,
55
+ exiting: prevState.exiting.filter((entry) => entry.id !== id),
56
+ }));
57
+ }, []);
58
+ useIsomorphicLayoutEffect(() => {
59
+ childrenRef.current = children;
60
+ });
61
+ const itemProps = {
62
+ as: TagName,
63
+ itemClassName,
64
+ enterClassName,
65
+ exitClassName,
66
+ onExited: handleExited,
67
+ };
68
+ return (_jsxs(TagName, { ...attributes, className: classNames(s.root, className), children: [state.exiting.map((entry) => (_jsx(PresenceItem, { entry: entry, status: "exit", ...itemProps }, entry.id))), _jsx(PresenceItem, { entry: { id: state.id, key: itemKey, children }, status: state.status, ...itemProps }, state.id)] }));
69
+ };
70
+ Presence.displayName = "Presence";
71
+ export default Presence;
@@ -0,0 +1 @@
1
+ .root{display:grid;grid-template-areas:"presence"}.item{grid-area:presence;min-width:0}.--exit{pointer-events:none}
@@ -0,0 +1,45 @@
1
+ import type React from "react";
2
+ import type { ClassName } from "@reshaped/utilities";
3
+ import type { Attributes } from "../../../types/global";
4
+ export type Status = "enter" | "exit";
5
+ export type Entry = {
6
+ id: number;
7
+ key: unknown;
8
+ children?: React.ReactNode;
9
+ };
10
+ export type State = {
11
+ /** Id of the currently rendered entry */
12
+ id: number;
13
+ /** Key the currently rendered entry was rendered with */
14
+ key: unknown;
15
+ /** Undefined until the first swap happens to avoid animating on mount */
16
+ status?: Status;
17
+ /** Entries kept mounted until their exit animation ends */
18
+ exiting: Entry[];
19
+ };
20
+ export type ItemProps = Pick<Props, "itemClassName" | "enterClassName" | "exitClassName"> & {
21
+ as: keyof React.JSX.IntrinsicElements;
22
+ entry: Entry;
23
+ status?: Status;
24
+ onExited: (id: number) => void;
25
+ };
26
+ export type Props<TagName extends keyof React.JSX.IntrinsicElements | void = void> = {
27
+ children?: React.ReactNode;
28
+ /**
29
+ * Identity of the current children, compared by reference.
30
+ * Changing it renders the previous and the next children at the same time
31
+ */
32
+ itemKey: unknown;
33
+ /** Render the root and every item as a different element */
34
+ as?: TagName extends keyof React.JSX.IntrinsicElements ? TagName : keyof React.JSX.IntrinsicElements;
35
+ /** Additional classname for the root element */
36
+ className?: ClassName;
37
+ /** Classname of every rendered item */
38
+ itemClassName?: ClassName;
39
+ /** Classname of the item animating in, added on every change except the first render */
40
+ enterClassName?: ClassName;
41
+ /** Classname of the item animating out, the item is removed when its animation ends */
42
+ exitClassName?: ClassName;
43
+ /** Additional attributes for the root element */
44
+ attributes?: Attributes<TagName>;
45
+ };
@@ -0,0 +1 @@
1
+ export { default } from "./Presence";
@@ -0,0 +1 @@
1
+ export { default } from "./Presence";
@@ -20,8 +20,8 @@ type HotkeyData = {
20
20
  export declare class HotkeyStore {
21
21
  hotkeyMap: Record<string, Set<HotkeyData>>;
22
22
  getSize: () => number;
23
- bindHotkeys: (hotkeys: Hotkeys, ref: React.RefObject<HTMLElement | null>, options: HotkeyOptions) => void;
24
- unbindHotkeys: (hotkeys: Hotkeys) => void;
23
+ hasHandlers: (pressedId: string) => boolean;
24
+ bindHotkeys: (hotkeys: Hotkeys, ref: React.RefObject<HTMLElement | null>, options: HotkeyOptions) => () => void;
25
25
  handleKeyDown: (pressedMap: PressedMap, e: KeyboardEvent) => void;
26
26
  }
27
27
  export declare const SingletonHotkeysProvider: React.FC<{
@@ -1,11 +1,11 @@
1
1
  "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
3
3
  import React from "react";
4
4
  /**
5
5
  * Utilities
6
6
  */
7
7
  const COMBINATION_DELIMETER = "+";
8
- let modifiedKeys = [];
8
+ const MODIFIER_KEYS = ["meta", "control", "alt", "shift"];
9
9
  const formatHotkey = (hotkey) => {
10
10
  if (hotkey === " ")
11
11
  return hotkey;
@@ -19,11 +19,26 @@ const getEventKey = (e) => {
19
19
  if (!e.key)
20
20
  return;
21
21
  // Having alt pressed modifies e.key value, so relying on e.code for it
22
- if (e.altKey && /^[Key|Digit|Numpad]/.test(e.code)) {
23
- return e.code.toLowerCase().replace(/key|digit|numpad/, "");
22
+ if (e.altKey && /^(Key|Digit|Numpad)/.test(e.code)) {
23
+ return e.code.toLowerCase().replace(/^(key|digit|numpad)/, "");
24
24
  }
25
25
  return e.key.toLowerCase();
26
26
  };
27
+ /**
28
+ * Support for `mod` that represents both Mac and Win keyboards
29
+ * We create the hotkeyId again to sort the mod key correctly
30
+ */
31
+ const getPressedIds = (pressedId) => {
32
+ const pressedFormattedKeys = pressedId.split(COMBINATION_DELIMETER);
33
+ const ids = [pressedId];
34
+ if (pressedFormattedKeys.includes("control")) {
35
+ ids.push(getHotkeyId(pressedId.replace("control", "mod")));
36
+ }
37
+ if (pressedFormattedKeys.includes("meta")) {
38
+ ids.push(getHotkeyId(pressedId.replace("meta", "mod")));
39
+ }
40
+ return ids;
41
+ };
27
42
  // Removing the unknown gets highlighted an invalid syntax
28
43
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint
29
44
  const walkHotkeys = (hotkeys, cb) => {
@@ -39,63 +54,50 @@ const walkHotkeys = (hotkeys, cb) => {
39
54
  export class HotkeyStore {
40
55
  hotkeyMap = {};
41
56
  getSize = () => Object.keys(this.hotkeyMap).length;
57
+ hasHandlers = (pressedId) => {
58
+ return getPressedIds(pressedId).some((id) => this.hotkeyMap[id]?.size);
59
+ };
42
60
  bindHotkeys = (hotkeys, ref, options) => {
43
- walkHotkeys(hotkeys, (id, hotkeyData) => {
44
- if (!hotkeyData)
61
+ const boundData = [];
62
+ walkHotkeys(hotkeys, (id, callback) => {
63
+ if (!callback)
45
64
  return;
65
+ const data = { callback, ref, options };
46
66
  if (!this.hotkeyMap[id]) {
47
67
  this.hotkeyMap[id] = new Set();
48
68
  }
49
- this.hotkeyMap[id].add({ callback: hotkeyData, ref, options });
69
+ this.hotkeyMap[id].add(data);
70
+ boundData.push({ id, data });
50
71
  });
51
- };
52
- unbindHotkeys = (hotkeys) => {
53
- walkHotkeys(hotkeys, (id, hotkeyCallback) => {
54
- if (!hotkeyCallback)
55
- return;
56
- this.hotkeyMap[id]?.forEach((data) => {
57
- if (data.callback === hotkeyCallback) {
58
- this.hotkeyMap[id].delete(data);
72
+ return () => {
73
+ boundData.forEach(({ id, data }) => {
74
+ this.hotkeyMap[id]?.delete(data);
75
+ if (!this.hotkeyMap[id]?.size) {
76
+ delete this.hotkeyMap[id];
59
77
  }
60
78
  });
61
- if (!this.hotkeyMap[id]?.size) {
62
- delete this.hotkeyMap[id];
63
- }
64
- });
79
+ };
65
80
  };
66
81
  handleKeyDown = (pressedMap, e) => {
67
82
  const pressedKeys = Object.keys(pressedMap);
68
83
  if (!pressedKeys.length)
69
84
  return;
70
85
  const pressedId = getHotkeyId(pressedKeys.join(COMBINATION_DELIMETER));
71
- const pressedFormattedKeys = pressedId.split(COMBINATION_DELIMETER);
72
- const hotkeyData = this.hotkeyMap[pressedId];
73
- /**
74
- * Support for `mod` that represents both Mac and Win keyboards
75
- * We create the hotkeyId again to sort the mod key correctly
76
- */
77
- const controlToModPressedId = getHotkeyId(pressedId.replace("control", "mod"));
78
- const metaToModPressedId = getHotkeyId(pressedId.replace("meta", "mod"));
79
- const hotkeyControlModData = pressedFormattedKeys.includes("control") && this.hotkeyMap[controlToModPressedId];
80
- const hotkeyMetaModData = pressedFormattedKeys.includes("meta") && this.hotkeyMap[metaToModPressedId];
81
- [hotkeyData, hotkeyControlModData, hotkeyMetaModData].forEach((hotkeyData) => {
82
- if (!hotkeyData)
86
+ const eventTarget = e.composedPath()[0];
87
+ getPressedIds(pressedId).forEach((id) => {
88
+ const hotkeyData = this.hotkeyMap[id];
89
+ if (!hotkeyData?.size)
83
90
  return;
84
- if (hotkeyData?.size) {
85
- hotkeyData.forEach((data) => {
86
- const eventTarget = e.composedPath()[0];
87
- if (data.ref.current &&
88
- !(eventTarget === data.ref.current || data.ref.current.contains(eventTarget))) {
89
- return;
90
- }
91
- const resolvedEvent = pressedMap[pressedId];
92
- if (data.options.preventDefault) {
93
- resolvedEvent?.preventDefault();
94
- e.preventDefault();
95
- }
96
- data.callback(e);
97
- });
98
- }
91
+ hotkeyData.forEach((data) => {
92
+ if (data.ref.current &&
93
+ !(eventTarget === data.ref.current || data.ref.current.contains(eventTarget))) {
94
+ return;
95
+ }
96
+ if (data.options.preventDefault) {
97
+ e.preventDefault();
98
+ }
99
+ data.callback(e);
100
+ });
99
101
  });
100
102
  };
101
103
  }
@@ -104,61 +106,76 @@ const globalHotkeyStore = new HotkeyStore();
104
106
  * Components / Hooks
105
107
  */
106
108
  const HotkeyContext = React.createContext({});
107
- export const SingletonHotkeysProvider = (props) => {
109
+ const HotkeysProvider = (props) => {
108
110
  const { children } = props;
109
- const [pressedMap, setPressedMap] = React.useState({});
111
+ // Ref is the source of truth to keep the map in sync with the native events,
112
+ // state is mirroring it to re-render the consumers relying on isPressed
113
+ const pressedMapRef = React.useRef({});
114
+ const [, setPressedState] = React.useState({});
115
+ // Keyup events don't trigger for regular keys while Meta is pressed on macOS,
116
+ // so we track the keys pressed during that time to release them manually
117
+ const metaModifiedKeysRef = React.useRef([]);
110
118
  const hooksCountRef = React.useRef(0);
119
+ const setPressedMap = React.useCallback((nextPressedMap) => {
120
+ pressedMapRef.current = nextPressedMap;
121
+ setPressedState(nextPressedMap);
122
+ }, []);
111
123
  const addPressedKey = React.useCallback((e) => {
112
124
  if (e.repeat || hooksCountRef.current === 0)
113
125
  return;
114
126
  const eventKey = getEventKey(e);
115
127
  if (!eventKey)
116
128
  return;
117
- const nextPressedMap = { ...pressedMap };
129
+ const nextPressedMap = { ...pressedMapRef.current };
118
130
  nextPressedMap[eventKey] = e;
119
- // Key up won't trigger for other keys while Meta is pressed so we need to cache them
120
- // and remove on Meta keyup
121
- if (e.metaKey)
122
- modifiedKeys.push(...Object.keys(nextPressedMap));
123
- if (nextPressedMap["meta"])
124
- modifiedKeys.push(eventKey);
131
+ if (nextPressedMap["meta"]) {
132
+ if (!MODIFIER_KEYS.includes(eventKey)) {
133
+ // Keys pressed while Meta was held might have been released already
134
+ // without us receiving their keyup events, so when the whole pressed combination
135
+ // doesn't match any hotkey - we treat them as released
136
+ const staleKeys = metaModifiedKeysRef.current.filter((key) => key !== eventKey && key in nextPressedMap);
137
+ const pressedId = getHotkeyId(Object.keys(nextPressedMap).join(COMBINATION_DELIMETER));
138
+ if (staleKeys.length && !globalHotkeyStore.hasHandlers(pressedId)) {
139
+ staleKeys.forEach((key) => delete nextPressedMap[key]);
140
+ }
141
+ }
142
+ metaModifiedKeysRef.current = Object.keys(nextPressedMap).filter((key) => !MODIFIER_KEYS.includes(key));
143
+ }
125
144
  setPressedMap(nextPressedMap);
126
145
  return nextPressedMap;
127
- }, [pressedMap]);
146
+ }, [setPressedMap]);
128
147
  const removePressedKey = React.useCallback((e) => {
129
148
  if (hooksCountRef.current === 0)
130
149
  return;
131
150
  const eventKey = getEventKey(e);
132
151
  if (!eventKey)
133
152
  return;
134
- const nextPressedMap = { ...pressedMap };
153
+ const nextPressedMap = { ...pressedMapRef.current };
135
154
  delete nextPressedMap[eventKey];
136
- if (eventKey === "meta" || eventKey === "control") {
137
- delete nextPressedMap["mod"];
138
- }
139
155
  if (eventKey === "meta") {
140
- modifiedKeys.forEach((key) => {
141
- if (!nextPressedMap[key])
142
- return;
156
+ metaModifiedKeysRef.current.forEach((key) => {
143
157
  delete nextPressedMap[key];
144
158
  });
145
- modifiedKeys = [];
159
+ metaModifiedKeysRef.current = [];
146
160
  }
147
161
  setPressedMap(nextPressedMap);
148
- }, [pressedMap]);
149
- const isPressed = (hotkey) => {
162
+ }, [setPressedMap]);
163
+ const isPressed = React.useCallback((hotkey) => {
164
+ const pressedMap = pressedMapRef.current;
150
165
  const keys = formatHotkey(hotkey).split(COMBINATION_DELIMETER);
151
- if (keys.some((key) => !pressedMap[key]))
152
- return false;
153
- return true;
154
- };
166
+ return keys.every((key) => {
167
+ if (key === "mod")
168
+ return Boolean(pressedMap["meta"] || pressedMap["control"]);
169
+ return Boolean(pressedMap[key]);
170
+ });
171
+ }, []);
155
172
  const handleWindowKeyDown = React.useCallback((e) => {
156
173
  // Browsers trigger keyboard event without passing e.key when you click on autocomplete
157
174
  if (!e.key)
158
175
  return;
159
- const nextPressedMap = addPressedKey(e) ?? pressedMap;
176
+ const nextPressedMap = addPressedKey(e) ?? pressedMapRef.current;
160
177
  globalHotkeyStore.handleKeyDown(nextPressedMap, e);
161
- }, [addPressedKey, pressedMap]);
178
+ }, [addPressedKey]);
162
179
  const handleWindowKeyUp = React.useCallback((e) => {
163
180
  if (!e.key)
164
181
  return;
@@ -166,14 +183,14 @@ export const SingletonHotkeysProvider = (props) => {
166
183
  }, [removePressedKey]);
167
184
  const handleWindowBlur = React.useCallback(() => {
168
185
  setPressedMap({});
169
- modifiedKeys = [];
170
- }, []);
186
+ metaModifiedKeysRef.current = [];
187
+ }, [setPressedMap]);
171
188
  const addHotkeys = React.useCallback((hotkeys, ref, options = {}) => {
172
189
  hooksCountRef.current += 1;
173
- globalHotkeyStore.bindHotkeys(hotkeys, ref, options);
190
+ const unbindHotkeys = globalHotkeyStore.bindHotkeys(hotkeys, ref, options);
174
191
  return () => {
175
192
  hooksCountRef.current -= 1;
176
- globalHotkeyStore.unbindHotkeys(hotkeys);
193
+ unbindHotkeys();
177
194
  };
178
195
  }, []);
179
196
  React.useEffect(() => {
@@ -188,4 +205,13 @@ export const SingletonHotkeysProvider = (props) => {
188
205
  }, [handleWindowKeyDown, handleWindowKeyUp, handleWindowBlur]);
189
206
  return (_jsx(HotkeyContext.Provider, { value: { addHotkeys, isPressed }, children: children }));
190
207
  };
208
+ export const SingletonHotkeysProvider = (props) => {
209
+ const { children } = props;
210
+ // Hotkeys are handled globally, so nested providers rely on the root provider
211
+ // instead of attaching their own window event listeners
212
+ const hasParentProvider = Boolean(React.useContext(HotkeyContext).addHotkeys);
213
+ if (hasParentProvider)
214
+ return _jsx(_Fragment, { children: children });
215
+ return _jsx(HotkeysProvider, { children: children });
216
+ };
191
217
  export const useSingletonHotkeys = () => React.useContext(HotkeyContext);
@@ -3,6 +3,16 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import React from "react";
4
4
  import { activateKeyboardMode, deactivateKeyboardMode } from "@reshaped/utilities/internal";
5
5
  const ESC = "Escape";
6
+ const isEditing = (e) => {
7
+ const el = e.target;
8
+ if (!el?.tagName)
9
+ return false;
10
+ const tagName = el.tagName?.toLowerCase();
11
+ const editable = el.isContentEditable || tagName === "textarea" || tagName === "input";
12
+ if (!editable)
13
+ return false;
14
+ return e.isComposing || e.key.length === 1;
15
+ };
6
16
  const SingletonKeyboardModeContext = React.createContext({
7
17
  disabledRef: null,
8
18
  disable: () => { },
@@ -34,6 +44,8 @@ export const SingletonKeyboardModeProvider = (props) => {
34
44
  // Prevent focus ring from appearing when using mouse but closing with esc
35
45
  if (e.key === ESC)
36
46
  return;
47
+ if (isEditing(e))
48
+ return;
37
49
  activate();
38
50
  }, [activate]);
39
51
  const handleClick = React.useCallback(() => {
@@ -10,21 +10,19 @@ const useOnClickOutside = (refs, handler, options) => {
10
10
  * before we checked if it's inside the refs
11
11
  */
12
12
  const isMouseDownInsideRef = React.useRef(false);
13
+ const checkEventInsideRefs = (event) => {
14
+ const eventEl = event.composedPath()[0];
15
+ if (!eventEl)
16
+ return false;
17
+ return refs.some((elRef) => !!elRef.current && (elRef.current === eventEl || elRef.current.contains(eventEl)));
18
+ };
13
19
  React.useEffect(() => {
14
20
  /**
15
21
  * Not checking for disabled here since some components can enable the hook
16
22
  * after it was clicked
17
23
  */
18
24
  const handleMouseDown = (event) => {
19
- isMouseDownInsideRef.current = false;
20
- const clickedEl = event.composedPath()[0];
21
- refs.forEach((elRef) => {
22
- if (!elRef.current)
23
- return;
24
- if (elRef.current === clickedEl || elRef.current.contains(clickedEl)) {
25
- isMouseDownInsideRef.current = true;
26
- }
27
- });
25
+ isMouseDownInsideRef.current = checkEventInsideRefs(event);
28
26
  };
29
27
  const handleKeyDown = (event) => {
30
28
  if (![keys.ENTER, keys.SPACE].includes(event.key))
@@ -47,15 +45,25 @@ const useOnClickOutside = (refs, handler, options) => {
47
45
  if (disabled)
48
46
  return;
49
47
  const handleClick = (event) => {
50
- if ("button" in event && event.button === 2)
51
- return;
48
+ if (event.type === "contextmenu") {
49
+ /**
50
+ * Context menu events can be claimed by a handler that opens a menu of its own,
51
+ * which prevents the default browser behavior. Such events open a new flyout
52
+ * instead of dismissing the current one, so they're not outside clicks
53
+ */
54
+ if (event.defaultPrevented)
55
+ return;
56
+ isMouseDownInsideRef.current = checkEventInsideRefs(event);
57
+ }
52
58
  if (isMouseDownInsideRef.current)
53
59
  return;
54
60
  handlerRef.current?.(event);
55
61
  };
56
62
  document.addEventListener("click", handleClick);
63
+ document.addEventListener("contextmenu", handleClick);
57
64
  return () => {
58
65
  document.removeEventListener("click", handleClick);
66
+ document.removeEventListener("contextmenu", handleClick);
59
67
  };
60
68
  // oxlint-disable-next-line react-hooks/exhaustive-deps
61
69
  }, [handlerRef, disabled, ...refs]);
package/dist/index.d.ts CHANGED
@@ -90,8 +90,12 @@ export type { ActionableProps, ActionableRef } from "./components/Actionable";
90
90
  export { default as Actionable } from "./components/Actionable";
91
91
  export type { ContainerProps } from "./components/Container";
92
92
  export { default as Container } from "./components/Container";
93
+ export type { ContentToggleProps } from "./components/ContentToggle";
94
+ export { default as ContentToggle } from "./components/ContentToggle";
93
95
  export type { DismissibleProps } from "./components/Dismissible";
94
96
  export { default as Dismissible } from "./components/Dismissible";
97
+ export type { ExpandableProps } from "./components/Expandable";
98
+ export { default as Expandable } from "./components/Expandable";
95
99
  export type { FlyoutInstance, FlyoutProps, FlyoutTriggerAttributes } from "./components/Flyout";
96
100
  export { default as Flyout } from "./components/Flyout";
97
101
  export type { FormControlProps } from "./components/FormControl";
@@ -106,6 +110,8 @@ export type { HiddenVisuallyProps } from "./components/HiddenVisually";
106
110
  export { default as HiddenVisually } from "./components/HiddenVisually";
107
111
  export type { IconProps } from "./components/Icon";
108
112
  export { default as Icon } from "./components/Icon";
113
+ export type { IconToggleProps } from "./components/IconToggle";
114
+ export { default as IconToggle } from "./components/IconToggle";
109
115
  export type { ImageProps } from "./components/Image";
110
116
  export { default as Image } from "./components/Image";
111
117
  export type { OverlayProps } from "./components/Overlay";
package/dist/index.js CHANGED
@@ -41,7 +41,9 @@ export { default as Tooltip } from "./components/Tooltip";
41
41
  export { default as Accordion } from "./components/Accordion";
42
42
  export { default as Actionable } from "./components/Actionable";
43
43
  export { default as Container } from "./components/Container";
44
+ export { default as ContentToggle } from "./components/ContentToggle";
44
45
  export { default as Dismissible } from "./components/Dismissible";
46
+ export { default as Expandable } from "./components/Expandable";
45
47
  export { default as Flyout } from "./components/Flyout";
46
48
  export { default as FormControl } from "./components/FormControl";
47
49
  export { default as Grid } from "./components/Grid";
@@ -49,6 +51,7 @@ export { default as Hidden } from "./components/Hidden";
49
51
  export { default as HiddenInput } from "./components/HiddenInput";
50
52
  export { default as HiddenVisually } from "./components/HiddenVisually";
51
53
  export { default as Icon } from "./components/Icon";
54
+ export { default as IconToggle } from "./components/IconToggle";
52
55
  export { default as Image } from "./components/Image";
53
56
  export { default as Overlay } from "./components/Overlay";
54
57
  export { default as Reshaped } from "./components/Reshaped";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reshaped",
3
- "version": "4.2.0-canary.0",
3
+ "version": "4.2.0",
4
4
  "description": "Professionally crafted design system in React and Figma for building products of any scale and complexity",
5
5
  "keywords": [
6
6
  "accessibility",
@@ -80,8 +80,8 @@
80
80
  "cssnano": "8.0.2",
81
81
  "csstype": "3.2.3",
82
82
  "postcss-custom-media": "12.0.1",
83
- "@reshaped/utilities": "4.2.0-canary.0",
84
- "@reshaped/theming": "4.2.0-canary.0"
83
+ "@reshaped/theming": "4.2.0",
84
+ "@reshaped/utilities": "4.2.0"
85
85
  },
86
86
  "peerDependencies": {
87
87
  "postcss": "^8",
@@ -1,4 +0,0 @@
1
- import React from "react";
2
- import * as T from "./Expandable.types";
3
- declare const Expandable: React.FC<T.ContentProps>;
4
- export default Expandable;