or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

compound-components.mdindex.mdmain-component.mdstore-management.mdutilities.md

utilities.mddocs/

0

# Utility Functions

1

2

Mantine Spotlight provides utility functions for type checking and custom filter implementations to support advanced use cases.

3

4

## Capabilities

5

6

### Type Guards

7

8

Type guard functions for distinguishing between different action data structures.

9

10

```typescript { .api }

11

/**

12

* Checks if an item is an actions group rather than a single action

13

* @param item - The item to check (action or group)

14

* @returns Type predicate indicating if item is SpotlightActionGroupData

15

*/

16

function isActionsGroup(

17

item: SpotlightActionData | SpotlightActionGroupData

18

): item is SpotlightActionGroupData;

19

```

20

21

**Usage Example:**

22

23

```typescript

24

import { isActionsGroup } from "@mantine/spotlight";

25

26

function processActions(actions: SpotlightActions[]) {

27

actions.forEach(item => {

28

if (isActionsGroup(item)) {

29

console.log(`Group: ${item.group} has ${item.actions.length} actions`);

30

item.actions.forEach(action => {

31

console.log(` - ${action.label}`);

32

});

33

} else {

34

console.log(`Action: ${item.label}`);

35

}

36

});

37

}

38

```

39

40

### Filter Functions

41

42

The Spotlight component uses an internal default filter function that provides smart searching across action labels, descriptions, and keywords. This function is not exported from the package but you can create custom filter functions with similar behavior.

43

44

```typescript { .api }

45

type SpotlightFilterFunction = (

46

query: string,

47

actions: SpotlightActions[]

48

) => SpotlightActions[];

49

```

50

51

**Custom Filter Examples:**

52

53

```typescript

54

import { SpotlightFilterFunction, isActionsGroup } from "@mantine/spotlight";

55

56

// Simple filter example

57

const simpleFilter: SpotlightFilterFunction = (query, actions) => {

58

if (!query.trim()) return actions;

59

60

return actions.filter(action => {

61

if (isActionsGroup(action)) {

62

// Filter group actions

63

const filteredActions = action.actions.filter(subAction =>

64

subAction.label?.toLowerCase().includes(query.toLowerCase())

65

);

66

return filteredActions.length > 0;

67

} else {

68

// Filter individual action

69

return action.label?.toLowerCase().includes(query.toLowerCase());

70

}

71

});

72

};

73

74

// Priority-based filter (similar to internal default)

75

const priorityFilter: SpotlightFilterFunction = (query, actions) => {

76

if (!query.trim()) return actions;

77

78

const queryLower = query.toLowerCase();

79

const labelMatches: SpotlightActions[] = [];

80

const descriptionMatches: SpotlightActions[] = [];

81

82

actions.forEach(action => {

83

if (isActionsGroup(action)) {

84

const labelMatched = action.actions.filter(subAction =>

85

subAction.label?.toLowerCase().includes(queryLower)

86

);

87

const descMatched = action.actions.filter(subAction =>

88

subAction.description?.toLowerCase().includes(queryLower) ||

89

(typeof subAction.keywords === 'string' &&

90

subAction.keywords.toLowerCase().includes(queryLower)) ||

91

(Array.isArray(subAction.keywords) &&

92

subAction.keywords.some(k => k.toLowerCase().includes(queryLower)))

93

);

94

95

if (labelMatched.length > 0) {

96

labelMatches.push({ ...action, actions: labelMatched });

97

} else if (descMatched.length > 0) {

98

descriptionMatches.push({ ...action, actions: descMatched });

99

}

100

} else {

101

if (action.label?.toLowerCase().includes(queryLower)) {

102

labelMatches.push(action);

103

} else if (

104

action.description?.toLowerCase().includes(queryLower) ||

105

(typeof action.keywords === 'string' &&

106

action.keywords.toLowerCase().includes(queryLower)) ||

107

(Array.isArray(action.keywords) &&

108

action.keywords.some(k => k.toLowerCase().includes(queryLower)))

109

) {

110

descriptionMatches.push(action);

111

}

112

}

113

});

114

115

return [...labelMatches, ...descriptionMatches];

116

};

117

```

118

119

### Action Data Types

120

121

Type definitions for working with actions and action groups.

122

123

```typescript { .api }

124

interface SpotlightActionData extends SpotlightActionProps {

125

/** Unique identifier for the action */

126

id: string;

127

/** Optional group name for organizing actions */

128

group?: string;

129

}

130

131

interface SpotlightActionGroupData {

132

/** Group label displayed in the interface */

133

group: string;

134

/** Array of actions belonging to this group */

135

actions: SpotlightActionData[];

136

}

137

138

type SpotlightActions = SpotlightActionData | SpotlightActionGroupData;

139

```

140

141

## Advanced Filter Implementation

142

143

You can create custom filter functions for specialized search behavior:

144

145

```typescript

146

import { SpotlightFilterFunction, isActionsGroup } from "@mantine/spotlight";

147

148

// Fuzzy search filter

149

const fuzzyFilter: SpotlightFilterFunction = (query, actions) => {

150

if (!query.trim()) return actions;

151

152

const fuzzyMatch = (text: string, query: string): boolean => {

153

const queryLower = query.toLowerCase();

154

const textLower = text.toLowerCase();

155

let queryIndex = 0;

156

157

for (let i = 0; i < textLower.length && queryIndex < queryLower.length; i++) {

158

if (textLower[i] === queryLower[queryIndex]) {

159

queryIndex++;

160

}

161

}

162

163

return queryIndex === queryLower.length;

164

};

165

166

return actions.filter(action => {

167

if (isActionsGroup(action)) {

168

const filteredActions = action.actions.filter(subAction =>

169

fuzzyMatch(subAction.label || "", query) ||

170

fuzzyMatch(subAction.description || "", query)

171

);

172

if (filteredActions.length > 0) {

173

return { ...action, actions: filteredActions };

174

}

175

return false;

176

} else {

177

return fuzzyMatch(action.label || "", query) ||

178

fuzzyMatch(action.description || "", query);

179

}

180

});

181

};

182

183

// Weighted search filter

184

const weightedFilter: SpotlightFilterFunction = (query, actions) => {

185

if (!query.trim()) return actions;

186

187

const queryLower = query.toLowerCase();

188

const scoredActions: Array<{ action: SpotlightActions; score: number }> = [];

189

190

actions.forEach(action => {

191

if (isActionsGroup(action)) {

192

let groupScore = 0;

193

const filteredActions = action.actions.filter(subAction => {

194

let score = 0;

195

if (subAction.label?.toLowerCase().includes(queryLower)) score += 10;

196

if (subAction.description?.toLowerCase().includes(queryLower)) score += 5;

197

if (typeof subAction.keywords === 'string' &&

198

subAction.keywords.toLowerCase().includes(queryLower)) score += 3;

199

if (Array.isArray(subAction.keywords) &&

200

subAction.keywords.some(k => k.toLowerCase().includes(queryLower))) score += 3;

201

202

if (score > 0) groupScore += score;

203

return score > 0;

204

});

205

206

if (filteredActions.length > 0) {

207

scoredActions.push({

208

action: { ...action, actions: filteredActions },

209

score: groupScore

210

});

211

}

212

} else {

213

let score = 0;

214

if (action.label?.toLowerCase().includes(queryLower)) score += 10;

215

if (action.description?.toLowerCase().includes(queryLower)) score += 5;

216

if (typeof action.keywords === 'string' &&

217

action.keywords.toLowerCase().includes(queryLower)) score += 3;

218

if (Array.isArray(action.keywords) &&

219

action.keywords.some(k => k.toLowerCase().includes(queryLower))) score += 3;

220

221

if (score > 0) {

222

scoredActions.push({ action, score });

223

}

224

}

225

});

226

227

// Sort by score (highest first) and return actions

228

return scoredActions

229

.sort((a, b) => b.score - a.score)

230

.map(item => item.action);

231

};

232

```

233

234

## Action Data Utilities

235

236

Helper functions for working with action data structures:

237

238

```typescript

239

import { SpotlightActions, isActionsGroup } from "@mantine/spotlight";

240

241

// Flatten grouped actions into a single array

242

function flattenActions(actions: SpotlightActions[]): SpotlightActionData[] {

243

const flattened: SpotlightActionData[] = [];

244

245

actions.forEach(action => {

246

if (isActionsGroup(action)) {

247

flattened.push(...action.actions);

248

} else {

249

flattened.push(action);

250

}

251

});

252

253

return flattened;

254

}

255

256

// Group individual actions by their group property

257

function groupActions(actions: SpotlightActionData[]): SpotlightActions[] {

258

const groups: Record<string, SpotlightActionData[]> = {};

259

const ungrouped: SpotlightActionData[] = [];

260

261

actions.forEach(action => {

262

if (action.group) {

263

if (!groups[action.group]) {

264

groups[action.group] = [];

265

}

266

groups[action.group].push(action);

267

} else {

268

ungrouped.push(action);

269

}

270

});

271

272

const result: SpotlightActions[] = [];

273

274

// Add grouped actions

275

Object.entries(groups).forEach(([group, groupActions]) => {

276

result.push({ group, actions: groupActions });

277

});

278

279

// Add ungrouped actions

280

result.push(...ungrouped);

281

282

return result;

283

}

284

285

// Count total actions including those in groups

286

function countActions(actions: SpotlightActions[]): number {

287

return actions.reduce((count, action) => {

288

if (isActionsGroup(action)) {

289

return count + action.actions.length;

290

} else {

291

return count + 1;

292

}

293

}, 0);

294

}

295

```

296

297

## Filter Performance Optimization

298

299

For large action datasets, consider implementing optimized filtering:

300

301

```typescript

302

import { SpotlightFilterFunction } from "@mantine/spotlight";

303

304

// Memoized filter for better performance

305

function createMemoizedFilter(baseFilter: SpotlightFilterFunction): SpotlightFilterFunction {

306

const cache = new Map<string, SpotlightActions[]>();

307

308

return (query: string, actions: SpotlightActions[]) => {

309

const cacheKey = `${query}_${actions.length}`;

310

311

if (cache.has(cacheKey)) {

312

return cache.get(cacheKey)!;

313

}

314

315

const result = baseFilter(query, actions);

316

cache.set(cacheKey, result);

317

318

// Limit cache size

319

if (cache.size > 100) {

320

const firstKey = cache.keys().next().value;

321

cache.delete(firstKey);

322

}

323

324

return result;

325

};

326

}

327

328

const memoizedFilter = createMemoizedFilter(simpleFilter);

329

```