@jackuait/blok 0.7.0-beta.4 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blok.mjs +3 -7
- package/dist/chunks/blok-Ufr5cPq-.mjs +12435 -0
- package/dist/chunks/constants-DT17zmu_.mjs +2934 -0
- package/dist/chunks/i18next-DymC16cN.mjs +1146 -0
- package/dist/chunks/i18next-loader-qjweOJ-t.mjs +35 -0
- package/dist/chunks/lightweight-i18n-vbtPx5C4.mjs +105 -0
- package/dist/chunks/messages-3bOAVT3X2.mjs +80 -0
- package/dist/chunks/messages-43N0Vfg42.mjs +80 -0
- package/dist/chunks/messages-B0cg-ThO2.mjs +80 -0
- package/dist/chunks/messages-B3StvafX.mjs +80 -0
- package/dist/chunks/messages-B7LU-b6n2.mjs +80 -0
- package/dist/chunks/messages-B87-89os.mjs +80 -0
- package/dist/chunks/messages-BFiMCfDX2.mjs +80 -0
- package/dist/chunks/messages-BLxyso1L.mjs +80 -0
- package/dist/chunks/messages-BQZtOYxr2.mjs +80 -0
- package/dist/chunks/messages-BRrtoRdw2.mjs +80 -0
- package/dist/chunks/messages-BU_YdaAf.mjs +80 -0
- package/dist/chunks/messages-BWbZYIs12.mjs +80 -0
- package/dist/chunks/messages-B_Qcy8kr2.mjs +80 -0
- package/dist/chunks/messages-B_uTiuQ-.mjs +80 -0
- package/dist/chunks/messages-BdWTM73p.mjs +80 -0
- package/dist/chunks/messages-BhZcNoIQ.mjs +80 -0
- package/dist/chunks/messages-Bn6LwI4B.mjs +80 -0
- package/dist/chunks/messages-BoTtYEct2.mjs +80 -0
- package/dist/chunks/messages-BrvAiuWT.mjs +80 -0
- package/dist/chunks/messages-Byp0YFMg.mjs +80 -0
- package/dist/chunks/messages-C0ZWDShx2.mjs +80 -0
- package/dist/chunks/messages-CA-jms9R.mjs +80 -0
- package/dist/chunks/messages-CFr0Ha6p2.mjs +80 -0
- package/dist/chunks/messages-CG2xl0IV.mjs +80 -0
- package/dist/chunks/messages-CIGX0FfW.mjs +80 -0
- package/dist/chunks/messages-CRMdL0jG.mjs +80 -0
- package/dist/chunks/messages-CRdl14uE.mjs +80 -0
- package/dist/chunks/messages-Cimsel4e.mjs +80 -0
- package/dist/chunks/messages-CjcSWeud.mjs +80 -0
- package/dist/chunks/messages-ClDJuy8K2.mjs +80 -0
- package/dist/chunks/messages-Cn1AC0Qk.mjs +80 -0
- package/dist/chunks/messages-CpnXbVOK2.mjs +80 -0
- package/dist/chunks/messages-CqsES1wk2.mjs +80 -0
- package/dist/chunks/messages-Csq7JatN.mjs +80 -0
- package/dist/chunks/messages-CtufKbaD.mjs +80 -0
- package/dist/chunks/messages-Cuk0QaLM.mjs +80 -0
- package/dist/chunks/messages-CvamFN6x.mjs +80 -0
- package/dist/chunks/messages-CwRhVVui.mjs +80 -0
- package/dist/chunks/messages-CzCezryo.mjs +80 -0
- package/dist/chunks/messages-D0v0Xa_i2.mjs +80 -0
- package/dist/chunks/messages-D3JVx_CH2.mjs +80 -0
- package/dist/chunks/messages-D4jR5Oc-.mjs +80 -0
- package/dist/chunks/messages-D7fI9Pj52.mjs +80 -0
- package/dist/chunks/messages-DGodJU2R.mjs +80 -0
- package/dist/chunks/messages-DLrmLkco2.mjs +80 -0
- package/dist/chunks/messages-DPe7kW6J.mjs +80 -0
- package/dist/chunks/messages-DRYKKPk8.mjs +80 -0
- package/dist/chunks/messages-DV5c_ZRQ.mjs +80 -0
- package/dist/chunks/messages-Dg6kSnxq.mjs +80 -0
- package/dist/chunks/messages-Dgfbmyf-.mjs +80 -0
- package/dist/chunks/messages-DihczS7L.mjs +80 -0
- package/dist/chunks/messages-DkSwQvmi2.mjs +80 -0
- package/dist/chunks/messages-Doxcj7Qy.mjs +80 -0
- package/dist/chunks/messages-DqGQvcXv2.mjs +80 -0
- package/dist/chunks/messages-Dr7yA3xM.mjs +80 -0
- package/dist/chunks/messages-DriB5lEF.mjs +80 -0
- package/dist/chunks/messages-FB_MePlt.mjs +80 -0
- package/dist/chunks/messages-JyZvGvrN.mjs +80 -0
- package/dist/chunks/messages-KdvbGwLH.mjs +80 -0
- package/dist/chunks/messages-M0HT-kBW.mjs +80 -0
- package/dist/chunks/messages-M8noQ6Kp2.mjs +80 -0
- package/dist/chunks/messages-elZUbCrN.mjs +80 -0
- package/dist/chunks/messages-iWMOMK822.mjs +80 -0
- package/dist/chunks/messages-kC92TJI72.mjs +80 -0
- package/dist/chunks/messages-tfyq1JIh2.mjs +80 -0
- package/dist/chunks/messages-v1HkA3kF2.mjs +80 -0
- package/dist/chunks/messages-yuqArCc6.mjs +80 -0
- package/dist/chunks/notifier-BqYxvxnV.mjs +96 -0
- package/dist/chunks/objectSpread2-CyPxu8-u.mjs +62 -0
- package/dist/chunks/tools-CJIETS-H.mjs +6004 -0
- package/dist/chunks/tw-DmW6-pCY.mjs +237 -0
- package/dist/cli.mjs +36 -49
- package/dist/full.mjs +26 -52
- package/dist/locales.mjs +181 -254
- package/dist/messages-2iHnlF0U.mjs +80 -0
- package/dist/messages-49ZJ_ISf.mjs +80 -0
- package/dist/messages-B8jjwMLK.mjs +80 -0
- package/dist/messages-BEDVb3ZX.mjs +80 -0
- package/dist/messages-BEEr6Vh82.mjs +80 -0
- package/dist/messages-BFT0F9pw.mjs +80 -0
- package/dist/messages-BHOI7R4K.mjs +80 -0
- package/dist/messages-BRPH_a6a.mjs +80 -0
- package/dist/messages-BSlQrYwp.mjs +80 -0
- package/dist/messages-BTNuOkhL.mjs +80 -0
- package/dist/messages-BX2KVzJp2.mjs +80 -0
- package/dist/messages-BaGwIHPb2.mjs +80 -0
- package/dist/messages-BdA_xvxj.mjs +80 -0
- package/dist/messages-BeJaje7e2.mjs +80 -0
- package/dist/messages-BfgHOkAy.mjs +80 -0
- package/dist/messages-BflWzIcP2.mjs +80 -0
- package/dist/messages-BigRnQS92.mjs +80 -0
- package/dist/messages-BjnJajTO2.mjs +80 -0
- package/dist/messages-BpA30dPf.mjs +80 -0
- package/dist/messages-BrPEPj382.mjs +80 -0
- package/dist/messages-Bt_9ptDu.mjs +80 -0
- package/dist/messages-C0cXOCHN2.mjs +80 -0
- package/dist/messages-C3tLCwJp2.mjs +80 -0
- package/dist/messages-C45IBZtA2.mjs +80 -0
- package/dist/messages-CA0hwajz.mjs +80 -0
- package/dist/messages-CCKZS2f4.mjs +80 -0
- package/dist/messages-CCm71gq3.mjs +80 -0
- package/dist/messages-CERs9LC9.mjs +80 -0
- package/dist/messages-CLQvtc_8.mjs +80 -0
- package/dist/messages-CPx1R-PH.mjs +80 -0
- package/dist/messages-CYFdbooL2.mjs +80 -0
- package/dist/messages-CYLYnOV82.mjs +80 -0
- package/dist/messages-CYZVFnaF.mjs +80 -0
- package/dist/messages-CaAdEXoh2.mjs +80 -0
- package/dist/messages-CicggErN2.mjs +80 -0
- package/dist/messages-CkAWTSc4.mjs +80 -0
- package/dist/messages-CkVfziK_2.mjs +80 -0
- package/dist/messages-CsM2iz1H2.mjs +80 -0
- package/dist/messages-D-12TeCM2.mjs +80 -0
- package/dist/messages-D0i5Vdyy2.mjs +80 -0
- package/dist/messages-D5KmRsUV2.mjs +80 -0
- package/dist/messages-DBwaWI0X.mjs +80 -0
- package/dist/messages-DDGzypb4.mjs +80 -0
- package/dist/messages-DQGzw4IC.mjs +80 -0
- package/dist/messages-DWZyaZNA.mjs +80 -0
- package/dist/messages-DYlxQEIv.mjs +80 -0
- package/dist/messages-DZo0x7Bd.mjs +80 -0
- package/dist/messages-Dc1yFFBM.mjs +80 -0
- package/dist/messages-DdUpYaJ1.mjs +80 -0
- package/dist/messages-DgstU8GH.mjs +80 -0
- package/dist/messages-DhdWq5oQ2.mjs +80 -0
- package/dist/messages-DmX52AQr.mjs +80 -0
- package/dist/messages-Dr-YJYIK2.mjs +80 -0
- package/dist/messages-DuubRyFf.mjs +80 -0
- package/dist/messages-DvTVsLOK2.mjs +80 -0
- package/dist/messages-DwPfgL_u.mjs +80 -0
- package/dist/messages-DxKIxLKw.mjs +80 -0
- package/dist/messages-DzhR8Klk.mjs +80 -0
- package/dist/messages-MBBSKGjJ2.mjs +80 -0
- package/dist/messages-RNusm48G2.mjs +80 -0
- package/dist/messages-XwPD18Kk.mjs +80 -0
- package/dist/messages-YfjdnhUF.mjs +80 -0
- package/dist/messages-aNMLsF8T2.mjs +80 -0
- package/dist/messages-cOqXp22e.mjs +80 -0
- package/dist/messages-g58itYPI.mjs +80 -0
- package/dist/messages-vfkwiKQo.mjs +80 -0
- package/dist/messages-vssmW7KO.mjs +80 -0
- package/dist/react.mjs +108 -0
- package/dist/tools.mjs +3 -7485
- package/dist/vendor.LICENSE.txt +86 -86
- package/package.json +56 -29
- package/src/blok.ts +52 -2
- package/src/components/block/api.ts +8 -0
- package/src/components/block/mutation-handler.ts +29 -4
- package/src/components/block/style-manager.ts +1 -1
- package/src/components/block-tunes/block-tune-width.ts +39 -0
- package/src/components/blocks.ts +56 -2
- package/src/components/core.ts +1 -0
- package/src/components/i18n/locales/am/messages.json +6 -1
- package/src/components/i18n/locales/ar/messages.json +6 -1
- package/src/components/i18n/locales/az/messages.json +6 -1
- package/src/components/i18n/locales/bg/messages.json +8 -3
- package/src/components/i18n/locales/bn/messages.json +6 -1
- package/src/components/i18n/locales/bs/messages.json +6 -1
- package/src/components/i18n/locales/cs/messages.json +6 -1
- package/src/components/i18n/locales/da/messages.json +6 -1
- package/src/components/i18n/locales/de/messages.json +8 -3
- package/src/components/i18n/locales/dv/messages.json +6 -1
- package/src/components/i18n/locales/el/messages.json +8 -3
- package/src/components/i18n/locales/en/messages.json +5 -0
- package/src/components/i18n/locales/es/messages.json +6 -1
- package/src/components/i18n/locales/et/messages.json +6 -1
- package/src/components/i18n/locales/fa/messages.json +8 -3
- package/src/components/i18n/locales/fi/messages.json +6 -1
- package/src/components/i18n/locales/fil/messages.json +21 -16
- package/src/components/i18n/locales/fr/messages.json +6 -1
- package/src/components/i18n/locales/gu/messages.json +6 -1
- package/src/components/i18n/locales/he/messages.json +6 -1
- package/src/components/i18n/locales/hi/messages.json +6 -1
- package/src/components/i18n/locales/hr/messages.json +6 -1
- package/src/components/i18n/locales/hu/messages.json +6 -1
- package/src/components/i18n/locales/hy/messages.json +8 -3
- package/src/components/i18n/locales/id/messages.json +12 -7
- package/src/components/i18n/locales/it/messages.json +6 -1
- package/src/components/i18n/locales/ja/messages.json +6 -1
- package/src/components/i18n/locales/ka/messages.json +6 -1
- package/src/components/i18n/locales/km/messages.json +6 -1
- package/src/components/i18n/locales/kn/messages.json +6 -1
- package/src/components/i18n/locales/ko/messages.json +6 -1
- package/src/components/i18n/locales/ku/messages.json +7 -2
- package/src/components/i18n/locales/lo/messages.json +7 -2
- package/src/components/i18n/locales/lt/messages.json +6 -1
- package/src/components/i18n/locales/lv/messages.json +6 -1
- package/src/components/i18n/locales/mk/messages.json +6 -1
- package/src/components/i18n/locales/ml/messages.json +6 -1
- package/src/components/i18n/locales/mn/messages.json +6 -1
- package/src/components/i18n/locales/mr/messages.json +6 -1
- package/src/components/i18n/locales/ms/messages.json +6 -1
- package/src/components/i18n/locales/my/messages.json +6 -1
- package/src/components/i18n/locales/ne/messages.json +9 -4
- package/src/components/i18n/locales/nl/messages.json +6 -1
- package/src/components/i18n/locales/no/messages.json +6 -1
- package/src/components/i18n/locales/pa/messages.json +6 -1
- package/src/components/i18n/locales/pl/messages.json +6 -1
- package/src/components/i18n/locales/ps/messages.json +8 -3
- package/src/components/i18n/locales/pt/messages.json +6 -1
- package/src/components/i18n/locales/ro/messages.json +6 -1
- package/src/components/i18n/locales/ru/messages.json +10 -5
- package/src/components/i18n/locales/sd/messages.json +6 -1
- package/src/components/i18n/locales/si/messages.json +6 -1
- package/src/components/i18n/locales/sk/messages.json +6 -1
- package/src/components/i18n/locales/sl/messages.json +6 -1
- package/src/components/i18n/locales/sq/messages.json +6 -1
- package/src/components/i18n/locales/sr/messages.json +6 -1
- package/src/components/i18n/locales/sv/messages.json +6 -1
- package/src/components/i18n/locales/sw/messages.json +6 -1
- package/src/components/i18n/locales/ta/messages.json +6 -1
- package/src/components/i18n/locales/te/messages.json +9 -4
- package/src/components/i18n/locales/th/messages.json +6 -1
- package/src/components/i18n/locales/tr/messages.json +6 -1
- package/src/components/i18n/locales/ug/messages.json +7 -2
- package/src/components/i18n/locales/uk/messages.json +6 -1
- package/src/components/i18n/locales/ur/messages.json +6 -1
- package/src/components/i18n/locales/vi/messages.json +6 -1
- package/src/components/i18n/locales/yi/messages.json +7 -2
- package/src/components/i18n/locales/zh/messages.json +6 -1
- package/src/components/icons/index.ts +16 -0
- package/src/components/inline-tools/inline-tool-link.ts +1 -1
- package/src/components/modules/api/blocks.ts +45 -2
- package/src/components/modules/api/index.ts +1 -0
- package/src/components/modules/api/width.ts +17 -0
- package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +99 -0
- package/src/components/modules/blockEvents/composers/markdownShortcuts.ts +109 -2
- package/src/components/modules/blockEvents/constants.ts +7 -0
- package/src/components/modules/blockManager/blockManager.ts +113 -9
- package/src/components/modules/blockManager/hierarchy.ts +61 -0
- package/src/components/modules/blockManager/operations.ts +133 -15
- package/src/components/modules/blockManager/yjs-sync.ts +112 -4
- package/src/components/modules/blockSelection.ts +36 -2
- package/src/components/modules/crossBlockSelection.ts +22 -2
- package/src/components/modules/drag/DragController.ts +178 -4
- package/src/components/modules/drag/operations/DragOperations.ts +48 -9
- package/src/components/modules/drag/preview/DragPreview.ts +21 -1
- package/src/components/modules/drag/state/DragStateMachine.ts +6 -1
- package/src/components/modules/drag/target/DropTargetDetector.ts +80 -4
- package/src/components/modules/drag/utils/ToggleSpringLoader.ts +71 -0
- package/src/components/modules/index.ts +7 -1
- package/src/components/modules/modificationsObserver.ts +19 -0
- package/src/components/modules/paste/constants.ts +2 -0
- package/src/components/modules/paste/handlers/base.ts +33 -1
- package/src/components/modules/paste/handlers/html-handler.ts +121 -54
- package/src/components/modules/paste/index.ts +5 -0
- package/src/components/modules/paste/types.ts +5 -0
- package/src/components/modules/rectangleSelection.ts +74 -81
- package/src/components/modules/toolbar/blockSettings.ts +25 -7
- package/src/components/modules/toolbar/index.ts +9 -7
- package/src/components/modules/toolbar/inline/index.ts +6 -1
- package/src/components/modules/toolbar/plus-button.ts +2 -6
- package/src/components/modules/toolbar/positioning.ts +10 -1
- package/src/components/modules/toolbar/settings-toggler.ts +1 -1
- package/src/components/modules/toolbar/styles.ts +3 -7
- package/src/components/modules/ui.ts +59 -5
- package/src/components/modules/uiControllers/handlers/click.ts +3 -2
- package/src/components/modules/widthManager.ts +69 -0
- package/src/components/modules/yjs/document-store.ts +11 -0
- package/src/components/modules/yjs/index.ts +11 -0
- package/src/components/shared/color-picker.ts +3 -3
- package/src/components/tools/block.ts +1 -11
- package/src/components/ui/toolbox.ts +52 -8
- package/src/components/utils/blocks.ts +37 -7
- package/src/components/utils/mutations.ts +2 -2
- package/src/components/utils/notifier/draw.ts +1 -1
- package/src/components/utils/placeholder.ts +5 -6
- package/src/components/utils/popover/components/popover-header/popover-header.const.ts +1 -1
- package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +4 -4
- package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +6 -6
- package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.const.ts +2 -2
- package/src/components/utils/popover/components/search-input/search-input.const.ts +2 -2
- package/src/components/utils/popover/components/search-input/search-input.ts +7 -11
- package/src/components/utils/popover/components/search-input/search-input.types.ts +149 -10
- package/src/components/utils/popover/popover-abstract.ts +3 -2
- package/src/components/utils/popover/popover-desktop.ts +133 -11
- package/src/components/utils/popover/popover-inline.ts +1 -1
- package/src/components/utils/popover/popover.const.ts +3 -3
- package/src/components/utils/shortcut.ts +2 -0
- package/src/components/utils/tooltip.ts +11 -1
- package/src/react/BlokContent.tsx +46 -0
- package/src/react/holder-map.ts +17 -0
- package/src/react/index.ts +3 -0
- package/src/react/types.ts +16 -0
- package/src/react/useBlok.ts +173 -0
- package/src/stories/Placeholder.stories.ts +0 -59
- package/src/styles/main.css +663 -52
- package/src/tools/header/header-toggle-keyboard.ts +115 -0
- package/src/tools/header/index.ts +382 -187
- package/src/tools/list/block-operations.ts +1 -1
- package/src/tools/list/caret-manager.ts +9 -12
- package/src/tools/list/index.ts +2 -6
- package/src/tools/list/list-keyboard.ts +2 -2
- package/src/tools/paragraph/index.ts +1 -1
- package/src/tools/table/index.ts +37 -3
- package/src/tools/table/table-add-controls.ts +97 -8
- package/src/tools/table/table-cell-blocks.ts +17 -8
- package/src/tools/table/table-cell-clipboard.ts +1 -1
- package/src/tools/table/table-cell-selection.ts +27 -2
- package/src/tools/table/table-operations.ts +3 -2
- package/src/tools/toggle/block-operations.ts +4 -2
- package/src/tools/toggle/constants.ts +26 -2
- package/src/tools/toggle/dom-builder.ts +90 -25
- package/src/tools/toggle/index.ts +112 -9
- package/src/tools/toggle/toggle-keyboard.ts +5 -3
- package/src/tools/toggle/toggle-lifecycle.ts +79 -7
- package/src/tools/toggle/toggle-shortcuts.ts +214 -20
- package/src/tools/toggle/types.ts +2 -0
- package/src/types-internal/blok-modules.d.ts +4 -0
- package/types/api/block.d.ts +5 -0
- package/types/api/blocks.d.ts +29 -0
- package/types/api/index.d.ts +1 -0
- package/types/api/width.d.ts +19 -0
- package/types/configs/blok-config.d.ts +33 -0
- package/types/index.d.ts +4 -0
- package/types/react.d.ts +58 -0
- package/types/utils/popover/popover.d.ts +7 -0
- package/dist/chunks/blok-B0pAWdVk.mjs +0 -20102
- package/dist/chunks/constants-DmDwNSTM.mjs +0 -5123
- package/dist/chunks/i18next-B47TKgbU.mjs +0 -1303
- package/dist/chunks/i18next-loader-v9SlYZ0i.mjs +0 -43
- package/dist/chunks/index-DHLWzZaA.mjs +0 -130
- package/dist/chunks/messages-0Pxnqd4N.mjs +0 -75
- package/dist/chunks/messages-0ZXYUq7S.mjs +0 -75
- package/dist/chunks/messages-2OD2uUDS.mjs +0 -75
- package/dist/chunks/messages-7cEMfYzh.mjs +0 -75
- package/dist/chunks/messages-8mwfda1Q.mjs +0 -75
- package/dist/chunks/messages-B-FqWsBM.mjs +0 -75
- package/dist/chunks/messages-B1jzqWiQ.mjs +0 -75
- package/dist/chunks/messages-B5wk4Ezz.mjs +0 -75
- package/dist/chunks/messages-BAZ5Ld8x.mjs +0 -75
- package/dist/chunks/messages-BBhGp198.mjs +0 -75
- package/dist/chunks/messages-BC9IjIb7.mjs +0 -75
- package/dist/chunks/messages-BFEmpeV-.mjs +0 -75
- package/dist/chunks/messages-BGqzTZy0.mjs +0 -75
- package/dist/chunks/messages-BICs1abK.mjs +0 -75
- package/dist/chunks/messages-BJX6rOnd.mjs +0 -75
- package/dist/chunks/messages-BL2bXRhN.mjs +0 -75
- package/dist/chunks/messages-BMs5qdlH.mjs +0 -75
- package/dist/chunks/messages-BRsjUNwB.mjs +0 -75
- package/dist/chunks/messages-BSqV8OUR.mjs +0 -75
- package/dist/chunks/messages-BTqu3DfG.mjs +0 -75
- package/dist/chunks/messages-BXnDEsur.mjs +0 -75
- package/dist/chunks/messages-BYcre4-6.mjs +0 -75
- package/dist/chunks/messages-BZ9LRJf-.mjs +0 -75
- package/dist/chunks/messages-BgypBy7y.mjs +0 -75
- package/dist/chunks/messages-BsuGf70G.mjs +0 -75
- package/dist/chunks/messages-BwaoF4lQ.mjs +0 -75
- package/dist/chunks/messages-C1l8_7-y.mjs +0 -75
- package/dist/chunks/messages-C5NA_r9v.mjs +0 -75
- package/dist/chunks/messages-C6zgZ5pA.mjs +0 -75
- package/dist/chunks/messages-CAo5ghFI.mjs +0 -75
- package/dist/chunks/messages-CH9qlJ9I.mjs +0 -75
- package/dist/chunks/messages-CI0HqAeS.mjs +0 -75
- package/dist/chunks/messages-CJJtms9k.mjs +0 -75
- package/dist/chunks/messages-CM2hJqk6.mjs +0 -75
- package/dist/chunks/messages-CRMiDPIQ.mjs +0 -75
- package/dist/chunks/messages-CWsZuBj1.mjs +0 -75
- package/dist/chunks/messages-C_gLHo6A.mjs +0 -75
- package/dist/chunks/messages-Cbu-NUDn.mjs +0 -75
- package/dist/chunks/messages-Cjb_MCeh.mjs +0 -75
- package/dist/chunks/messages-ClXYO9Wn.mjs +0 -75
- package/dist/chunks/messages-CsH20vhP.mjs +0 -75
- package/dist/chunks/messages-CsjAGhzA.mjs +0 -75
- package/dist/chunks/messages-Cx7VKFOE.mjs +0 -75
- package/dist/chunks/messages-D3JeBwxo.mjs +0 -75
- package/dist/chunks/messages-D541fieJ.mjs +0 -75
- package/dist/chunks/messages-D7XPdglc.mjs +0 -75
- package/dist/chunks/messages-DBhylfvt.mjs +0 -75
- package/dist/chunks/messages-DCA120lW.mjs +0 -75
- package/dist/chunks/messages-DCf_xZMN.mjs +0 -75
- package/dist/chunks/messages-DDwXKCpe.mjs +0 -75
- package/dist/chunks/messages-DNKDlxcy.mjs +0 -75
- package/dist/chunks/messages-DPvEjrGK.mjs +0 -75
- package/dist/chunks/messages-DQ-AkNxA.mjs +0 -75
- package/dist/chunks/messages-DVuvkNap.mjs +0 -75
- package/dist/chunks/messages-DaglyqUT.mjs +0 -75
- package/dist/chunks/messages-Di0bAfwA.mjs +0 -75
- package/dist/chunks/messages-DuLct0Yr.mjs +0 -75
- package/dist/chunks/messages-DzEYYhZh.mjs +0 -75
- package/dist/chunks/messages-DznNGAB2.mjs +0 -75
- package/dist/chunks/messages-DzoIzyu8.mjs +0 -75
- package/dist/chunks/messages-QYOGmket.mjs +0 -75
- package/dist/chunks/messages-cEjGDAgI.mjs +0 -75
- package/dist/chunks/messages-ddhvrdpE.mjs +0 -75
- package/dist/chunks/messages-mwfNK5nZ.mjs +0 -75
- package/dist/chunks/messages-nG_vNDte.mjs +0 -75
- package/dist/chunks/messages-tDq3Owh7.mjs +0 -75
- package/dist/chunks/messages-x6VJVZKx.mjs +0 -75
- package/dist/messages-0Pxnqd4N.mjs +0 -75
- package/dist/messages-0ZXYUq7S.mjs +0 -75
- package/dist/messages-2OD2uUDS.mjs +0 -75
- package/dist/messages-7cEMfYzh.mjs +0 -75
- package/dist/messages-8mwfda1Q.mjs +0 -75
- package/dist/messages-B-FqWsBM.mjs +0 -75
- package/dist/messages-B1jzqWiQ.mjs +0 -75
- package/dist/messages-B5wk4Ezz.mjs +0 -75
- package/dist/messages-BAZ5Ld8x.mjs +0 -75
- package/dist/messages-BBhGp198.mjs +0 -75
- package/dist/messages-BC9IjIb7.mjs +0 -75
- package/dist/messages-BFEmpeV-.mjs +0 -75
- package/dist/messages-BGqzTZy0.mjs +0 -75
- package/dist/messages-BICs1abK.mjs +0 -75
- package/dist/messages-BJX6rOnd.mjs +0 -75
- package/dist/messages-BL2bXRhN.mjs +0 -75
- package/dist/messages-BMs5qdlH.mjs +0 -75
- package/dist/messages-BRsjUNwB.mjs +0 -75
- package/dist/messages-BSqV8OUR.mjs +0 -75
- package/dist/messages-BTqu3DfG.mjs +0 -75
- package/dist/messages-BXnDEsur.mjs +0 -75
- package/dist/messages-BYcre4-6.mjs +0 -75
- package/dist/messages-BZ9LRJf-.mjs +0 -75
- package/dist/messages-BgypBy7y.mjs +0 -75
- package/dist/messages-BsuGf70G.mjs +0 -75
- package/dist/messages-BwaoF4lQ.mjs +0 -75
- package/dist/messages-C1l8_7-y.mjs +0 -75
- package/dist/messages-C5NA_r9v.mjs +0 -75
- package/dist/messages-C6zgZ5pA.mjs +0 -75
- package/dist/messages-CAo5ghFI.mjs +0 -75
- package/dist/messages-CH9qlJ9I.mjs +0 -75
- package/dist/messages-CI0HqAeS.mjs +0 -75
- package/dist/messages-CJJtms9k.mjs +0 -75
- package/dist/messages-CM2hJqk6.mjs +0 -75
- package/dist/messages-CRMiDPIQ.mjs +0 -75
- package/dist/messages-CWsZuBj1.mjs +0 -75
- package/dist/messages-C_gLHo6A.mjs +0 -75
- package/dist/messages-Cbu-NUDn.mjs +0 -75
- package/dist/messages-Cjb_MCeh.mjs +0 -75
- package/dist/messages-ClXYO9Wn.mjs +0 -75
- package/dist/messages-CsH20vhP.mjs +0 -75
- package/dist/messages-CsjAGhzA.mjs +0 -75
- package/dist/messages-Cx7VKFOE.mjs +0 -75
- package/dist/messages-D3JeBwxo.mjs +0 -75
- package/dist/messages-D541fieJ.mjs +0 -75
- package/dist/messages-D7XPdglc.mjs +0 -75
- package/dist/messages-DBhylfvt.mjs +0 -75
- package/dist/messages-DCA120lW.mjs +0 -75
- package/dist/messages-DCf_xZMN.mjs +0 -75
- package/dist/messages-DDwXKCpe.mjs +0 -75
- package/dist/messages-DNKDlxcy.mjs +0 -75
- package/dist/messages-DPvEjrGK.mjs +0 -75
- package/dist/messages-DQ-AkNxA.mjs +0 -75
- package/dist/messages-DVuvkNap.mjs +0 -75
- package/dist/messages-DaglyqUT.mjs +0 -75
- package/dist/messages-Di0bAfwA.mjs +0 -75
- package/dist/messages-DuLct0Yr.mjs +0 -75
- package/dist/messages-DzEYYhZh.mjs +0 -75
- package/dist/messages-DznNGAB2.mjs +0 -75
- package/dist/messages-DzoIzyu8.mjs +0 -75
- package/dist/messages-QYOGmket.mjs +0 -75
- package/dist/messages-cEjGDAgI.mjs +0 -75
- package/dist/messages-ddhvrdpE.mjs +0 -75
- package/dist/messages-mwfNK5nZ.mjs +0 -75
- package/dist/messages-nG_vNDte.mjs +0 -75
- package/dist/messages-tDq3Owh7.mjs +0 -75
- package/dist/messages-x6VJVZKx.mjs +0 -75
|
@@ -262,6 +262,17 @@ export class YjsManager extends Module {
|
|
|
262
262
|
this.documentStore.transact(fn, 'local');
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
+
/**
|
|
266
|
+
* Execute Yjs operations without adding them to the undo history.
|
|
267
|
+
* Uses a non-tracked origin so the UndoManager ignores these changes.
|
|
268
|
+
* Use this for auto-repair operations (e.g. ensuring empty cells have a block)
|
|
269
|
+
* that should never be undoable by the user.
|
|
270
|
+
* @param fn - Function containing Yjs operations to execute
|
|
271
|
+
*/
|
|
272
|
+
public transactWithoutCapture(fn: () => void): void {
|
|
273
|
+
this.documentStore.transactWithoutCapture(fn);
|
|
274
|
+
}
|
|
275
|
+
|
|
265
276
|
// ========== Public API: Smart Grouping ==========
|
|
266
277
|
|
|
267
278
|
/**
|
|
@@ -64,7 +64,7 @@ export interface ColorPickerHandle {
|
|
|
64
64
|
/**
|
|
65
65
|
* Base Tailwind classes shared by tab buttons
|
|
66
66
|
*/
|
|
67
|
-
const TAB_BASE_CLASSES = 'flex-1 py-1.5 text-xs text-center rounded-md cursor-pointer border-none transition-colors';
|
|
67
|
+
const TAB_BASE_CLASSES = 'flex-1 py-1.5 text-xs text-center rounded-md cursor-pointer border-none outline-hidden transition-colors';
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
70
|
* Neutral background for text-mode swatches so they render as visible buttons
|
|
@@ -139,7 +139,7 @@ export function createColorPicker(options: ColorPickerOptions): ColorPickerHandl
|
|
|
139
139
|
|
|
140
140
|
swatch.setAttribute('data-blok-testid', `${testIdPrefix}-swatch-${preset.name}`);
|
|
141
141
|
swatch.className = twMerge(
|
|
142
|
-
'w-8 h-8 rounded-md cursor-pointer border-none',
|
|
142
|
+
'w-8 h-8 rounded-md cursor-pointer border-none outline-hidden',
|
|
143
143
|
'flex items-center justify-center text-sm font-semibold',
|
|
144
144
|
'transition-shadow ring-inset hover:ring-2 hover:ring-black/10',
|
|
145
145
|
isActive && 'ring-2 ring-black/30'
|
|
@@ -169,7 +169,7 @@ export function createColorPicker(options: ColorPickerOptions): ColorPickerHandl
|
|
|
169
169
|
defaultBtn.setAttribute('data-blok-testid', `${testIdPrefix}-default-btn`);
|
|
170
170
|
defaultBtn.className = twMerge(
|
|
171
171
|
'w-full py-1.5 text-xs text-center rounded-md cursor-pointer',
|
|
172
|
-
'bg-transparent border-none hover:bg-item-hover-bg',
|
|
172
|
+
'bg-transparent border-none outline-hidden hover:bg-item-hover-bg',
|
|
173
173
|
'mt-0.5 transition-colors'
|
|
174
174
|
);
|
|
175
175
|
defaultBtn.textContent = i18n.t('tools.marker.default');
|
|
@@ -53,22 +53,12 @@ export class BlockToolAdapter extends BaseToolAdapter<ToolType.Block, IBlockTool
|
|
|
53
53
|
* @param readOnly - True if Blok is in read-only mode
|
|
54
54
|
*/
|
|
55
55
|
public create(data: BlockToolData, block: BlockAPI, readOnly: boolean): IBlockTool {
|
|
56
|
-
const toolboxEntries = this.toolbox;
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Inject merged toolbox entries into config so tools can use them in renderSettings().
|
|
60
|
-
* This allows tools like Header to show the same options in block settings as in the toolbox.
|
|
61
|
-
*/
|
|
62
|
-
const configWithToolbox = toolboxEntries !== undefined
|
|
63
|
-
? { ...this.settings, _toolboxEntries: toolboxEntries }
|
|
64
|
-
: this.settings;
|
|
65
|
-
|
|
66
56
|
return new this.constructable({
|
|
67
57
|
data,
|
|
68
58
|
block,
|
|
69
59
|
readOnly,
|
|
70
60
|
api: this.api,
|
|
71
|
-
config:
|
|
61
|
+
config: this.settings,
|
|
72
62
|
}) as IBlockTool;
|
|
73
63
|
}
|
|
74
64
|
|
|
@@ -587,6 +587,8 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
587
587
|
return;
|
|
588
588
|
}
|
|
589
589
|
|
|
590
|
+
const currentBlockParentId: string | null = currentBlock.parentId ?? null;
|
|
591
|
+
|
|
590
592
|
/**
|
|
591
593
|
* Check if the block contains only slash search text (e.g., "/head").
|
|
592
594
|
* If so, treat it as empty and replace it with the new block.
|
|
@@ -605,14 +607,33 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
605
607
|
? Object.assign(await this.api.blocks.composeBlockData(toolName), blockDataOverrides)
|
|
606
608
|
: undefined;
|
|
607
609
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
shouldReplaceBlock
|
|
615
|
-
|
|
610
|
+
/**
|
|
611
|
+
* When replacing a child block (e.g. inside a toggle), the parent-clear,
|
|
612
|
+
* insert, and parent-restore must be a single undo entry. Wrap them in
|
|
613
|
+
* a transaction so undo/redo treats the conversion atomically.
|
|
614
|
+
*/
|
|
615
|
+
const performInsert = (): BlockAPI => {
|
|
616
|
+
if (shouldReplaceBlock && currentBlockParentId !== null) {
|
|
617
|
+
this.api.blocks.setBlockParent(currentBlock.id, null);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
const inserted = this.api.blocks.insert(
|
|
621
|
+
toolName,
|
|
622
|
+
blockData,
|
|
623
|
+
undefined,
|
|
624
|
+
index,
|
|
625
|
+
undefined,
|
|
626
|
+
shouldReplaceBlock
|
|
627
|
+
);
|
|
628
|
+
|
|
629
|
+
if (currentBlockParentId !== null) {
|
|
630
|
+
this.api.blocks.setBlockParent(inserted.id, currentBlockParentId);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
return inserted;
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
const newBlock = this.insertWithTransaction(performInsert, currentBlockParentId);
|
|
616
637
|
|
|
617
638
|
this.api.caret.setToBlock(index);
|
|
618
639
|
|
|
@@ -627,6 +648,29 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|
|
627
648
|
this.api.toolbar.close({ setExplicitlyClosed: false });
|
|
628
649
|
}
|
|
629
650
|
|
|
651
|
+
/**
|
|
652
|
+
* Runs a block-insert callback inside a transaction when the block has a parent,
|
|
653
|
+
* so that parent-clear + insert + parent-restore form a single undo entry.
|
|
654
|
+
* When there is no parent (or transact is unavailable), runs the callback directly.
|
|
655
|
+
*
|
|
656
|
+
* @param fn - synchronous callback that performs the insert and returns the new BlockAPI
|
|
657
|
+
* @param parentId - the current block's parentId, or null if none
|
|
658
|
+
* @returns the BlockAPI returned by fn
|
|
659
|
+
*/
|
|
660
|
+
private insertWithTransaction(fn: () => BlockAPI, parentId: string | null): BlockAPI {
|
|
661
|
+
const result: { block: BlockAPI | undefined } = { block: undefined };
|
|
662
|
+
|
|
663
|
+
if (parentId !== null && this.api.blocks.transact !== undefined) {
|
|
664
|
+
this.api.blocks.transact(() => {
|
|
665
|
+
result.block = fn();
|
|
666
|
+
});
|
|
667
|
+
} else {
|
|
668
|
+
result.block = fn();
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
return result.block as BlockAPI;
|
|
672
|
+
}
|
|
673
|
+
|
|
630
674
|
/**
|
|
631
675
|
* Starts listening to input events on the current block for inline slash search.
|
|
632
676
|
* When the user types after "/", the toolbox filters based on the typed text.
|
|
@@ -73,6 +73,22 @@ export const getConvertibleToolsForBlock = async (block: BlockAPI, allBlockTools
|
|
|
73
73
|
return result;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Collect all data property names that appear in any toolbox entry for this tool.
|
|
78
|
+
* This lets us compare toolbox items against the current block using ALL distinguishing
|
|
79
|
+
* properties, not just the ones in a single entry.
|
|
80
|
+
*
|
|
81
|
+
* Example: header tool has entries with { level } and { level, isToggleable }.
|
|
82
|
+
* The union of keys is { level, isToggleable }, so regular headings and toggle
|
|
83
|
+
* headings are correctly treated as different block variants.
|
|
84
|
+
*/
|
|
85
|
+
const allToolboxDataKeys = new Set(
|
|
86
|
+
tool.toolbox
|
|
87
|
+
.map(item => item.data)
|
|
88
|
+
.filter((data): data is BlockToolData => data !== undefined)
|
|
89
|
+
.flatMap(data => Object.keys(data))
|
|
90
|
+
);
|
|
91
|
+
|
|
76
92
|
/** Filter out invalid toolbox entries */
|
|
77
93
|
const actualToolboxItems = tool.toolbox.filter((toolboxItem) => {
|
|
78
94
|
/**
|
|
@@ -86,10 +102,22 @@ export const getConvertibleToolsForBlock = async (block: BlockAPI, allBlockTools
|
|
|
86
102
|
|
|
87
103
|
/**
|
|
88
104
|
* When a tool has several toolbox entries, we need to make sure we do not add
|
|
89
|
-
* toolbox item with the same data to the resulting array. This helps exclude duplicates
|
|
105
|
+
* toolbox item with the same data to the resulting array. This helps exclude duplicates.
|
|
106
|
+
*
|
|
107
|
+
* We compare ALL distinguishing data properties (union of keys across all toolbox entries)
|
|
108
|
+
* to correctly differentiate variants like regular heading vs toggle heading.
|
|
90
109
|
*/
|
|
91
|
-
if (hasToolboxData && toolboxItem.data !== undefined
|
|
92
|
-
|
|
110
|
+
if (hasToolboxData && toolboxItem.data !== undefined) {
|
|
111
|
+
const wouldProduceSameBlock = [...allToolboxDataKeys].every(key => {
|
|
112
|
+
const toolboxValue = toolboxItem.data !== undefined ? toolboxItem.data[key] : undefined;
|
|
113
|
+
const blockValue = blockData[key];
|
|
114
|
+
|
|
115
|
+
return equals(toolboxValue, blockValue);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
if (wouldProduceSameBlock) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
93
121
|
}
|
|
94
122
|
|
|
95
123
|
if (!hasToolboxData && tool.name === block.name) {
|
|
@@ -99,10 +127,12 @@ export const getConvertibleToolsForBlock = async (block: BlockAPI, allBlockTools
|
|
|
99
127
|
return true;
|
|
100
128
|
});
|
|
101
129
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
130
|
+
if (actualToolboxItems.length > 0) {
|
|
131
|
+
result.push({
|
|
132
|
+
...tool,
|
|
133
|
+
toolbox: actualToolboxItems,
|
|
134
|
+
} as BlockToolAdapter);
|
|
135
|
+
}
|
|
106
136
|
|
|
107
137
|
return result;
|
|
108
138
|
}, [] as BlockToolAdapter[]);
|
|
@@ -7,9 +7,9 @@ export const isMutationBelongsToElement = (mutationRecord: MutationRecord, eleme
|
|
|
7
7
|
const { type, target, addedNodes, removedNodes } = mutationRecord;
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Skip own technical mutations, for example, data-blok-empty attribute changes
|
|
10
|
+
* Skip own technical mutations, for example, data-blok-empty or data-blok-toggle-open attribute changes
|
|
11
11
|
*/
|
|
12
|
-
if (mutationRecord.type === 'attributes' && mutationRecord.attributeName === 'data-blok-empty') {
|
|
12
|
+
if (mutationRecord.type === 'attributes' && (mutationRecord.attributeName === 'data-blok-empty' || mutationRecord.attributeName === 'data-blok-toggle-open')) {
|
|
13
13
|
return false;
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -38,7 +38,7 @@ export const CSS = {
|
|
|
38
38
|
'hover:opacity-100'
|
|
39
39
|
),
|
|
40
40
|
btnsWrapper: 'flex flex-row flex-nowrap mt-[5px]',
|
|
41
|
-
btn: 'border-none rounded-[3px] text-[13px] py-[5px] px-2.5 cursor-pointer last:ml-2.5',
|
|
41
|
+
btn: 'border-none rounded-[3px] text-[13px] py-[5px] px-2.5 cursor-pointer outline-hidden last:ml-2.5',
|
|
42
42
|
okBtn: 'bg-[#34c992] shadow-[0_1px_1px_0_rgba(18,49,35,0.05)] text-white hover:bg-[#2db583]',
|
|
43
43
|
cancelBtn: 'bg-[#f2f5f7] shadow-[0_2px_1px_0_rgba(16,19,29,0)] text-[#656b7c] hover:bg-[#e9ecee]',
|
|
44
44
|
input: twJoin(
|
|
@@ -119,8 +119,10 @@ const handleEmptyElement = (element: HTMLElement): void => {
|
|
|
119
119
|
|
|
120
120
|
/**
|
|
121
121
|
* Set up placeholder behavior for a contenteditable element.
|
|
122
|
-
* Adds focus
|
|
123
|
-
* when the element is empty.
|
|
122
|
+
* Adds a focus event listener to position the caret at the start
|
|
123
|
+
* when the element is empty. Does NOT handle caret positioning on
|
|
124
|
+
* input events — doing so would race with the browser's own editing
|
|
125
|
+
* model and destroy content mid-typing (e.g. "Hello world" → "He").
|
|
124
126
|
*
|
|
125
127
|
* @param element - The contenteditable element
|
|
126
128
|
* @param placeholder - Optional placeholder text to set
|
|
@@ -134,10 +136,7 @@ export const setupPlaceholder = (
|
|
|
134
136
|
// Always set the attribute, even if empty (for consistency and testing)
|
|
135
137
|
element.setAttribute(attributeName, placeholder ?? '');
|
|
136
138
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
element.addEventListener('focus', handler);
|
|
140
|
-
element.addEventListener('input', handler);
|
|
139
|
+
element.addEventListener('focus', () => handleEmptyElement(element));
|
|
141
140
|
};
|
|
142
141
|
|
|
143
142
|
/**
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
export const css = {
|
|
5
5
|
root: 'flex items-center mb-2 mt-1',
|
|
6
6
|
text: 'text-lg font-semibold text-text-primary',
|
|
7
|
-
backButton: 'border-0 bg-transparent w-9 h-9 text-text-primary cursor-pointer [&_svg]:block [&_svg]:w-7 [&_svg]:h-7',
|
|
7
|
+
backButton: 'border-0 bg-transparent w-9 h-9 text-text-primary cursor-pointer outline-hidden [&_svg]:block [&_svg]:w-7 [&_svg]:h-7',
|
|
8
8
|
};
|
|
@@ -10,7 +10,7 @@ export const css = {
|
|
|
10
10
|
* Note: noHover state is handled via [data-blok-popover-item-no-hover] which disables hover
|
|
11
11
|
* Priority order: active < hover < focus (focus wins when navigating with keyboard)
|
|
12
12
|
*/
|
|
13
|
-
item: 'flex items-center select-none border-none bg-transparent rounded-lg px-2 py-
|
|
13
|
+
item: 'flex items-center select-none border-none bg-transparent rounded-lg px-2 py-1 text-text-primary mb-px outline-hidden transition-[color,background-color,border-color,opacity,max-height,padding,margin] duration-150 max-h-9 overflow-hidden data-blok-popover-item-active:bg-icon-active-bg data-blok-popover-item-active:text-icon-active-text can-hover:hover:cursor-pointer can-hover:hover:bg-item-hover-bg data-blok-force-hover:cursor-pointer data-blok-force-hover:bg-item-hover-bg data-[blok-focused="true"]:bg-item-focus-bg data-blok-popover-item-no-hover:hover:bg-transparent data-blok-popover-item-no-hover:cursor-default can-hover:data-blok-popover-item-destructive:hover:text-item-destructive-text can-hover:data-blok-popover-item-destructive:hover:bg-item-destructive-hover-bg [&[data-blok-popover-item-destructive][data-blok-force-hover]]:text-item-destructive-text [&[data-blok-popover-item-destructive][data-blok-force-hover]]:bg-item-destructive-hover-bg [&[data-blok-popover-item-destructive][data-blok-focused="true"]]:text-item-destructive-text [&[data-blok-popover-item-destructive][data-blok-focused="true"]]:bg-item-destructive-hover-bg',
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Item disabled state
|
|
@@ -21,7 +21,7 @@ export const css = {
|
|
|
21
21
|
/**
|
|
22
22
|
* Icon container styles
|
|
23
23
|
*/
|
|
24
|
-
icon: 'flex items-center justify-center w-
|
|
24
|
+
icon: 'flex items-center justify-center w-7 h-7 shrink-0 rounded-lg bg-popover-icon-bg transition-colors duration-150 [&_svg]:w-icon [&_svg]:h-icon',
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Focused state class for DomIterator/Flipper keyboard navigation.
|
|
@@ -37,7 +37,7 @@ export const cssInline = {
|
|
|
37
37
|
/**
|
|
38
38
|
* Item in inline context - more compact styling
|
|
39
39
|
*/
|
|
40
|
-
item: 'rounded
|
|
40
|
+
item: 'rounded p-1',
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -47,5 +47,5 @@ export const cssNestedInline = {
|
|
|
47
47
|
/**
|
|
48
48
|
* Nested item - back to desktop popover styling
|
|
49
49
|
*/
|
|
50
|
-
item: 'rounded
|
|
50
|
+
item: 'rounded p-[3px] mobile:p-1',
|
|
51
51
|
};
|
|
@@ -258,8 +258,8 @@ export class PopoverItemDefault extends PopoverItem {
|
|
|
258
258
|
const titleEl = document.createElement('div');
|
|
259
259
|
|
|
260
260
|
titleEl.className = params.secondaryLabel
|
|
261
|
-
? 'grow whitespace-nowrap text-
|
|
262
|
-
: 'mr-auto whitespace-nowrap text-
|
|
261
|
+
? 'grow whitespace-nowrap text-[13px] font-medium leading-5'
|
|
262
|
+
: 'mr-auto whitespace-nowrap text-[13px] font-medium leading-5';
|
|
263
263
|
titleEl.setAttribute(DATA_ATTR.popoverItemTitle, '');
|
|
264
264
|
titleEl.setAttribute('data-blok-testid', 'popover-item-title');
|
|
265
265
|
titleEl.textContent = title;
|
|
@@ -272,7 +272,7 @@ export class PopoverItemDefault extends PopoverItem {
|
|
|
272
272
|
if (params.secondaryLabel) {
|
|
273
273
|
const secondaryEl = document.createElement('div');
|
|
274
274
|
|
|
275
|
-
secondaryEl.className = 'ml-auto shrink-0 flex items-center whitespace-nowrap pl-20 text-[
|
|
275
|
+
secondaryEl.className = 'ml-auto shrink-0 flex items-center whitespace-nowrap pl-20 font-mono text-[13px] tracking-wide text-text-secondary/60';
|
|
276
276
|
secondaryEl.setAttribute(DATA_ATTR.popoverItemSecondaryTitle, '');
|
|
277
277
|
secondaryEl.setAttribute('data-blok-testid', 'popover-item-secondary-title');
|
|
278
278
|
secondaryEl.textContent = params.secondaryLabel;
|
|
@@ -326,7 +326,7 @@ export class PopoverItemDefault extends PopoverItem {
|
|
|
326
326
|
|
|
327
327
|
return twMerge(
|
|
328
328
|
css.item,
|
|
329
|
-
!isInline && !isNestedInline && 'pl-2 pr-
|
|
329
|
+
!isInline && !isNestedInline && 'pl-2 pr-3',
|
|
330
330
|
isInline && cssInline.item,
|
|
331
331
|
isNestedInline && cssNestedInline.item,
|
|
332
332
|
this.params.isDisabled && css.itemDisabled
|
|
@@ -342,8 +342,7 @@ export class PopoverItemDefault extends PopoverItem {
|
|
|
342
342
|
isInline && 'w-auto h-auto [&_svg]:w-icon [&_svg]:h-icon mobile:[&_svg]:w-icon-mobile mobile:[&_svg]:h-icon-mobile',
|
|
343
343
|
isNestedInline && 'w-toolbox-btn h-toolbox-btn',
|
|
344
344
|
iconWithGap && 'mr-3',
|
|
345
|
-
iconWithGap &&
|
|
346
|
-
iconWithGap && isInline && 'shadow-none bg-transparent mr-0!',
|
|
345
|
+
iconWithGap && isInline && 'shadow-none mr-0!',
|
|
347
346
|
iconWithGap && isNestedInline && 'mr-2!',
|
|
348
347
|
isWobbling && 'animate-wobble'
|
|
349
348
|
);
|
|
@@ -355,6 +354,7 @@ export class PopoverItemDefault extends PopoverItem {
|
|
|
355
354
|
private getChevronClass(isInline: boolean): string {
|
|
356
355
|
return twMerge(
|
|
357
356
|
css.icon,
|
|
357
|
+
'w-5 h-5 bg-transparent',
|
|
358
358
|
isInline && 'rotate-90'
|
|
359
359
|
);
|
|
360
360
|
}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Tailwind CSS classes for popover separator component
|
|
3
3
|
*/
|
|
4
4
|
export const css = {
|
|
5
|
-
container: 'py-1 px-
|
|
5
|
+
container: 'py-1.5 px-2 transition-[opacity,max-height,padding] duration-150 max-h-5 overflow-hidden',
|
|
6
6
|
containerHidden: 'opacity-0 max-h-0! py-0!',
|
|
7
|
-
line: 'h-px w-full bg-popover-border',
|
|
7
|
+
line: 'h-px w-full bg-popover-border/60',
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* CSS class names to be used in popover search input class
|
|
3
3
|
*/
|
|
4
4
|
export const css = {
|
|
5
|
-
wrapper: 'bg-
|
|
5
|
+
wrapper: 'bg-search-input-bg border border-search-input-border rounded-lg p-1 grid grid-cols-[auto_auto_1fr] grid-rows-[auto] transition-all duration-200 focus-within:bg-popover-bg focus-within:border-search-input-focus-border focus-within:shadow-[0_0_0_2px_rgba(35,131,226,0.08)]',
|
|
6
6
|
icon: 'w-toolbox-btn h-toolbox-btn flex items-center justify-center mr-2 [&_svg]:w-icon [&_svg]:h-icon [&_svg]:text-gray-text',
|
|
7
|
-
input: "text-sm outline-hidden font-medium font-inherit border-0 bg-transparent m-0 p-0 leading-[22px] min-w-[calc(100%-(--spacing(6))-10px)] placeholder:text-gray-text placeholder:font-
|
|
7
|
+
input: "text-sm outline-hidden font-medium font-inherit border-0 bg-transparent m-0 p-0 leading-[22px] min-w-[calc(100%-(--spacing(6))-10px)] placeholder:text-gray-text/60 placeholder:font-normal",
|
|
8
8
|
};
|
|
@@ -5,7 +5,7 @@ import { Listeners } from '../../../listeners';
|
|
|
5
5
|
|
|
6
6
|
import { css } from './search-input.const';
|
|
7
7
|
import type { SearchInputEventMap, SearchableItem } from './search-input.types';
|
|
8
|
-
import { SearchInputEvent,
|
|
8
|
+
import { SearchInputEvent, scoreSearchMatch } from './search-input.types';
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -167,17 +167,13 @@ export class SearchInput extends EventsDispatcher<SearchInputEventMap> {
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
/**
|
|
170
|
-
* Returns list of found items for the current search query
|
|
170
|
+
* Returns list of found items for the current search query, sorted by relevance
|
|
171
171
|
*/
|
|
172
172
|
private get foundItems(): SearchableItem[] {
|
|
173
|
-
return this.items
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
* @param item - item to be checked
|
|
179
|
-
*/
|
|
180
|
-
private checkItem(item: SearchableItem): boolean {
|
|
181
|
-
return matchesSearchQuery(item, this.searchQuery);
|
|
173
|
+
return this.items
|
|
174
|
+
.map(item => ({ item, score: scoreSearchMatch(item, this.searchQuery) }))
|
|
175
|
+
.filter(({ score }) => score > 0)
|
|
176
|
+
.sort((a, b) => b.score - a.score)
|
|
177
|
+
.map(({ item }) => item);
|
|
182
178
|
}
|
|
183
179
|
}
|
|
@@ -18,24 +18,163 @@ export interface SearchableItem {
|
|
|
18
18
|
searchTerms?: string[];
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Standard Levenshtein distance using single-row optimization.
|
|
23
|
+
* @param a - first string
|
|
24
|
+
* @param b - second string
|
|
25
|
+
* @returns edit distance between a and b
|
|
26
|
+
*/
|
|
27
|
+
const levenshteinDistance = (a: string, b: string): number => {
|
|
28
|
+
if (a.length === 0) {
|
|
29
|
+
return b.length;
|
|
30
|
+
}
|
|
31
|
+
if (b.length === 0) {
|
|
32
|
+
return a.length;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Two-row DP approach: prevRow holds distances for the previous character of `a`,
|
|
37
|
+
* currRow is computed for the current character, then they swap.
|
|
38
|
+
*/
|
|
39
|
+
const prevRow = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
40
|
+
const currRow = new Array<number>(b.length + 1);
|
|
41
|
+
|
|
42
|
+
for (const [idx, char] of [...a].entries()) {
|
|
43
|
+
currRow[0] = idx + 1;
|
|
44
|
+
|
|
45
|
+
for (const [jIdx, bChar] of [...b].entries()) {
|
|
46
|
+
const j = jIdx + 1;
|
|
47
|
+
const cost = char === bChar ? 0 : 1;
|
|
48
|
+
|
|
49
|
+
currRow[j] = Math.min(
|
|
50
|
+
prevRow[j] + 1, // deletion
|
|
51
|
+
currRow[j - 1] + 1, // insertion
|
|
52
|
+
prevRow[j - 1] + cost // substitution
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Copy currRow into prevRow for the next iteration */
|
|
57
|
+
prevRow.splice(0, prevRow.length, ...currRow);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return prevRow[b.length];
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Checks if query characters appear in order in target (subsequence match).
|
|
65
|
+
* @param target - string to search in
|
|
66
|
+
* @param query - characters to find in order
|
|
67
|
+
* @returns true if query is a subsequence of target
|
|
68
|
+
*/
|
|
69
|
+
const isSubsequence = (target: string, query: string): boolean => {
|
|
70
|
+
const matched = [...target].reduce(
|
|
71
|
+
(qi, char) => (qi < query.length && char === query[qi] ? qi + 1 : qi),
|
|
72
|
+
0
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return matched === query.length;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Checks if query characters match at word boundaries in target.
|
|
80
|
+
* Word boundaries are: start of string, after space, hyphen, or underscore.
|
|
81
|
+
* @param target - string to search in
|
|
82
|
+
* @param query - characters to match at word boundaries
|
|
83
|
+
* @returns true if each query char matches a word-boundary character in order
|
|
84
|
+
*/
|
|
85
|
+
const matchesWordBoundaries = (target: string, query: string): boolean => {
|
|
86
|
+
const matched = [...target].reduce(
|
|
87
|
+
(qi, char, ti) => {
|
|
88
|
+
const isWordBoundary = ti === 0 || target[ti - 1] === ' ' || target[ti - 1] === '-' || target[ti - 1] === '_';
|
|
89
|
+
|
|
90
|
+
return qi < query.length && isWordBoundary && char === query[qi] ? qi + 1 : qi;
|
|
91
|
+
},
|
|
92
|
+
0
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
return matched === query.length;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Scores how well a single string matches a lowercased query.
|
|
100
|
+
* @param target - string to score against (already lowercased)
|
|
101
|
+
* @param lowerQuery - search query (already lowercased)
|
|
102
|
+
* @returns score: 100 (exact) > 90 (prefix) > 75 (substring) > 55 (word-boundary) > 35 (subsequence) > 15 (typo) > 0
|
|
103
|
+
*/
|
|
104
|
+
const scoreString = (target: string, lowerQuery: string): number => {
|
|
105
|
+
if (target === lowerQuery) {
|
|
106
|
+
return 100;
|
|
107
|
+
}
|
|
108
|
+
if (target.startsWith(lowerQuery)) {
|
|
109
|
+
return 90;
|
|
110
|
+
}
|
|
111
|
+
if (target.includes(lowerQuery)) {
|
|
112
|
+
return 75;
|
|
113
|
+
}
|
|
114
|
+
if (lowerQuery.length >= 2 && matchesWordBoundaries(target, lowerQuery)) {
|
|
115
|
+
return 55;
|
|
116
|
+
}
|
|
117
|
+
if (lowerQuery.length >= 2 && isSubsequence(target, lowerQuery)) {
|
|
118
|
+
return 35;
|
|
119
|
+
}
|
|
120
|
+
if (lowerQuery.length >= 3) {
|
|
121
|
+
const maxDist = Math.floor(lowerQuery.length / 3);
|
|
122
|
+
const dist = levenshteinDistance(target, lowerQuery);
|
|
123
|
+
|
|
124
|
+
if (dist <= maxDist) {
|
|
125
|
+
return 15;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return 0;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Scores how well an item matches a search query.
|
|
134
|
+
* Returns the best score across title, englishTitle, and searchTerms.
|
|
135
|
+
* @param item - item to score
|
|
136
|
+
* @param query - search query (case-insensitive)
|
|
137
|
+
* @returns score from 0 (no match) to 100 (exact match). Empty query returns 100.
|
|
138
|
+
*/
|
|
139
|
+
export const scoreSearchMatch = (item: SearchableItem, query: string): number => {
|
|
140
|
+
if (query === '') {
|
|
141
|
+
return 100;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const lowerQuery = query.toLowerCase();
|
|
145
|
+
const candidates: string[] = [];
|
|
146
|
+
|
|
147
|
+
if (item.title !== undefined) {
|
|
148
|
+
candidates.push(item.title.toLowerCase());
|
|
149
|
+
}
|
|
150
|
+
if (item.englishTitle !== undefined) {
|
|
151
|
+
candidates.push(item.englishTitle.toLowerCase());
|
|
152
|
+
}
|
|
153
|
+
if (item.searchTerms !== undefined) {
|
|
154
|
+
for (const term of item.searchTerms) {
|
|
155
|
+
candidates.push(term.toLowerCase());
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const best = candidates.reduce((max, candidate) => {
|
|
160
|
+
const score = scoreString(candidate, lowerQuery);
|
|
161
|
+
|
|
162
|
+
return score > max ? score : max;
|
|
163
|
+
}, 0);
|
|
164
|
+
|
|
165
|
+
return best;
|
|
166
|
+
};
|
|
167
|
+
|
|
21
168
|
/**
|
|
22
169
|
* Checks if an item matches a search query.
|
|
170
|
+
* Uses fuzzy matching: exact, prefix, substring, subsequence, and typo-tolerant (Levenshtein).
|
|
23
171
|
* Matches against: displayed title, English title, and search term aliases.
|
|
24
172
|
* @param item - item to check
|
|
25
173
|
* @param query - search query (case-insensitive)
|
|
26
174
|
* @returns true if the item matches the query
|
|
27
175
|
*/
|
|
28
176
|
export const matchesSearchQuery = (item: SearchableItem, query: string): boolean => {
|
|
29
|
-
|
|
30
|
-
const title = item.title?.toLowerCase() ?? '';
|
|
31
|
-
const englishTitle = item.englishTitle?.toLowerCase() ?? '';
|
|
32
|
-
const searchTerms = item.searchTerms ?? [];
|
|
33
|
-
|
|
34
|
-
return (
|
|
35
|
-
title.includes(lowerQuery) ||
|
|
36
|
-
englishTitle.includes(lowerQuery) ||
|
|
37
|
-
searchTerms.some(term => term.toLowerCase().includes(lowerQuery))
|
|
38
|
-
);
|
|
177
|
+
return scoreSearchMatch(item, query) > 0;
|
|
39
178
|
};
|
|
40
179
|
|
|
41
180
|
/**
|
|
@@ -452,7 +452,7 @@ export abstract class PopoverAbstract<Nodes extends PopoverNodes = PopoverNodes>
|
|
|
452
452
|
// Set CSS variables
|
|
453
453
|
popover.style.setProperty('--width', this.params.width ?? 'auto');
|
|
454
454
|
popover.style.setProperty('--item-padding', '4px');
|
|
455
|
-
popover.style.setProperty('--item-height', 'calc(1.
|
|
455
|
+
popover.style.setProperty('--item-height', 'calc(1.75rem + 2 * var(--item-padding))');
|
|
456
456
|
popover.style.setProperty('--popover-top', 'calc(100% + 0.5rem)');
|
|
457
457
|
popover.style.setProperty('--popover-left', '0');
|
|
458
458
|
popover.style.setProperty('--nested-popover-overlap', '0.25rem');
|
|
@@ -461,13 +461,14 @@ export abstract class PopoverAbstract<Nodes extends PopoverNodes = PopoverNodes>
|
|
|
461
461
|
// Create popover container
|
|
462
462
|
const popoverContainer = document.createElement('div');
|
|
463
463
|
popoverContainer.className = css.popoverContainer;
|
|
464
|
+
popoverContainer.style.boxShadow = '0 0 0 1px var(--blok-popover-border), 0 4px 16px rgba(0, 0, 0, 0.1), 0 16px 40px -8px rgba(0, 0, 0, 0.08)';
|
|
464
465
|
popoverContainer.setAttribute(DATA_ATTR.popoverContainer, '');
|
|
465
466
|
popoverContainer.setAttribute('data-blok-testid', 'popover-container');
|
|
466
467
|
|
|
467
468
|
// Create nothing found message
|
|
468
469
|
const nothingFoundMessage = document.createElement('div');
|
|
469
470
|
nothingFoundMessage.className = twMerge(
|
|
470
|
-
'cursor-default text-
|
|
471
|
+
'cursor-default text-[13px] leading-5 font-normal whitespace-nowrap overflow-hidden text-ellipsis text-gray-text/70 px-3 py-4 text-center',
|
|
471
472
|
'hidden'
|
|
472
473
|
);
|
|
473
474
|
nothingFoundMessage.setAttribute('data-blok-testid', 'popover-nothing-found');
|