@fibery/ui-kit 1.37.0 → 1.38.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 (139) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +8 -5
  3. package/src/actions-menu/actions-menu-item.tsx +18 -6
  4. package/src/actions-menu/actions-menu-props.tsx +4 -0
  5. package/src/actions-menu/actions-menu-sub-command-menu.tsx +7 -4
  6. package/src/actions-menu/actions-menu-sub-menu.tsx +9 -3
  7. package/src/actions-menu/actions-menu.tsx +24 -8
  8. package/src/actions-panel.tsx +12 -2
  9. package/src/axis-header.tsx +136 -76
  10. package/src/box.tsx +1 -1
  11. package/src/breadcrumb.tsx +78 -0
  12. package/src/button/actions-button-compact.tsx +4 -1
  13. package/src/button/base-button.tsx +5 -3
  14. package/src/button/button-group.tsx +1 -1
  15. package/src/button/button.tsx +31 -0
  16. package/src/button/icon-button.tsx +9 -0
  17. package/src/button/select-button.tsx +4 -0
  18. package/src/card-container.tsx +7 -4
  19. package/src/color-picker/swatch.tsx +1 -0
  20. package/src/command-menu/index.tsx +3 -3
  21. package/src/context-menu/index.tsx +10 -1
  22. package/src/date-picker/date-range-picker.tsx +6 -3
  23. package/src/date-picker/relative-date-picker.tsx +6 -2
  24. package/src/date-picker/single-date-picker.tsx +8 -5
  25. package/src/date-picker/styles.ts +32 -26
  26. package/src/date-picker/types.ts +3 -1
  27. package/src/day-select/day-select.tsx +21 -75
  28. package/src/day-select/iso-week-day-select.tsx +1 -1
  29. package/src/day-select/mount-day-select.tsx +2 -2
  30. package/src/day-select/week-day-select.tsx +1 -5
  31. package/src/design-system.ts +21 -12
  32. package/src/dropdown-menu/index.tsx +8 -1
  33. package/src/emoji-picker/app-icon-picker.tsx +6 -4
  34. package/src/emoji-picker/emoji-picker-preview-footer.tsx +1 -1
  35. package/src/emoji-picker/emoji-picker.tsx +3 -0
  36. package/src/emoji-picker/emoji.tsx +2 -1
  37. package/src/emoji-picker/icon-emoji-picker.tsx +10 -1
  38. package/src/emoji-picker/primitives/footer.tsx +0 -1
  39. package/src/emoji-picker/primitives/skin-tone.tsx +1 -0
  40. package/src/emoji-picker/use-responsive-emoji-size.ts +11 -0
  41. package/src/emoji-picker/utils/emoji-set.ts +2 -1
  42. package/src/empty-state/empty-state.tsx +6 -0
  43. package/src/field-icon.tsx +4 -1
  44. package/src/file-item.tsx +164 -47
  45. package/src/html-styles.ts +8 -0
  46. package/src/icons/ast/ActivityFilled.ts +8 -0
  47. package/src/icons/ast/AiAssistantFilled.ts +8 -0
  48. package/src/icons/ast/CollapseAllHorizontal.ts +8 -0
  49. package/src/icons/ast/DateRange.ts +1 -1
  50. package/src/icons/ast/EntityCreateFilled.ts +8 -0
  51. package/src/icons/ast/ExpandAllHorizontal.ts +8 -0
  52. package/src/icons/ast/GlobeUnpublish.ts +8 -0
  53. package/src/icons/ast/HomeFilled.ts +8 -0
  54. package/src/icons/ast/KeyboardHideDown.ts +8 -0
  55. package/src/icons/ast/LayoutMultiple.ts +8 -0
  56. package/src/icons/ast/RicheditorHistoryRedo.ts +1 -1
  57. package/src/icons/ast/RicheditorHistoryUndo.ts +1 -1
  58. package/src/icons/ast/SearchFilled.ts +8 -0
  59. package/src/icons/ast/SettingsFilled.ts +8 -0
  60. package/src/icons/ast/SlideMenuFilled.ts +8 -0
  61. package/src/icons/ast/TypeDate.ts +1 -1
  62. package/src/icons/ast/index.tsx +12 -0
  63. package/src/icons/react/ActivityFilled.tsx +13 -0
  64. package/src/icons/react/AiAssistantFilled.tsx +13 -0
  65. package/src/icons/react/CollapseAllHorizontal.tsx +13 -0
  66. package/src/icons/react/EntityCreateFilled.tsx +13 -0
  67. package/src/icons/react/ExpandAllHorizontal.tsx +13 -0
  68. package/src/icons/react/GlobeUnpublish.tsx +13 -0
  69. package/src/icons/react/HomeFilled.tsx +13 -0
  70. package/src/icons/react/KeyboardHideDown.tsx +13 -0
  71. package/src/icons/react/LayoutMultiple.tsx +13 -0
  72. package/src/icons/react/SearchFilled.tsx +13 -0
  73. package/src/icons/react/SettingsFilled.tsx +13 -0
  74. package/src/icons/react/SlideMenuFilled.tsx +13 -0
  75. package/src/icons/react/index.tsx +12 -0
  76. package/src/icons/svg/activity-filled.svg +4 -0
  77. package/src/icons/svg/ai-assistant-filled.svg +3 -0
  78. package/src/icons/svg/collapse-all-horizontal.svg +3 -0
  79. package/src/icons/svg/date-range.svg +3 -3
  80. package/src/icons/svg/entity-create-filled.svg +4 -0
  81. package/src/icons/svg/expand-all-horizontal.svg +3 -0
  82. package/src/icons/svg/globe-unpublish.svg +4 -0
  83. package/src/icons/svg/home-filled.svg +3 -0
  84. package/src/icons/svg/keyboard-hide-down.svg +1 -0
  85. package/src/icons/svg/layout-multiple.svg +3 -0
  86. package/src/icons/svg/richeditor/history/redo.svg +3 -2
  87. package/src/icons/svg/richeditor/history/undo.svg +3 -2
  88. package/src/icons/svg/search-filled.svg +3 -0
  89. package/src/icons/svg/settings-filled.svg +3 -0
  90. package/src/icons/svg/slide-menu-filled.svg +1 -0
  91. package/src/icons/svg/type/date.svg +3 -2
  92. package/src/images-gallery/images-gallery.tsx +254 -0
  93. package/src/images-gallery/zoom.tsx +112 -0
  94. package/src/is-in-popup.ts +1 -1
  95. package/src/layout-styles.ts +21 -1
  96. package/src/lists/actions-menu-row-surface.tsx +3 -1
  97. package/src/lists/list-row-surface.tsx +7 -1
  98. package/src/mobile-keyboard-aware-popup.tsx +87 -0
  99. package/src/mobile-styles.ts +8 -0
  100. package/src/modal-menu/contexts/modal-menu-context.tsx +22 -0
  101. package/src/modal-menu/contexts/modal-submenu-context.tsx +16 -0
  102. package/src/modal-menu/index.tsx +14 -0
  103. package/src/modal-menu/modal-menu-content.tsx +68 -0
  104. package/src/modal-menu/modal-menu-group.tsx +10 -0
  105. package/src/modal-menu/modal-menu-item.tsx +37 -0
  106. package/src/modal-menu/modal-menu-label.tsx +24 -0
  107. package/src/modal-menu/modal-menu-root.tsx +57 -0
  108. package/src/modal-menu/modal-menu-separator.tsx +14 -0
  109. package/src/modal-menu/modal-menu-trigger.tsx +37 -0
  110. package/src/modal-menu/modal-submenu-content.tsx +49 -0
  111. package/src/modal-menu/modal-submenu-trigger.tsx +34 -0
  112. package/src/modal-menu/modal-submenu.tsx +17 -0
  113. package/src/online-users.tsx +3 -3
  114. package/src/palette.ts +1 -1
  115. package/src/popover/index.tsx +687 -0
  116. package/src/popover/mobile-popup-context.tsx +29 -0
  117. package/src/{popup → popover}/modifiers.tsx +0 -7
  118. package/src/{popup → popover}/popup-modifiers-context.ts +3 -3
  119. package/src/reactions/reaction-button.tsx +30 -45
  120. package/src/reactions/reaction-picker.tsx +39 -45
  121. package/src/select/index.tsx +8 -4
  122. package/src/select/select-in-popover.tsx +21 -33
  123. package/src/tab-nav/tab-nav.tsx +35 -9
  124. package/src/toast/primitives.tsx +8 -1
  125. package/src/toggle-button/round-toggle-button.tsx +23 -0
  126. package/src/toggle-button/toggle-button-group.tsx +34 -0
  127. package/src/toggle-button/toggle-button.tsx +164 -0
  128. package/src/toggle.tsx +3 -3
  129. package/src/tooltip.tsx +2 -2
  130. package/src/unit/styles.ts +2 -2
  131. package/src/use-is-phone.tsx +28 -3
  132. package/src/use-is-support-hover.ts +1 -1
  133. package/src/use-on-screen-keyboard-data.tsx +66 -0
  134. package/src/workflow-progress-icon.tsx +16 -22
  135. package/src/popup/index.tsx +0 -383
  136. /package/src/{popup → popover}/popup-stack-context.tsx +0 -0
  137. /package/src/{popup → popover}/reference.ts +0 -0
  138. /package/src/{popup → popover}/styles.ts +0 -0
  139. /package/src/{popup → popover}/use-click-outside.ts +0 -0
@@ -1,2 +1,3 @@
1
- <?xml version="1.0" standalone="no"?>
2
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" class="Icon"><path d="M15.05 11.225C13.5986 9.80981 11.6485 9.0226 9.62138 9.03351C7.59423 9.04443 5.65279 9.85259 4.21667 11.2833C3.50854 11.9851 2.94262 12.817 2.55 13.7333L2.45 14H0.666666C0.766666 13.6917 0.874998 13.3833 0.999998 13.0833C1.59463 11.6717 2.52608 10.4273 3.7129 9.45892C4.89971 8.49054 6.30572 7.82772 7.80795 7.52843C9.31017 7.22913 10.8629 7.30248 12.3302 7.74206C13.7975 8.18164 15.1348 8.97406 16.225 10.05L19 7.275V14H12.275L15.05 11.225Z" class="richEditorIcons"></path></svg>
1
+ <svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M4.64001 11.3477C4.64001 8.94531 6.26302 7.28711 8.91152 7.28711H12.3861L13.7279 7.3457L12.7669 6.54883L11.4369 5.24805C11.2962 5.11328 11.2025 4.94922 11.2025 4.7207C11.2025 4.30469 11.4955 4 11.9349 4C12.1283 4 12.3275 4.08203 12.4798 4.22852L15.7728 7.48633C15.9193 7.62695 16.0013 7.83789 16.0013 8.03711C16.0013 8.24219 15.9193 8.45312 15.7728 8.59375L12.4798 11.8457C12.3275 11.9922 12.1283 12.0801 11.9349 12.0801C11.4955 12.0801 11.2025 11.7695 11.2025 11.3535C11.2025 11.125 11.2962 10.9609 11.4369 10.8262L12.7669 9.52539L13.7279 8.72852L12.3861 8.79297H8.92321C7.14781 8.79297 6.12242 9.83008 6.12242 11.3125C6.12242 12.7891 7.14781 13.8555 8.92321 13.8555H10.3119C10.7513 13.8555 11.0736 14.1953 11.0736 14.6113C11.0736 15.0215 10.7513 15.3613 10.3119 15.3613H8.84702C6.23962 15.3613 4.64001 13.7793 4.64001 11.3477Z" />
3
+ </svg>
@@ -1,2 +1,3 @@
1
- <?xml version="1.0" standalone="no"?>
2
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" class="Icon"><path d="M4.95 11.225C6.40144 9.80981 8.35147 9.0226 10.3786 9.03351C12.4058 9.04443 14.3472 9.85259 15.7833 11.2833C16.4915 11.9851 17.0574 12.817 17.45 13.7333L17.55 14H19.3333C19.2333 13.6917 19.125 13.3833 19 13.0833C18.4054 11.6717 17.4739 10.4273 16.2871 9.45892C15.1003 8.49054 13.6943 7.82772 12.1921 7.52843C10.6898 7.22913 9.13715 7.30248 7.66982 7.74206C6.2025 8.18164 4.86524 8.97406 3.775 10.05L1 7.275V14H7.725L4.95 11.225Z" class="richEditorIcons"></path></svg>
1
+ <svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M15.3613 11.3477C15.3613 8.94531 13.7383 7.28711 11.0898 7.28711H7.61523L6.27344 7.3457L7.23438 6.54883L8.56445 5.24805C8.70508 5.11328 8.79883 4.94922 8.79883 4.7207C8.79883 4.30469 8.50586 4 8.06641 4C7.87305 4 7.67383 4.08203 7.52148 4.22852L4.22852 7.48633C4.08203 7.62695 4 7.83789 4 8.03711C4 8.24219 4.08203 8.45312 4.22852 8.59375L7.52148 11.8457C7.67383 11.9922 7.87305 12.0801 8.06641 12.0801C8.50586 12.0801 8.79883 11.7695 8.79883 11.3535C8.79883 11.125 8.70508 10.9609 8.56445 10.8262L7.23438 9.52539L6.27344 8.72852L7.61523 8.79297H11.0781C12.8535 8.79297 13.8789 9.83008 13.8789 11.3125C13.8789 12.7891 12.8535 13.8555 11.0781 13.8555H9.68945C9.25 13.8555 8.92773 14.1953 8.92773 14.6113C8.92773 15.0215 9.25 15.3613 9.68945 15.3613H11.1543C13.7617 15.3613 15.3613 13.7793 15.3613 11.3477Z" />
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M13.875 8.75C13.875 5.91954 11.5805 3.625 8.75 3.625C5.91954 3.625 3.625 5.91954 3.625 8.75C3.625 11.5805 5.91954 13.875 8.75 13.875C10.1655 13.875 11.4453 13.3027 12.374 12.374C13.3026 11.4454 13.875 10.1654 13.875 8.75ZM16.125 8.75C16.125 10.3801 15.5937 11.8867 14.6982 13.1074L18.2959 16.7041C18.7352 17.1435 18.7352 17.8566 18.2959 18.2959C17.8565 18.7352 17.1434 18.7352 16.7041 18.2959L13.1074 14.6982C11.8867 15.5937 10.3801 16.125 8.75 16.125C4.6769 16.125 1.375 12.8231 1.375 8.75C1.375 4.6769 4.6769 1.375 8.75 1.375C12.8231 1.375 16.125 4.6769 16.125 8.75Z" />
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 1.34961C10.8958 1.35078 11.2824 1.47186 11.6084 1.69629C11.9343 1.92065 12.1847 2.23836 12.3272 2.60742L12.7539 3.70898L14.0703 4.4668L15.2568 4.28613C15.6429 4.23378 16.036 4.29728 16.3858 4.46875C16.7355 4.64028 17.0266 4.91185 17.2217 5.24902L17.2236 5.25293L17.707 6.09961C17.9066 6.44122 17.9986 6.83577 17.9707 7.23047C17.9427 7.62564 17.7961 8.0032 17.5498 8.31348L16.8242 9.23828V10.7559L17.5723 11.6836C17.8198 11.9944 17.967 12.3732 17.9951 12.7695C18.0231 13.1642 17.931 13.5588 17.7315 13.9004L17.2481 14.7471L17.2461 14.751C17.0511 15.0881 16.7599 15.3597 16.4102 15.5312C16.0604 15.7027 15.6672 15.7662 15.2813 15.7139L15.2686 15.7129L14.0947 15.5332L12.7783 16.291L12.3516 17.3916C12.2091 17.7609 11.9589 18.0792 11.6328 18.3037C11.3068 18.5282 10.9203 18.6492 10.5244 18.6504H9.50001C9.10419 18.6492 8.71765 18.5281 8.39161 18.3037C8.06583 18.0794 7.81537 17.7615 7.67286 17.3926L7.24611 16.291L5.9297 15.5332L4.74318 15.7139C4.35716 15.7662 3.96405 15.7027 3.61427 15.5312C3.26453 15.3597 2.97339 15.0882 2.77833 14.751L2.77638 14.7471L2.29298 13.9004C2.09345 13.5588 2.00138 13.1642 2.02931 12.7695C2.05734 12.3744 2.20395 11.9968 2.45021 11.6865L3.17579 10.7617V9.24414L2.42775 8.31641C2.18022 8.00564 2.03301 7.62676 2.0049 7.23047C1.97697 6.83577 2.06903 6.44122 2.26857 6.09961L2.75392 5.24902C2.94895 4.91188 3.24016 4.64028 3.58986 4.46875C3.93974 4.29723 4.33361 4.2337 4.71974 4.28613L4.73146 4.28711L5.90822 4.4668L7.24708 3.70703L7.67286 2.6084C7.81537 2.23947 8.06583 1.92059 8.39161 1.69629C8.71765 1.47185 9.10418 1.35078 9.50001 1.34961H10.5ZM10 7.25C8.48124 7.25001 7.25001 8.48122 7.25001 10C7.25001 11.5188 8.48124 12.75 10 12.75C11.5188 12.75 12.75 11.5188 12.75 10C12.75 8.48122 11.5188 7.25 10 7.25Z" />
3
+ </svg>
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="m18 5.75c0-1.51878-1.2312-2.75-2.75-2.75h-10.5c-1.51878 0-2.75 1.23122-2.75 2.75v8.5c0 1.5188 1.23122 2.75 2.75 2.75h10.5c1.5188 0 2.75-1.2312 2.75-2.75zm-11-1.25c.69036 0 1.25.55964 1.25 1.25v8.5c0 .6904-.55964 1.25-1.25 1.25h-2.25c-.69036 0-1.25-.5596-1.25-1.25v-8.5c0-.69036.55964-1.25 1.25-1.25z" fill-rule="evenodd"/></svg>
@@ -1,2 +1,3 @@
1
- <?xml version="1.0" standalone="no"?>
2
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" class="Icon"><path d="M15.6 3.6H14V2.8C14 2.58783 13.9157 2.38434 13.7657 2.23431C13.6157 2.08429 13.4122 2 13.2 2C12.9878 2 12.7843 2.08429 12.6343 2.23431C12.4843 2.38434 12.4 2.58783 12.4 2.8V3.6H7.6V2.8C7.6 2.58783 7.51571 2.38434 7.36569 2.23431C7.21566 2.08429 7.01217 2 6.8 2C6.58783 2 6.38434 2.08429 6.23431 2.23431C6.08429 2.38434 6 2.58783 6 2.8V3.6H4.4C3.76348 3.6 3.15303 3.85286 2.70294 4.30294C2.25286 4.75303 2 5.36348 2 6V15.6C2 16.2365 2.25286 16.847 2.70294 17.2971C3.15303 17.7471 3.76348 18 4.4 18H15.6C16.2365 18 16.847 17.7471 17.2971 17.2971C17.7471 16.847 18 16.2365 18 15.6V6C18 5.36348 17.7471 4.75303 17.2971 4.30294C16.847 3.85286 16.2365 3.6 15.6 3.6ZM16.4 15.6C16.4 15.8122 16.3157 16.0157 16.1657 16.1657C16.0157 16.3157 15.8122 16.4 15.6 16.4H4.4C4.18783 16.4 3.98434 16.3157 3.83431 16.1657C3.68429 16.0157 3.6 15.8122 3.6 15.6V10H16.4V15.6ZM16.4 8.4H3.6V6C3.6 5.78783 3.68429 5.58434 3.83431 5.43431C3.98434 5.28429 4.18783 5.2 4.4 5.2H6V6C6 6.21217 6.08429 6.41566 6.23431 6.56569C6.38434 6.71571 6.58783 6.8 6.8 6.8C7.01217 6.8 7.21566 6.71571 7.36569 6.56569C7.51571 6.41566 7.6 6.21217 7.6 6V5.2H12.4V6C12.4 6.21217 12.4843 6.41566 12.6343 6.56569C12.7843 6.71571 12.9878 6.8 13.2 6.8C13.4122 6.8 13.6157 6.71571 13.7657 6.56569C13.9157 6.41566 14 6.21217 14 6V5.2H15.6C15.8122 5.2 16.0157 5.28429 16.1657 5.43431C16.3157 5.58434 16.4 5.78783 16.4 6V8.4Z" class="typeIcons"></path></svg>
1
+ <svg height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M15.6 3H14V1.80391C14 1.59174 13.9157 1.38825 13.7657 1.23822C13.6157 1.0882 13.4122 1.00391 13.2 1.00391C12.9878 1.00391 12.7843 1.0882 12.6343 1.23822C12.4843 1.38825 12.4 1.59174 12.4 1.80391V3H7.6V1.80391C7.6 1.59174 7.51571 1.38825 7.36569 1.23822C7.21566 1.0882 7.01217 1.00391 6.8 1.00391C6.58783 1.00391 6.38434 1.0882 6.23431 1.23822C6.08429 1.38825 6 1.59174 6 1.80391V3H4.4C3.76348 3 3.15303 3.25286 2.70294 3.70294C2.25286 4.15303 2 4.76348 2 5.4V14.6C2 15.2365 2.25286 15.847 2.70294 16.2971C3.15303 16.7471 3.76348 17 4.4 17H15.6C16.2365 17 16.847 16.7471 17.2971 16.2971C17.7471 15.847 18 15.2365 18 14.6V5.4C18 4.76348 17.7471 4.15303 17.2971 3.70294C16.847 3.25286 16.2365 3 15.6 3ZM16.1657 15.1657C16.3157 15.0157 16.4 14.8122 16.4 14.6V9H3.6V14.6C3.6 14.8122 3.68429 15.0157 3.83431 15.1657C3.98434 15.3157 4.18783 15.4 4.4 15.4H15.6C15.8122 15.4 16.0157 15.3157 16.1657 15.1657ZM3.6 7.4H16.4V5.4C16.4 5.18783 16.3157 4.98434 16.1657 4.83431C16.0157 4.68429 15.8122 4.6 15.6 4.6H4.4C4.18783 4.6 3.98434 4.68429 3.83431 4.83431C3.68429 4.98434 3.6 5.18783 3.6 5.4V7.4Z" />
3
+ </svg>
@@ -0,0 +1,254 @@
1
+ import {ReactNode, useCallback, useEffect, useMemo, useReducer, useRef, useState} from "react";
2
+ import _ from "lodash";
3
+ import {createContext} from "@fibery/react/src/create-context";
4
+ import {useIsPhone} from "../use-is-phone";
5
+ import {safeAreaInsetBottomVar} from "../mobile-styles";
6
+ import {buttonGroup, ButtonGroup} from "../button/button-group";
7
+ import {IconButton} from "../button/icon-button";
8
+ import ArrowRight from "../icons/react/ArrowRight";
9
+ import ArrowLeft from "../icons/react/ArrowLeft";
10
+ import {css} from "@linaria/core";
11
+ import cx from "classnames";
12
+
13
+ type Registry = Map<string, {zoomed: boolean; setZoomed: (zoomed: boolean) => void}>;
14
+
15
+ const [ImagesGalleryRegistryProvider, useImagesGalleryRegistry] = createContext(
16
+ "ImagesGalleryRegistryContext",
17
+ new Map()
18
+ );
19
+
20
+ export {useImagesGalleryRegistry};
21
+
22
+ type Methods = {
23
+ setVisible: (visible: boolean) => void;
24
+ setActivated: (activated: boolean) => void;
25
+ update: () => void;
26
+ nextImage: () => void;
27
+ prevImage: () => void;
28
+ };
29
+ const [ImagesGalleryMethodsProvider, useImagesGalleryMethods] = createContext<Methods>("ImagesGalleryMethodsProvider", {
30
+ setVisible: _.noop,
31
+ setActivated: _.noop,
32
+ update: _.noop,
33
+ nextImage: _.noop,
34
+ prevImage: _.noop,
35
+ });
36
+ type State = {visible: boolean; activated: boolean; _inc: number};
37
+
38
+ const [ImagesGalleryStateProvider, useImagesGalleryState] = createContext<State>("ImagesGalleryStateContext", {
39
+ visible: false,
40
+ activated: false,
41
+ _inc: 0,
42
+ });
43
+
44
+ export {useImagesGalleryMethods, useImagesGalleryState};
45
+
46
+ type Actions = {type: "set-visible"; payload: boolean} | {type: "set-activated"; payload: boolean} | {type: "inc"};
47
+
48
+ function initialState(): State {
49
+ return {visible: false, activated: false, _inc: 0};
50
+ }
51
+
52
+ function imagesGalleryReducer(state: State, action: Actions) {
53
+ if (action.type === "set-visible") {
54
+ return {...state, visible: action.payload};
55
+ }
56
+ if (action.type === "set-activated") {
57
+ return {...state, activated: action.payload};
58
+ }
59
+ if (action.type === "inc") {
60
+ return {...state, _inc: state._inc + 1};
61
+ }
62
+ return state;
63
+ }
64
+
65
+ const controlsGroupStyle = css`
66
+ &.${buttonGroup} {
67
+ display: none;
68
+ }
69
+ `;
70
+
71
+ export function Controls() {
72
+ const imagesRegistry = useImagesGalleryRegistry();
73
+ const {visible} = useImagesGalleryState();
74
+ const isPhone = useIsPhone();
75
+ const {prevImage, nextImage, setVisible} = useImagesGalleryMethods();
76
+ // Determine if there is a zoomed image and its position in the registry
77
+ let activeNext = true;
78
+ let activePrev = true;
79
+ let i = 0;
80
+ for (const image of imagesRegistry.values()) {
81
+ if (image.zoomed) {
82
+ if (i === 0) {
83
+ activePrev = false;
84
+ }
85
+ if (i === imagesRegistry.size - 1) {
86
+ activeNext = false;
87
+ }
88
+ break;
89
+ }
90
+ i++;
91
+ }
92
+ const visibleState = visible || isPhone;
93
+ if (imagesRegistry.size > 1) {
94
+ const size = isPhone ? "xLarge" : "small";
95
+ return (
96
+ <div
97
+ className={css`
98
+ position: fixed;
99
+ bottom: max(var(${safeAreaInsetBottomVar}), 12px);
100
+ left: 0;
101
+ right: 0;
102
+ display: grid;
103
+ align-items: center;
104
+ justify-content: center;
105
+ &:hover .${controlsGroupStyle}.${buttonGroup} {
106
+ display: flex;
107
+ }
108
+ `}
109
+ >
110
+ <ButtonGroup
111
+ className={cx(
112
+ controlsGroupStyle,
113
+ visibleState &&
114
+ css`
115
+ &.${controlsGroupStyle}.${buttonGroup} {
116
+ display: flex;
117
+ }
118
+ `
119
+ )}
120
+ >
121
+ <IconButton
122
+ variant={"outline"}
123
+ size={size}
124
+ onClick={() => {
125
+ prevImage();
126
+ setVisible(true);
127
+ }}
128
+ disabled={!activePrev}
129
+ >
130
+ <ArrowLeft />
131
+ </IconButton>
132
+ <IconButton
133
+ variant={"outline"}
134
+ size={size}
135
+ onClick={() => {
136
+ nextImage();
137
+ setVisible(true);
138
+ }}
139
+ disabled={!activeNext}
140
+ >
141
+ <ArrowRight />
142
+ </IconButton>
143
+ </ButtonGroup>
144
+ </div>
145
+ );
146
+ }
147
+ return null;
148
+ }
149
+
150
+ export function ImagesGalleryProvider({children}: {children: ReactNode}) {
151
+ const [imagesRegistry] = useState<Registry>(() => new Map());
152
+ const timeoutRef = useRef<ReturnType<typeof setTimeout>>();
153
+ const [galleryState, dispatchState] = useReducer(imagesGalleryReducer, [], initialState);
154
+ const setVisible = useCallback((visible: boolean) => dispatchState({type: "set-visible", payload: visible}), []);
155
+ const setActivated = useCallback(
156
+ (activated: boolean) => dispatchState({type: "set-activated", payload: activated}),
157
+ []
158
+ );
159
+ const update = useCallback(() => dispatchState({type: "inc"}), []);
160
+ const {visible} = galleryState;
161
+ const timeout = 3000;
162
+ useEffect(() => {
163
+ if (visible) {
164
+ clearTimeout(timeoutRef.current);
165
+ timeoutRef.current = setTimeout(() => {
166
+ setVisible(false);
167
+ }, timeout);
168
+ }
169
+ return () => {
170
+ clearTimeout(timeoutRef.current);
171
+ };
172
+ }, [setVisible, visible]);
173
+ const nextImage = useCallback(() => {
174
+ let i = 0;
175
+ for (const image of imagesRegistry.values()) {
176
+ if (image.zoomed) {
177
+ if (i === imagesRegistry.size - 1) {
178
+ break;
179
+ }
180
+ image.setZoomed(false);
181
+ const nextImage = Array.from(imagesRegistry.values())[i + 1];
182
+ nextImage.setZoomed(true);
183
+ break;
184
+ }
185
+ i++;
186
+ }
187
+ clearTimeout(timeoutRef.current);
188
+ timeoutRef.current = setTimeout(() => {
189
+ setVisible(false);
190
+ }, timeout);
191
+ }, [imagesRegistry, setVisible]);
192
+
193
+ const prevImage = useCallback(() => {
194
+ let i = 0;
195
+ for (const image of imagesRegistry.values()) {
196
+ if (image.zoomed) {
197
+ if (i === 0) {
198
+ break;
199
+ }
200
+ image.setZoomed(false);
201
+ const prevImage = Array.from(imagesRegistry.values())[i - 1];
202
+ prevImage.setZoomed(true);
203
+ break;
204
+ }
205
+ i++;
206
+ }
207
+ clearTimeout(timeoutRef.current);
208
+ timeoutRef.current = setTimeout(() => {
209
+ setVisible(false);
210
+ }, timeout);
211
+ }, [imagesRegistry, setVisible]);
212
+
213
+ const methods = useMemo(
214
+ () => ({setVisible, setActivated, update, nextImage, prevImage}),
215
+ [setVisible, setActivated, update, nextImage, prevImage]
216
+ );
217
+
218
+ useEffect(() => {
219
+ const listener = (evt: KeyboardEvent) => {
220
+ const hasActive = Array.from(imagesRegistry.values()).some((image) => image.zoomed);
221
+ if (!hasActive) {
222
+ return;
223
+ }
224
+ if (evt.key === "Escape") {
225
+ for (const image of imagesRegistry.values()) {
226
+ if (image.zoomed) {
227
+ image.setZoomed(false);
228
+ break;
229
+ }
230
+ }
231
+ }
232
+ if (evt.key === "ArrowRight") {
233
+ nextImage();
234
+ }
235
+ if (evt.key === "ArrowLeft") {
236
+ prevImage();
237
+ }
238
+ };
239
+ document.addEventListener("keydown", listener, {capture: true});
240
+ return () => {
241
+ document.removeEventListener("keydown", listener, {capture: true});
242
+ };
243
+ }, [imagesRegistry, nextImage, prevImage]);
244
+
245
+ return (
246
+ <>
247
+ <ImagesGalleryRegistryProvider value={imagesRegistry}>
248
+ <ImagesGalleryMethodsProvider value={methods}>
249
+ <ImagesGalleryStateProvider value={galleryState}>{children}</ImagesGalleryStateProvider>
250
+ </ImagesGalleryMethodsProvider>
251
+ </ImagesGalleryRegistryProvider>
252
+ </>
253
+ );
254
+ }
@@ -0,0 +1,112 @@
1
+ import {css} from "@linaria/core";
2
+ import React, {ReactNode, useCallback} from "react";
3
+ import {Controlled} from "react-medium-image-zoom";
4
+ import "react-medium-image-zoom/dist/styles.css";
5
+ import {themeVars} from "../design-system";
6
+ import {IconButton} from "../button/icon-button";
7
+ import Close from "../icons/react/Close";
8
+ import {Controls, useImagesGalleryMethods} from "./images-gallery";
9
+ import {useIsPhone} from "../use-is-phone";
10
+
11
+ const dialogCss = css`
12
+ :global() {
13
+ [data-rmiz-modal-overlay="hidden"] {
14
+ background-color: transparent;
15
+ }
16
+ [data-rmiz-modal-overlay="visible"] {
17
+ background-color: ${themeVars.colorBgEditorImageZoomed};
18
+ }
19
+ [data-rmiz-content="found"] > img {
20
+ cursor: auto;
21
+ }
22
+ }
23
+ `;
24
+
25
+ type ModalState = "LOADED" | "LOADING" | "UNLOADED" | "UNLOADING";
26
+
27
+ const ZoomContent = ({
28
+ img,
29
+ modalState,
30
+ onUnzoom,
31
+ }: {
32
+ img: React.ReactElement | null;
33
+ modalState: ModalState;
34
+ onUnzoom: (e: Event) => void;
35
+ }) => {
36
+ const imagesGalleryMethods = useImagesGalleryMethods();
37
+ const isPhone = useIsPhone();
38
+
39
+ const button = modalState === "LOADED" && (
40
+ <IconButton
41
+ size={isPhone ? "xLarge" : "small"}
42
+ color="neutral"
43
+ variant="ghost"
44
+ className={css`
45
+ position: absolute;
46
+ inset: 20px 20px auto auto;
47
+ z-index: 1;
48
+ `}
49
+ onClick={onUnzoom as () => void}
50
+ >
51
+ <Close />
52
+ </IconButton>
53
+ );
54
+ return (
55
+ <>
56
+ <div
57
+ onMouseMove={() => {
58
+ imagesGalleryMethods.setVisible(true);
59
+ }}
60
+ className={css`
61
+ position: absolute;
62
+ inset: 0;
63
+ `}
64
+ >
65
+ {img}
66
+ {button}
67
+ </div>
68
+ <Controls />
69
+ </>
70
+ );
71
+ };
72
+
73
+ type Props = {
74
+ zoomed: boolean;
75
+ onZoomedChange: (value: boolean) => void;
76
+ children: ReactNode;
77
+ className?: string;
78
+ openOnClick?: boolean;
79
+ zoomImg?: {src: string; alt?: string};
80
+ };
81
+
82
+ export const Zoom = (props: Props) => {
83
+ const {zoomed, onZoomedChange, children, className, openOnClick, zoomImg} = props;
84
+ const imagesGalleryMethods = useImagesGalleryMethods();
85
+ const onClick = useCallback(() => {
86
+ if (zoomed) {
87
+ onZoomedChange(false);
88
+ } else if (openOnClick) {
89
+ onZoomedChange(true);
90
+ }
91
+ }, [zoomed, onZoomedChange, openOnClick]);
92
+
93
+ const onDoubleClick = useCallback(() => {
94
+ if (!zoomed) {
95
+ onZoomedChange(true);
96
+ }
97
+ }, [zoomed, onZoomedChange]);
98
+
99
+ return (
100
+ <div className={className} onClick={onClick} onDoubleClick={openOnClick ? undefined : onDoubleClick}>
101
+ <Controlled
102
+ zoomImg={zoomImg}
103
+ onZoomChange={imagesGalleryMethods.update}
104
+ classDialog={dialogCss}
105
+ isZoomed={zoomed}
106
+ ZoomContent={ZoomContent}
107
+ >
108
+ {children}
109
+ </Controlled>
110
+ </div>
111
+ );
112
+ };
@@ -1,4 +1,4 @@
1
- import {basicPopupClassName} from "./popup/styles";
1
+ import {basicPopupClassName} from "./popover/styles";
2
2
  import {$TSFixMe} from "./tsfixme";
3
3
 
4
4
  const selectors = [
@@ -5,18 +5,38 @@ import {
5
5
  sidebarMenuItemHeightVar,
6
6
  sidebarMenuIconSizeVar,
7
7
  textStyles,
8
+ menuItemHeightVar,
8
9
  } from "./design-system";
9
- import {mobileRootClassName} from "./mobile-styles";
10
+ import {
11
+ keyboardHeightVar,
12
+ keyboardInsetBottomVar,
13
+ mobileRootClassName,
14
+ safeAreaInsetLeftVar,
15
+ safeAreaInsetRightVar,
16
+ safeAreaInsetTopVar,
17
+ safeAreaInsetBottomVar,
18
+ } from "./mobile-styles";
10
19
 
11
20
  export const layoutStyles = css`
12
21
  :global() {
13
22
  html:root {
23
+ ${safeAreaInsetLeftVar}: var(--app-safe-area-inset-left, env(safe-area-inset-left));
24
+ ${safeAreaInsetRightVar}: var(--app-safe-area-inset-right, env(safe-area-inset-right));
25
+ ${safeAreaInsetTopVar}: var(--app-safe-area-inset-top, env(safe-area-inset-top));
26
+ ${safeAreaInsetBottomVar}: var(--app-safe-area-inset-bottom, env(safe-area-inset-bottom));
27
+ ${keyboardInsetBottomVar}: var(--app-current-keyboard-height, 0px);
28
+ ${keyboardHeightVar}: 350px;
29
+
30
+ ${menuItemHeightVar}: ${layout.menuItemHeight}px;
14
31
  ${sidebarMenuItemHeightVar}: ${layout.newMenuItemHeight}px;
15
32
  ${sidebarMenuIconSizeVar}: ${layout.newMenuIconSize}px;
16
33
  ${listRowSurfaceFontSizeVar}: ${textStyles.regular.fontSize}px;
34
+
17
35
  &.${mobileRootClassName} {
36
+ ${menuItemHeightVar}: ${layout.mobileMenuItemHeight}px;
18
37
  ${sidebarMenuItemHeightVar}: ${layout.mobileSidebarMenuItemHeight}px;
19
38
  ${sidebarMenuIconSizeVar}: ${layout.mobileSidebarIconSize}px;
39
+ ${listRowSurfaceFontSizeVar}: ${textStyles.big.fontSize}px;
20
40
  }
21
41
  }
22
42
  }
@@ -7,6 +7,7 @@ import {useActionsMenuContext} from "../actions-menu";
7
7
  import {ActionsMenuItemProps} from "../actions-menu/actions-menu-item";
8
8
  import {useActiveDangerousRow, useSetActiveDangerousRow} from "../actions-menu/contexts/actions-menu-dangerous-rows";
9
9
  import {colors, layout, lineHeight, space, textStyles, themeVars} from "../design-system";
10
+ import {useIsSupportHover} from "../use-is-support-hover";
10
11
  import {
11
12
  ListRowContent,
12
13
  ListRowContentProps,
@@ -66,6 +67,7 @@ export const actionMenuItemSurface = css`
66
67
  export const ActionMenuItemSurface = forwardRef<HTMLDivElement, ActionMenuItemSurfaceProps>(
67
68
  ({testId, className, loading, selected, hovered, focused, title, disabled, children, dangerous, ...props}, ref) => {
68
69
  const id = useId();
70
+ const platformSupportsHover = useIsSupportHover();
69
71
  const activeDangerousRow = useActiveDangerousRow();
70
72
  const showWarning = activeDangerousRow === id;
71
73
  const onSelect = useCallbackRef(props.onSelect || _.noop);
@@ -100,7 +102,7 @@ export const ActionMenuItemSurface = forwardRef<HTMLDivElement, ActionMenuItemSu
100
102
  listRowSurfaceStylesBase,
101
103
  actionMenuItemSurface,
102
104
  className,
103
- hovered === undefined && NATURAL_HOVER_CLASS,
105
+ platformSupportsHover && hovered === undefined && NATURAL_HOVER_CLASS,
104
106
  focused === undefined && NATURAL_FOCUS_CLASS,
105
107
  focused && "focus",
106
108
  disabled && "done",
@@ -1,6 +1,8 @@
1
1
  import {css, cx} from "@linaria/core";
2
2
  import {ForwardedRef, forwardRef, HTMLAttributes, PropsWithChildren, ReactNode} from "react";
3
3
  import {border, layoutVars, space, textStyles, themeVars, transition} from "../design-system";
4
+ import {useIsSupportHover} from "../use-is-support-hover";
5
+ import {mobileRootSelector} from "../mobile-styles";
4
6
 
5
7
  interface ListRowSurfaceBase extends HTMLAttributes<HTMLDivElement> {
6
8
  testId?: string;
@@ -152,6 +154,7 @@ export const listRowSurfaceStatesDark = css`
152
154
 
153
155
  export const ListRowSurface = forwardRef<HTMLDivElement, ListRowSurfaceProps>(
154
156
  ({selected, focused, darker, hovered, done, className, children, ...rest}, ref) => {
157
+ const platformSupportsHover = useIsSupportHover();
155
158
  return (
156
159
  <div
157
160
  {...rest}
@@ -159,7 +162,7 @@ export const ListRowSurface = forwardRef<HTMLDivElement, ListRowSurfaceProps>(
159
162
  listRowSurfaceStylesBase,
160
163
  darker ? listRowSurfaceStatesDark : listRowSurfaceStatesDefault,
161
164
  className,
162
- hovered === undefined && NATURAL_HOVER_CLASS,
165
+ platformSupportsHover && hovered === undefined && NATURAL_HOVER_CLASS,
163
166
  focused === undefined && NATURAL_FOCUS_CLASS,
164
167
  focused && "focus",
165
168
  hovered && "hover",
@@ -204,6 +207,9 @@ const ListRowContentSegment = css`
204
207
  min-width: 0; // allows ellipsis in flex contexts :)
205
208
  max-width: 100%;
206
209
  width: 100%;
210
+ ${mobileRootSelector} & {
211
+ ${textStyles.big}
212
+ }
207
213
  `;
208
214
  export const ListRowContent = forwardRef<HTMLDivElement, ListRowContentProps>(
209
215
  (
@@ -0,0 +1,87 @@
1
+ import {ReactNode, useEffect, useRef} from "react";
2
+ import {createPortal} from "react-dom";
3
+ import _ from "lodash";
4
+ import {css, cx} from "@linaria/core";
5
+ import {border, space, themeVars} from "./design-system";
6
+ import {keyboardInsetBottomVar, safeAreaInsetBottomVar} from "./mobile-styles";
7
+
8
+ export const mobileKeyboardAwarePopupZIndex = 1001;
9
+
10
+ export function MobileKeyboardAwarePopup({children, className}: {children: ReactNode; className?: string}) {
11
+ const ref = useRef<HTMLDivElement>(null);
12
+ useEffect(() => {
13
+ const viewport = window.visualViewport;
14
+ if (!viewport) {
15
+ return _.noop;
16
+ }
17
+
18
+ const manualKeyboardModeHandler = document.documentElement.classList.contains("fibery-manual-keyboard-mode");
19
+
20
+ let pendingUpdate = false;
21
+ const layoutViewport = document.getElementById("root") as HTMLDivElement;
22
+ const timeoutId = setTimeout(() => {
23
+ if (viewport.offsetTop >= 0 && ref.current) {
24
+ ref.current.style.transform = `translateY(${
25
+ manualKeyboardModeHandler
26
+ ? viewport.offsetTop
27
+ : viewport.height - layoutViewport.getBoundingClientRect().height + viewport.offsetTop
28
+ }px)`;
29
+ }
30
+ }, 0);
31
+
32
+ function viewportHandler() {
33
+ clearTimeout(timeoutId);
34
+ if (pendingUpdate) {
35
+ return;
36
+ }
37
+ pendingUpdate = true;
38
+
39
+ requestAnimationFrame(() => {
40
+ pendingUpdate = false;
41
+
42
+ if (viewport && viewport.offsetTop >= 0 && ref.current) {
43
+ ref.current.style.transform = `translateY(${
44
+ manualKeyboardModeHandler
45
+ ? viewport.offsetTop
46
+ : viewport.height - layoutViewport.getBoundingClientRect().height + viewport.offsetTop
47
+ }px)`;
48
+ }
49
+ });
50
+ }
51
+
52
+ viewport.addEventListener("scroll", viewportHandler);
53
+ viewport.addEventListener("resize", viewportHandler);
54
+ return () => {
55
+ clearTimeout(timeoutId);
56
+ viewport.removeEventListener("scroll", viewportHandler);
57
+ viewport.removeEventListener("resize", viewportHandler);
58
+ };
59
+ }, []);
60
+ return createPortal(
61
+ <div
62
+ ref={ref}
63
+ className={cx(
64
+ css`
65
+ box-shadow: ${themeVars.shadow200};
66
+ background-color: ${themeVars.actionMenuBg};
67
+ position: fixed;
68
+ bottom: max(max(var(${keyboardInsetBottomVar}), var(${safeAreaInsetBottomVar})), 8px);
69
+ left: 0;
70
+ right: 0;
71
+ z-index: ${mobileKeyboardAwarePopupZIndex};
72
+ transform: translateY(0px);
73
+ border-radius: ${border.radius16}px;
74
+ border: 1px solid ${themeVars.separatorColor};
75
+ margin: 0 ${space.s8}px;
76
+ .fibery-manual-keyboard-mode & {
77
+ margin-bottom: 8px;
78
+ }
79
+ `,
80
+ className
81
+ )}
82
+ >
83
+ {children}
84
+ </div>,
85
+ document.body
86
+ );
87
+ }
@@ -1,2 +1,10 @@
1
1
  export const mobileRootClassName = "fibery-mobile";
2
2
  export const mobileRootSelector = `.${mobileRootClassName}`;
3
+ export const mobileKeyboardOpenClassName = "fibery-mobile-keyboard-open";
4
+ export const mobileKeyboardOpenSelector = `.${mobileKeyboardOpenClassName}`;
5
+ export const keyboardHeightVar = "--fibery-on-screen-keyboard-height";
6
+ export const safeAreaInsetLeftVar = "--safe-area-inset-left";
7
+ export const safeAreaInsetRightVar = "--safe-area-inset-right";
8
+ export const safeAreaInsetTopVar = "--safe-area-inset-top";
9
+ export const safeAreaInsetBottomVar = "--safe-area-inset-bottom";
10
+ export const keyboardInsetBottomVar = "--keyboard-inset-bottom";
@@ -0,0 +1,22 @@
1
+ import {createContext} from "@fibery/react/src/create-context";
2
+
3
+ export type SubmenuItem = {
4
+ id: string;
5
+ title: string;
6
+ };
7
+
8
+ export type ModalMenuContext = {
9
+ open: boolean;
10
+ title: string;
11
+ submenuStack: SubmenuItem[];
12
+ contentContainer: HTMLDivElement | null;
13
+ setContentContainer: (el: HTMLDivElement | null) => void;
14
+ onOpenSubmenu: (item: SubmenuItem) => void;
15
+ onNavigateBack: () => void;
16
+ onOpenChange: (open: boolean) => void;
17
+ height?: string;
18
+ };
19
+
20
+ const [ModalMenuContextProvider, useModalMenu] = createContext<ModalMenuContext>("ModalMenuContext");
21
+
22
+ export {ModalMenuContextProvider, useModalMenu};