@@ -552,6 +552,7 @@ import type { RealPolicySettingCategory } from '../settings/realTypes'
552552import PolicyRuleEditorPanel from ' ../PolicyRuleEditorPanel.vue'
553553import { createRealPolicyWorkbenchState } from ' ../useRealPolicyWorkbench'
554554import { useCatalogState } from ' ./composables/useCatalogState'
555+ import { useCatalogInteractions } from ' ./composables/useCatalogInteractions'
555556import { useNavigation } from ' ./composables/useNavigation'
556557
557558defineOptions ({
@@ -571,9 +572,6 @@ const selectedCreateScope = ref<'system' | 'group' | 'user' | null>(null)
571572const isRemovingRule = ref (false )
572573const removalFeedback = ref <string | null >(null )
573574const removalFeedbackTimeout = ref <number | null >(null )
574- const lastPress = ref <{ surface: ' cards' | ' list' , key: string , x: number , y: number } | null >(null )
575- const recentSelectionGesture = ref <{ surface: ' cards' | ' list' , key: string , at: number } | null >(null )
576- const clearCatalogFocusOnClose = ref (false )
577575const openRuleActionsKey = ref <string | null >(null )
578576const crudSearch = ref (' ' )
579577const crudScopeFilter = ref <' all' | ' system' | ' group' | ' user' >(' all' )
@@ -583,10 +581,20 @@ const isRtl = ref(false)
583581
584582// Initialize catalog state composable
585583const catalogState = useCatalogState ()
584+ const {
585+ clearCatalogFocusOnClose,
586+ markSelectionGesture,
587+ trackPress,
588+ openSettingFromPointer,
589+ openSettingFromAction,
590+ openSettingFromKeyboard,
591+ highlightText,
592+ } = useCatalogInteractions ({
593+ getFilter : () => catalogState .settingsFilter .value ,
594+ onOpenSetting : (key ) => state .openSetting (key as never ),
595+ })
586596
587597const CRUD_PAGE_SIZE = 20
588- const DRAG_OPEN_THRESHOLD_PX = 6
589- const SELECTION_GUARD_WINDOW_MS = 400
590598const REMOVAL_FEEDBACK_DURATION_MS = 6000
591599
592600const CATEGORY_ORDER: RealPolicySettingCategory [] = [
@@ -1217,121 +1225,6 @@ function requestBackToCreateScope() {
12171225 selectedCreateScope .value = null
12181226}
12191227
1220- function markSelectionGesture(surface : ' cards' | ' list' , key : string ) {
1221- if (! hasActiveTextSelection ()) {
1222- return
1223- }
1224-
1225- recentSelectionGesture .value = {
1226- surface ,
1227- key ,
1228- at: Date .now (),
1229- }
1230- }
1231-
1232- function shouldIgnoreDueToRecentSelection(surface : ' cards' | ' list' , key : string ) {
1233- const gesture = recentSelectionGesture .value
1234- if (! gesture ) {
1235- return false
1236- }
1237-
1238- const expired = (Date .now () - gesture .at ) > SELECTION_GUARD_WINDOW_MS
1239- const matchesTarget = gesture .surface === surface && gesture .key === key
1240- if (expired || ! matchesTarget ) {
1241- return false
1242- }
1243-
1244- recentSelectionGesture .value = null
1245- return true
1246- }
1247-
1248- function isPlainPrimaryClick(event : MouseEvent ) {
1249- const button = typeof event .button === ' number' ? event .button : 0
1250- const hasModifier = Boolean (event .metaKey || event .ctrlKey || event .shiftKey || event .altKey )
1251- return button === 0 && ! hasModifier
1252- }
1253-
1254- function trackPress(surface : ' cards' | ' list' , key : string , event : PointerEvent ) {
1255- if (event .button !== 0 ) {
1256- lastPress .value = null
1257- return
1258- }
1259-
1260- lastPress .value = {
1261- surface ,
1262- key ,
1263- x: event .clientX ,
1264- y: event .clientY ,
1265- }
1266- }
1267-
1268- function movedBeyondThreshold(event : MouseEvent , press : { x: number , y: number }) {
1269- const deltaX = Math .abs (event .clientX - press .x )
1270- const deltaY = Math .abs (event .clientY - press .y )
1271- return deltaX > DRAG_OPEN_THRESHOLD_PX || deltaY > DRAG_OPEN_THRESHOLD_PX
1272- }
1273-
1274- function openSettingFromPointer(surface : ' cards' | ' list' , key : string , event : MouseEvent ) {
1275- if (! isPlainPrimaryClick (event )) {
1276- return
1277- }
1278-
1279- if (shouldIgnoreDueToRecentSelection (surface , key )) {
1280- return
1281- }
1282-
1283- if (hasActiveTextSelection ()) {
1284- return
1285- }
1286-
1287- const press = lastPress .value
1288- if (press && press .surface === surface && press .key === key && movedBeyondThreshold (event , press )) {
1289- return
1290- }
1291-
1292- clearCatalogFocusOnClose .value = true
1293- state .openSetting (key as never )
1294- }
1295-
1296- function openSettingFromAction(key : string , event : MouseEvent ) {
1297- clearCatalogFocusOnClose .value = event .detail > 0
1298- state .openSetting (key as never )
1299- }
1300-
1301- function openSettingFromKeyboard(key : string ) {
1302- clearCatalogFocusOnClose .value = false
1303- state .openSetting (key as never )
1304- }
1305-
1306- function hasActiveTextSelection() {
1307- const selection = window .getSelection ()
1308- return !! selection && selection .type === ' Range' && selection .toString ().trim ().length > 0
1309- }
1310-
1311- function escapeRegExp(value : string ) {
1312- return value .replace (/ [. *+?^${}()|[\]\\ ] / g , ' \\ $&' )
1313- }
1314-
1315- function escapeHtml(value : string ) {
1316- return value
1317- .replaceAll (' &' , ' &' )
1318- .replaceAll (' <' , ' <' )
1319- .replaceAll (' >' , ' >' )
1320- .replaceAll (' "' , ' "' )
1321- .replaceAll (" '" , ' '' )
1322- }
1323-
1324- function highlightText(value : string ) {
1325- const query = catalogState .settingsFilter .value .trim ()
1326- const safeValue = escapeHtml (value )
1327- if (! query ) {
1328- return safeValue
1329- }
1330-
1331- const matcher = new RegExp (` (${escapeRegExp (query )}) ` , ' ig' )
1332- return safeValue .replace (matcher , ' <mark>$1</mark>' )
1333- }
1334-
13351228function hasActiveOverrides(groupCount : number , userCount : number ) {
13361229 return groupCount > 0 || userCount > 0
13371230}
0 commit comments