@masterteam/components 0.0.134 → 0.0.136
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/assets/common.css +1 -1
- package/fesm2022/masterteam-components-business-fields.mjs +1 -1
- package/fesm2022/masterteam-components-business-fields.mjs.map +1 -1
- package/fesm2022/masterteam-components-color-picker-field.mjs +2 -2
- package/fesm2022/masterteam-components-color-picker-field.mjs.map +1 -1
- package/fesm2022/masterteam-components-formula.mjs +407 -39
- package/fesm2022/masterteam-components-formula.mjs.map +1 -1
- package/fesm2022/masterteam-components-module-summary-card.mjs +1 -1
- package/fesm2022/masterteam-components-module-summary-card.mjs.map +1 -1
- package/fesm2022/masterteam-components-slider-field.mjs +61 -50
- package/fesm2022/masterteam-components-slider-field.mjs.map +1 -1
- package/fesm2022/masterteam-components-table.mjs +231 -22
- package/fesm2022/masterteam-components-table.mjs.map +1 -1
- package/fesm2022/masterteam-components-toggle-field.mjs +15 -3
- package/fesm2022/masterteam-components-toggle-field.mjs.map +1 -1
- package/fesm2022/masterteam-components-tree.mjs +22 -9
- package/fesm2022/masterteam-components-tree.mjs.map +1 -1
- package/package.json +1 -1
- package/types/masterteam-components-formula.d.ts +76 -13
- package/types/masterteam-components-slider-field.d.ts +15 -22
- package/types/masterteam-components-table.d.ts +22 -2
- package/types/masterteam-components-toggle-field.d.ts +4 -1
- package/types/masterteam-components-tree.d.ts +4 -1
|
@@ -172,6 +172,21 @@ function createLiteralToken(value, depth = 0, functionId, argIndex) {
|
|
|
172
172
|
dragBehavior: 'single',
|
|
173
173
|
};
|
|
174
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Create a raw text token for identifiers or syntax fragments that are not
|
|
177
|
+
* properties, literals, or operators.
|
|
178
|
+
*/
|
|
179
|
+
function createTextToken(value, depth = 0, functionId, argIndex) {
|
|
180
|
+
return {
|
|
181
|
+
id: generateTokenId(),
|
|
182
|
+
type: 'text',
|
|
183
|
+
value,
|
|
184
|
+
depth,
|
|
185
|
+
functionId,
|
|
186
|
+
argIndex,
|
|
187
|
+
dragBehavior: 'single',
|
|
188
|
+
};
|
|
189
|
+
}
|
|
175
190
|
// ============ TOKEN ARRAY OPERATIONS ============
|
|
176
191
|
/**
|
|
177
192
|
* Find the complete range of a function in the token array
|
|
@@ -237,20 +252,34 @@ function isValidDropPosition(tokens, dropIndex, draggedTokens) {
|
|
|
237
252
|
function serializeTokens(tokens) {
|
|
238
253
|
const parts = [];
|
|
239
254
|
let lastType = null;
|
|
255
|
+
let lastValue = '';
|
|
240
256
|
for (const token of tokens) {
|
|
241
|
-
|
|
242
|
-
if (lastType &&
|
|
243
|
-
lastType !== 'fn-open' &&
|
|
244
|
-
token.type !== 'fn-close' &&
|
|
245
|
-
token.type !== 'fn-separator' &&
|
|
246
|
-
lastType !== 'fn-separator') {
|
|
257
|
+
if (shouldInsertSpace(lastType, lastValue, token)) {
|
|
247
258
|
parts.push(' ');
|
|
248
259
|
}
|
|
249
260
|
parts.push(token.value);
|
|
250
261
|
lastType = token.type;
|
|
262
|
+
lastValue = token.value;
|
|
251
263
|
}
|
|
252
264
|
return parts.join('');
|
|
253
265
|
}
|
|
266
|
+
function shouldInsertSpace(previousType, previousValue, current) {
|
|
267
|
+
if (!previousType)
|
|
268
|
+
return false;
|
|
269
|
+
if (previousType === 'fn-open')
|
|
270
|
+
return false;
|
|
271
|
+
if (current.type === 'fn-close')
|
|
272
|
+
return false;
|
|
273
|
+
if (current.type === 'fn-separator')
|
|
274
|
+
return false;
|
|
275
|
+
if (previousType === 'fn-separator')
|
|
276
|
+
return true;
|
|
277
|
+
if (current.value === ':')
|
|
278
|
+
return false;
|
|
279
|
+
if (previousValue === ':')
|
|
280
|
+
return true;
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
254
283
|
/**
|
|
255
284
|
* Clone a token with new ID
|
|
256
285
|
*/
|
|
@@ -299,6 +328,226 @@ function recalculateDepths(tokens) {
|
|
|
299
328
|
}
|
|
300
329
|
return result;
|
|
301
330
|
}
|
|
331
|
+
/**
|
|
332
|
+
* Convert a backend template or insertText into builder tokens while
|
|
333
|
+
* preserving the authored syntax as closely as the flat token model allows.
|
|
334
|
+
*/
|
|
335
|
+
function tokenizeFormulaTemplate(template) {
|
|
336
|
+
const source = template.trim();
|
|
337
|
+
if (!source)
|
|
338
|
+
return [];
|
|
339
|
+
let index = 0;
|
|
340
|
+
const parseExpression = (context) => {
|
|
341
|
+
const tokens = [];
|
|
342
|
+
while (index < source.length) {
|
|
343
|
+
skipWhitespace();
|
|
344
|
+
if (index >= source.length)
|
|
345
|
+
break;
|
|
346
|
+
const char = source[index];
|
|
347
|
+
if (char === ',' || char === ')') {
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
if (char === '@') {
|
|
351
|
+
tokens.push(createPropertyToken(readPropertyPath(), 'current', context.depth, context.functionId, context.argIndex));
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
if (char === '"' || char === "'") {
|
|
355
|
+
tokens.push(createLiteralToken(readQuotedString(), context.depth, context.functionId, context.argIndex));
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (isDigit(char)) {
|
|
359
|
+
tokens.push(createLiteralToken(Number(readNumber()), context.depth, context.functionId, context.argIndex));
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (char === '-' &&
|
|
363
|
+
isDigit(source[index + 1] ?? '') &&
|
|
364
|
+
shouldReadNegativeNumber(tokens)) {
|
|
365
|
+
tokens.push(createLiteralToken(Number(readNumber()), context.depth, context.functionId, context.argIndex));
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
if (isIdentifierStart(char)) {
|
|
369
|
+
const identifier = readIdentifier();
|
|
370
|
+
skipWhitespace();
|
|
371
|
+
if (source[index] === '(') {
|
|
372
|
+
tokens.push(...parseFunction(identifier, context.depth));
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (identifier === 'true' || identifier === 'false') {
|
|
376
|
+
tokens.push(createLiteralToken(identifier === 'true', context.depth, context.functionId, context.argIndex));
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
if (identifier === 'null') {
|
|
380
|
+
tokens.push(createTextToken(identifier, context.depth, context.functionId, context.argIndex));
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
tokens.push(createTextToken(identifier, context.depth, context.functionId, context.argIndex));
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
const operator = readOperator();
|
|
387
|
+
if (operator) {
|
|
388
|
+
tokens.push(createOperatorToken(operator, context.depth, context.functionId, context.argIndex));
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
tokens.push(createTextToken(char, context.depth, context.functionId, context.argIndex));
|
|
392
|
+
index += 1;
|
|
393
|
+
}
|
|
394
|
+
return tokens;
|
|
395
|
+
};
|
|
396
|
+
const parseFunction = (name, depth) => {
|
|
397
|
+
const functionId = generateFunctionId();
|
|
398
|
+
const tokens = [
|
|
399
|
+
{
|
|
400
|
+
id: generateTokenId(),
|
|
401
|
+
type: 'fn-open',
|
|
402
|
+
value: `${name}(`,
|
|
403
|
+
functionId,
|
|
404
|
+
functionName: name,
|
|
405
|
+
depth,
|
|
406
|
+
dragBehavior: 'function',
|
|
407
|
+
},
|
|
408
|
+
];
|
|
409
|
+
index += 1; // consume '('
|
|
410
|
+
let argIndex = 0;
|
|
411
|
+
while (index < source.length) {
|
|
412
|
+
skipWhitespace();
|
|
413
|
+
if (source[index] === ')') {
|
|
414
|
+
index += 1;
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
tokens.push(...parseExpression({
|
|
418
|
+
depth: depth + 1,
|
|
419
|
+
functionId,
|
|
420
|
+
argIndex,
|
|
421
|
+
}));
|
|
422
|
+
skipWhitespace();
|
|
423
|
+
if (source[index] === ',') {
|
|
424
|
+
tokens.push({
|
|
425
|
+
id: generateTokenId(),
|
|
426
|
+
type: 'fn-separator',
|
|
427
|
+
value: ',',
|
|
428
|
+
functionId,
|
|
429
|
+
depth,
|
|
430
|
+
argIndex,
|
|
431
|
+
dragBehavior: 'function',
|
|
432
|
+
});
|
|
433
|
+
argIndex += 1;
|
|
434
|
+
index += 1;
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
if (source[index] === ')') {
|
|
438
|
+
index += 1;
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
tokens.push({
|
|
443
|
+
id: generateTokenId(),
|
|
444
|
+
type: 'fn-close',
|
|
445
|
+
value: ')',
|
|
446
|
+
functionId,
|
|
447
|
+
depth,
|
|
448
|
+
dragBehavior: 'function',
|
|
449
|
+
});
|
|
450
|
+
return tokens;
|
|
451
|
+
};
|
|
452
|
+
const skipWhitespace = () => {
|
|
453
|
+
while (index < source.length && /\s/.test(source[index])) {
|
|
454
|
+
index += 1;
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
const readQuotedString = () => {
|
|
458
|
+
const quote = source[index];
|
|
459
|
+
let value = quote;
|
|
460
|
+
index += 1;
|
|
461
|
+
while (index < source.length) {
|
|
462
|
+
const char = source[index];
|
|
463
|
+
value += char;
|
|
464
|
+
index += 1;
|
|
465
|
+
if (char === '\\' && index < source.length) {
|
|
466
|
+
value += source[index];
|
|
467
|
+
index += 1;
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
if (char === quote) {
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return value;
|
|
475
|
+
};
|
|
476
|
+
const readNumber = () => {
|
|
477
|
+
const start = index;
|
|
478
|
+
if (source[index] === '-') {
|
|
479
|
+
index += 1;
|
|
480
|
+
}
|
|
481
|
+
while (index < source.length && /[0-9.]/.test(source[index])) {
|
|
482
|
+
index += 1;
|
|
483
|
+
}
|
|
484
|
+
return source.slice(start, index);
|
|
485
|
+
};
|
|
486
|
+
const readIdentifier = () => {
|
|
487
|
+
const start = index;
|
|
488
|
+
index += 1;
|
|
489
|
+
while (index < source.length && /[\w]/.test(source[index])) {
|
|
490
|
+
index += 1;
|
|
491
|
+
}
|
|
492
|
+
return source.slice(start, index);
|
|
493
|
+
};
|
|
494
|
+
const readPropertyPath = () => {
|
|
495
|
+
const start = index;
|
|
496
|
+
index += 1;
|
|
497
|
+
while (index < source.length) {
|
|
498
|
+
const char = source[index];
|
|
499
|
+
const next = source[index + 1] ?? '';
|
|
500
|
+
if (/\s/.test(char) || char === ',' || char === ')' || char === '(') {
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
if (/[+\-*/%<>=!&|?:]/.test(char) && !(char === ':' && next === ':')) {
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
index += 1;
|
|
507
|
+
}
|
|
508
|
+
return source.slice(start, index);
|
|
509
|
+
};
|
|
510
|
+
const readOperator = () => {
|
|
511
|
+
const operators = [
|
|
512
|
+
'==',
|
|
513
|
+
'!=',
|
|
514
|
+
'>=',
|
|
515
|
+
'<=',
|
|
516
|
+
'&&',
|
|
517
|
+
'||',
|
|
518
|
+
':',
|
|
519
|
+
'+',
|
|
520
|
+
'-',
|
|
521
|
+
'*',
|
|
522
|
+
'/',
|
|
523
|
+
'%',
|
|
524
|
+
'>',
|
|
525
|
+
'<',
|
|
526
|
+
'!',
|
|
527
|
+
'?',
|
|
528
|
+
];
|
|
529
|
+
for (const operator of operators) {
|
|
530
|
+
if (source.startsWith(operator, index)) {
|
|
531
|
+
index += operator.length;
|
|
532
|
+
return operator;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return null;
|
|
536
|
+
};
|
|
537
|
+
return recalculateDepths(parseExpression({ depth: 0 }));
|
|
538
|
+
}
|
|
539
|
+
function isDigit(value) {
|
|
540
|
+
return /[0-9]/.test(value);
|
|
541
|
+
}
|
|
542
|
+
function isIdentifierStart(value) {
|
|
543
|
+
return /[A-Za-z_]/.test(value);
|
|
544
|
+
}
|
|
545
|
+
function shouldReadNegativeNumber(tokens) {
|
|
546
|
+
const previous = tokens[tokens.length - 1];
|
|
547
|
+
if (!previous)
|
|
548
|
+
return true;
|
|
549
|
+
return previous.type === 'operator' || previous.type === 'fn-open';
|
|
550
|
+
}
|
|
302
551
|
/**
|
|
303
552
|
* Find which argument index a position belongs to within a function
|
|
304
553
|
*/
|
|
@@ -325,7 +574,7 @@ function generateSmartBlockId() {
|
|
|
325
574
|
return `sb_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
326
575
|
}
|
|
327
576
|
/** Create a function block from signature */
|
|
328
|
-
function createFunctionBlock(signature, name) {
|
|
577
|
+
function createFunctionBlock(signature, name, insertText, cursorOffset) {
|
|
329
578
|
const parsed = parseSignature(signature);
|
|
330
579
|
return {
|
|
331
580
|
id: generateSmartBlockId(),
|
|
@@ -333,6 +582,8 @@ function createFunctionBlock(signature, name) {
|
|
|
333
582
|
value: parsed.name,
|
|
334
583
|
functionName: name || parsed.name,
|
|
335
584
|
signature,
|
|
585
|
+
insertText,
|
|
586
|
+
cursorOffset,
|
|
336
587
|
arguments: parsed.args.map((arg) => ({
|
|
337
588
|
id: generateSmartBlockId(),
|
|
338
589
|
name: arg,
|
|
@@ -692,6 +943,8 @@ class FormulaEditor {
|
|
|
692
943
|
case 'literal':
|
|
693
944
|
// Literal tokens - emerald colors
|
|
694
945
|
return `${base} bg-emerald-100 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300 hover:bg-emerald-200 dark:hover:bg-emerald-900/50`;
|
|
946
|
+
case 'text':
|
|
947
|
+
return `${base} bg-slate-100 dark:bg-slate-800 text-slate-700 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-700`;
|
|
695
948
|
default:
|
|
696
949
|
return base;
|
|
697
950
|
}
|
|
@@ -703,7 +956,7 @@ class FormulaEditor {
|
|
|
703
956
|
smartBlockToTokens(block) {
|
|
704
957
|
switch (block.type) {
|
|
705
958
|
case 'function':
|
|
706
|
-
return
|
|
959
|
+
return tokenizeFormulaTemplate(block.insertText || block.signature || `${block.functionName}()`);
|
|
707
960
|
case 'property':
|
|
708
961
|
return [
|
|
709
962
|
createPropertyToken(block.propertyKey || block.value, block.propertyType, 0, undefined, undefined, block.display),
|
|
@@ -751,6 +1004,49 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
751
1004
|
], template: "<div\r\n [class]=\"containerClasses()\"\r\n [class.opacity-60]=\"disabled()\"\r\n [class.pointer-events-none]=\"disabled()\"\r\n (focus)=\"handleFocus()\"\r\n (blur)=\"handleBlur()\"\r\n tabindex=\"0\"\r\n>\r\n <!-- Line Numbers -->\r\n <div [class]=\"lineNumberClasses()\">\r\n @for (line of lines(); track $index) {\r\n <div class=\"text-surface-400 dark:text-surface-500 text-sm leading-6\">\r\n {{ $index + 1 }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Editor Content - Single Drop Zone -->\r\n <div\r\n class=\"flex-1 p-2 flex flex-wrap gap-0.5 items-start content-start min-h-14 cursor-text\"\r\n cdkDropList\r\n cdkDropListOrientation=\"mixed\"\r\n [cdkDropListData]=\"tokens()\"\r\n [cdkDropListDisabled]=\"disabled()\"\r\n (cdkDropListDropped)=\"onDrop($event)\"\r\n >\r\n @if (tokens().length === 0) {\r\n <div class=\"text-surface-400 dark:text-surface-500 text-sm italic\">\r\n {{ placeholder() }}\r\n </div>\r\n } @else {\r\n @for (token of tokens(); track token.id; let i = $index) {\r\n <div\r\n class=\"relative inline-flex items-center group\"\r\n cdkDrag\r\n [cdkDragData]=\"token\"\r\n [cdkDragDisabled]=\"disabled()\"\r\n (cdkDragStarted)=\"onDragStart($event, token, i)\"\r\n (cdkDragEnded)=\"onDragEnd()\"\r\n >\r\n <!-- Drag Placeholder (thin cursor line) -->\r\n <div\r\n *cdkDragPlaceholder\r\n class=\"w-2 min-w-2 h-5 bg-primary rounded-sm opacity-80\"\r\n ></div>\r\n\r\n <!-- Token -->\r\n <span\r\n [class]=\"getTokenClasses(token)\"\r\n [class.border-b-2]=\"!isDragging() && isHighlighted(token)\"\r\n [class.border-primary]=\"!isDragging() && isHighlighted(token)\"\r\n (mouseenter)=\"onTokenHover(token)\"\r\n (mouseleave)=\"onTokenLeave()\"\r\n >\r\n {{ token.display ?? token.value }}\r\n </span>\r\n\r\n <!-- Delete Button (shows on hover, hidden during drag) -->\r\n @if (!isDragging() && !disabled()) {\r\n <button\r\n type=\"button\"\r\n class=\"absolute -top-1.5 -right-1.5 w-3.5 h-3.5 flex items-center justify-center bg-red-500 hover:bg-red-600 text-white rounded-full cursor-pointer scale-75 hover:scale-100 opacity-0 group-hover:opacity-100 pointer-events-none group-hover:pointer-events-auto z-10\"\r\n (click)=\"removeToken(token, i); $event.stopPropagation()\"\r\n [title]=\"\r\n token.dragBehavior === 'function' ? 'Remove function' : 'Remove'\r\n \"\r\n >\r\n <svg\r\n class=\"h-2.5 w-2.5\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"3\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M6 18L18 6M6 6l12 12\"\r\n />\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n</div>\r\n", styles: [".cdk-drag-preview{width:20px!important;height:20px!important;min-width:20px!important;min-height:20px!important;max-width:20px!important;max-height:20px!important;border-radius:4px;opacity:.9;overflow:hidden!important;cursor:grabbing}.cdk-drag-animating{transition:none!important}.cdk-drag-dragging{opacity:1}\n"] }]
|
|
752
1005
|
}], ctorParameters: () => [], propDecorators: { placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], initialTokens: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialTokens", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], borderless: [{ type: i0.Input, args: [{ isSignal: true, alias: "borderless", required: false }] }], formulaChange: [{ type: i0.Output, args: ["formulaChange"] }], tokensChange: [{ type: i0.Output, args: ["tokensChange"] }], onBlur: [{ type: i0.Output, args: ["onBlur"] }], onFocus: [{ type: i0.Output, args: ["onFocus"] }] } });
|
|
753
1006
|
|
|
1007
|
+
function buildCompletionFilterText(suggestion) {
|
|
1008
|
+
const candidates = [
|
|
1009
|
+
suggestion.displayText,
|
|
1010
|
+
suggestion.insertText,
|
|
1011
|
+
suggestion.text,
|
|
1012
|
+
]
|
|
1013
|
+
.map((value) => value?.trim() ?? '')
|
|
1014
|
+
.filter((value) => value.length > 0);
|
|
1015
|
+
return Array.from(new Set(candidates)).join(' ');
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
const FORMULA_REFERENCE_CHARACTER = /[@\w.:]/;
|
|
1019
|
+
function resolveCompletionRangeBounds(lineContent, column, wordStartColumn, wordEndColumn, suggestion) {
|
|
1020
|
+
const referenceBounds = getReferenceCompletionBounds(lineContent, column, suggestion);
|
|
1021
|
+
return (referenceBounds ?? {
|
|
1022
|
+
startColumn: wordStartColumn,
|
|
1023
|
+
endColumn: wordEndColumn,
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
function getReferenceCompletionBounds(lineContent, column, suggestion) {
|
|
1027
|
+
const insertText = suggestion.insertText ?? suggestion.text;
|
|
1028
|
+
if (suggestion.type !== 'Property' &&
|
|
1029
|
+
suggestion.type !== 'Context' &&
|
|
1030
|
+
!insertText.startsWith('@')) {
|
|
1031
|
+
return null;
|
|
1032
|
+
}
|
|
1033
|
+
const cursorIndex = Math.max(0, Math.min(column - 1, lineContent.length));
|
|
1034
|
+
let startIndex = cursorIndex;
|
|
1035
|
+
while (startIndex > 0 &&
|
|
1036
|
+
FORMULA_REFERENCE_CHARACTER.test(lineContent[startIndex - 1] ?? '')) {
|
|
1037
|
+
startIndex--;
|
|
1038
|
+
}
|
|
1039
|
+
let endIndex = cursorIndex;
|
|
1040
|
+
while (endIndex < lineContent.length &&
|
|
1041
|
+
FORMULA_REFERENCE_CHARACTER.test(lineContent[endIndex] ?? '')) {
|
|
1042
|
+
endIndex++;
|
|
1043
|
+
}
|
|
1044
|
+
return {
|
|
1045
|
+
startColumn: startIndex + 1,
|
|
1046
|
+
endColumn: endIndex + 1,
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
|
|
754
1050
|
class FormulaEditorCode {
|
|
755
1051
|
placeholder = input('Enter formula...', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
|
|
756
1052
|
initialFormula = input('', ...(ngDevMode ? [{ debugName: "initialFormula" }] : []));
|
|
@@ -986,6 +1282,7 @@ class FormulaEditorCode {
|
|
|
986
1282
|
tokenizer: {
|
|
987
1283
|
root: [
|
|
988
1284
|
[/@[a-zA-Z_][\w.]*::[\w_]+/, 'type.identifier'],
|
|
1285
|
+
[/@[a-zA-Z_][\w.]*/, 'type.identifier'],
|
|
989
1286
|
[/\b[A-Z_][A-Z0-9_]*\b/, 'function'],
|
|
990
1287
|
[
|
|
991
1288
|
/[a-zA-Z_][\w_]*/,
|
|
@@ -1257,8 +1554,6 @@ class FormulaEditorCode {
|
|
|
1257
1554
|
if (!response || requestId !== this.currentAutocompleteRequestId) {
|
|
1258
1555
|
return { suggestions: [] };
|
|
1259
1556
|
}
|
|
1260
|
-
const word = model.getWordUntilPosition(position);
|
|
1261
|
-
const range = new this.monaco.Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn);
|
|
1262
1557
|
return {
|
|
1263
1558
|
suggestions: (response.suggestions ?? []).map((suggestion, index) => {
|
|
1264
1559
|
const insertText = suggestion.insertText ?? suggestion.text;
|
|
@@ -1269,9 +1564,9 @@ class FormulaEditorCode {
|
|
|
1269
1564
|
insertText: text,
|
|
1270
1565
|
insertTextRules: rule,
|
|
1271
1566
|
detail: suggestion.description ?? suggestion.category,
|
|
1272
|
-
range,
|
|
1567
|
+
range: this.getCompletionRanges(model, position, suggestion),
|
|
1273
1568
|
sortText: String(index).padStart(4, '0'),
|
|
1274
|
-
filterText: suggestion
|
|
1569
|
+
filterText: buildCompletionFilterText(suggestion),
|
|
1275
1570
|
};
|
|
1276
1571
|
}),
|
|
1277
1572
|
};
|
|
@@ -1314,6 +1609,14 @@ class FormulaEditorCode {
|
|
|
1314
1609
|
rule: this.monaco?.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
1315
1610
|
};
|
|
1316
1611
|
}
|
|
1612
|
+
getCompletionRanges(model, position, suggestion) {
|
|
1613
|
+
const word = model.getWordUntilPosition(position);
|
|
1614
|
+
const { startColumn, endColumn } = resolveCompletionRangeBounds(model.getLineContent(position.lineNumber), position.column, word.startColumn, Math.max(word.endColumn, position.column), suggestion);
|
|
1615
|
+
return {
|
|
1616
|
+
insert: new this.monaco.Range(position.lineNumber, startColumn, position.lineNumber, position.column),
|
|
1617
|
+
replace: new this.monaco.Range(position.lineNumber, startColumn, position.lineNumber, endColumn),
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1317
1620
|
mapSuggestionKind(type) {
|
|
1318
1621
|
if (!this.monaco) {
|
|
1319
1622
|
return 17;
|
|
@@ -1325,6 +1628,8 @@ class FormulaEditorCode {
|
|
|
1325
1628
|
return this.monaco.languages.CompletionItemKind.Operator;
|
|
1326
1629
|
case 'Property':
|
|
1327
1630
|
return this.monaco.languages.CompletionItemKind.Field;
|
|
1631
|
+
case 'Context':
|
|
1632
|
+
return this.monaco.languages.CompletionItemKind.Module;
|
|
1328
1633
|
case 'Keyword':
|
|
1329
1634
|
return this.monaco.languages.CompletionItemKind.Keyword;
|
|
1330
1635
|
default:
|
|
@@ -1343,9 +1648,11 @@ class FormulaEditorCode {
|
|
|
1343
1648
|
smartBlockToText(block) {
|
|
1344
1649
|
switch (block.type) {
|
|
1345
1650
|
case 'function': {
|
|
1346
|
-
const
|
|
1347
|
-
|
|
1348
|
-
|
|
1651
|
+
const text = block.insertText ??
|
|
1652
|
+
block.signature ??
|
|
1653
|
+
`${block.functionName ?? block.value}()`;
|
|
1654
|
+
const cursorOffset = block.cursorOffset ?? (text.endsWith(')') ? -1 : 0);
|
|
1655
|
+
return { text, cursorOffset };
|
|
1349
1656
|
}
|
|
1350
1657
|
case 'property':
|
|
1351
1658
|
return { text: block.value ?? '', cursorOffset: 0 };
|
|
@@ -1421,29 +1728,84 @@ class FormulaToolbarItem {
|
|
|
1421
1728
|
signature = input(...(ngDevMode ? [undefined, { debugName: "signature" }] : []));
|
|
1422
1729
|
/** Function examples (for functions only) - can be strings or objects with code/description */
|
|
1423
1730
|
examples = input([], ...(ngDevMode ? [{ debugName: "examples" }] : []));
|
|
1731
|
+
/** Backend template to insert (preferred over signature) */
|
|
1732
|
+
template = input(...(ngDevMode ? [undefined, { debugName: "template" }] : []));
|
|
1733
|
+
/** Cursor offset relative to inserted template */
|
|
1734
|
+
templateCursorOffset = input(...(ngDevMode ? [undefined, { debugName: "templateCursorOffset" }] : []));
|
|
1735
|
+
/** Function hints from backend documentation */
|
|
1736
|
+
hints = input([], ...(ngDevMode ? [{ debugName: "hints" }] : []));
|
|
1737
|
+
/** Function return type */
|
|
1738
|
+
returnType = input(...(ngDevMode ? [undefined, { debugName: "returnType" }] : []));
|
|
1739
|
+
/** Function parameters */
|
|
1740
|
+
parameters = input([], ...(ngDevMode ? [{ debugName: "parameters" }] : []));
|
|
1741
|
+
/** Normalized description */
|
|
1742
|
+
normalizedDescription = computed(() => this.description().trim(), ...(ngDevMode ? [{ debugName: "normalizedDescription" }] : []));
|
|
1743
|
+
/** Normalized signature */
|
|
1744
|
+
normalizedSignature = computed(() => this.signature()?.trim() ?? '', ...(ngDevMode ? [{ debugName: "normalizedSignature" }] : []));
|
|
1745
|
+
/** Normalized template */
|
|
1746
|
+
normalizedTemplate = computed(() => this.template()?.trim() ?? '', ...(ngDevMode ? [{ debugName: "normalizedTemplate" }] : []));
|
|
1747
|
+
/** Normalized return type */
|
|
1748
|
+
normalizedReturnType = computed(() => this.returnType()?.trim() ?? '', ...(ngDevMode ? [{ debugName: "normalizedReturnType" }] : []));
|
|
1424
1749
|
/** Normalized examples as ExampleItem[] */
|
|
1425
1750
|
normalizedExamples = computed(() => {
|
|
1426
|
-
return this.examples()
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1751
|
+
return this.examples()
|
|
1752
|
+
.map((example) => {
|
|
1753
|
+
const normalized = typeof example === 'string' ? { code: example } : example;
|
|
1754
|
+
return {
|
|
1755
|
+
code: normalized.code.trim(),
|
|
1756
|
+
description: normalized.description?.trim() || undefined,
|
|
1757
|
+
};
|
|
1758
|
+
})
|
|
1759
|
+
.filter((example) => example.code.length > 0);
|
|
1432
1760
|
}, ...(ngDevMode ? [{ debugName: "normalizedExamples" }] : []));
|
|
1761
|
+
/** Normalized hints */
|
|
1762
|
+
normalizedHints = computed(() => this.hints()
|
|
1763
|
+
.map((hint) => hint.trim())
|
|
1764
|
+
.filter((hint) => hint.length > 0), ...(ngDevMode ? [{ debugName: "normalizedHints" }] : []));
|
|
1765
|
+
/** Normalized parameters */
|
|
1766
|
+
normalizedParameters = computed(() => this.parameters()
|
|
1767
|
+
.map((parameter) => ({
|
|
1768
|
+
...parameter,
|
|
1769
|
+
name: parameter.name.trim(),
|
|
1770
|
+
type: parameter.type.trim(),
|
|
1771
|
+
description: parameter.description.trim(),
|
|
1772
|
+
}))
|
|
1773
|
+
.filter((parameter) => parameter.name.length > 0 ||
|
|
1774
|
+
parameter.type.length > 0 ||
|
|
1775
|
+
parameter.description.length > 0), ...(ngDevMode ? [{ debugName: "normalizedParameters" }] : []));
|
|
1433
1776
|
/** Property type for properties */
|
|
1434
1777
|
propertyType = input('current', ...(ngDevMode ? [{ debugName: "propertyType" }] : []));
|
|
1435
1778
|
/** Insert event - emits SmartBlock */
|
|
1436
1779
|
onInsert = output();
|
|
1437
1780
|
/** Computed display value */
|
|
1438
1781
|
displayValue = computed(() => this.display() ?? this.value(), ...(ngDevMode ? [{ debugName: "displayValue" }] : []));
|
|
1439
|
-
/**
|
|
1782
|
+
/** Whether function metadata should use the rich tooltip template */
|
|
1440
1783
|
hasRichTooltip = computed(() => this.type() === 'function' &&
|
|
1441
|
-
(this.
|
|
1784
|
+
Boolean(this.normalizedSignature() ||
|
|
1785
|
+
this.shouldShowTemplate() ||
|
|
1786
|
+
this.normalizedDescription() ||
|
|
1787
|
+
this.normalizedReturnType() ||
|
|
1788
|
+
this.normalizedParameters().length > 0 ||
|
|
1789
|
+
this.normalizedExamples().length > 0 ||
|
|
1790
|
+
this.normalizedHints().length > 0), ...(ngDevMode ? [{ debugName: "hasRichTooltip" }] : []));
|
|
1791
|
+
/** Keep lightweight tooltip behavior only for small descriptions */
|
|
1792
|
+
simpleTooltip = computed(() => this.hasRichTooltip() ? '' : this.normalizedDescription(), ...(ngDevMode ? [{ debugName: "simpleTooltip" }] : []));
|
|
1793
|
+
/** Show insert template when it adds useful information beyond the signature */
|
|
1794
|
+
shouldShowTemplate = computed(() => {
|
|
1795
|
+
const template = this.normalizedTemplate();
|
|
1796
|
+
return template.length > 0 && template !== this.normalizedSignature();
|
|
1797
|
+
}, ...(ngDevMode ? [{ debugName: "shouldShowTemplate" }] : []));
|
|
1798
|
+
/** PrimeNG tooltip class to widen rich tooltip content */
|
|
1799
|
+
tooltipStyleClass = computed(() => this.hasRichTooltip()
|
|
1800
|
+
? 'mt-formula-toolbar-item-tooltip mt-formula-toolbar-item-tooltip-rich'
|
|
1801
|
+
: 'mt-formula-toolbar-item-tooltip', ...(ngDevMode ? [{ debugName: "tooltipStyleClass" }] : []));
|
|
1442
1802
|
/** Build SmartBlock from item data */
|
|
1443
1803
|
smartBlock = computed(() => {
|
|
1444
1804
|
switch (this.type()) {
|
|
1445
1805
|
case 'function':
|
|
1446
|
-
return createFunctionBlock(this.
|
|
1806
|
+
return createFunctionBlock(this.normalizedSignature() || `${this.value()}()`, this.value(), this.normalizedTemplate() ||
|
|
1807
|
+
this.normalizedSignature() ||
|
|
1808
|
+
`${this.value()}()`, this.templateCursorOffset());
|
|
1447
1809
|
case 'property':
|
|
1448
1810
|
return createPropertyBlock(this.value(), this.propertyType(), this.display());
|
|
1449
1811
|
case 'operator':
|
|
@@ -1470,14 +1832,14 @@ class FormulaToolbarItem {
|
|
|
1470
1832
|
this.onInsert.emit(this.smartBlock());
|
|
1471
1833
|
}
|
|
1472
1834
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FormulaToolbarItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1473
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: FormulaToolbarItem, isStandalone: true, selector: "mt-formula-toolbar-item", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, signature: { classPropertyName: "signature", publicName: "signature", isSignal: true, isRequired: false, transformFunction: null }, examples: { classPropertyName: "examples", publicName: "examples", isSignal: true, isRequired: false, transformFunction: null }, propertyType: { classPropertyName: "propertyType", publicName: "propertyType", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onInsert: "onInsert" }, host: { classAttribute: "inline-block" }, ngImport: i0, template: "<button\
|
|
1835
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: FormulaToolbarItem, isStandalone: true, selector: "mt-formula-toolbar-item", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, signature: { classPropertyName: "signature", publicName: "signature", isSignal: true, isRequired: false, transformFunction: null }, examples: { classPropertyName: "examples", publicName: "examples", isSignal: true, isRequired: false, transformFunction: null }, template: { classPropertyName: "template", publicName: "template", isSignal: true, isRequired: false, transformFunction: null }, templateCursorOffset: { classPropertyName: "templateCursorOffset", publicName: "templateCursorOffset", isSignal: true, isRequired: false, transformFunction: null }, hints: { classPropertyName: "hints", publicName: "hints", isSignal: true, isRequired: false, transformFunction: null }, returnType: { classPropertyName: "returnType", publicName: "returnType", isSignal: true, isRequired: false, transformFunction: null }, parameters: { classPropertyName: "parameters", publicName: "parameters", isSignal: true, isRequired: false, transformFunction: null }, propertyType: { classPropertyName: "propertyType", publicName: "propertyType", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onInsert: "onInsert" }, host: { classAttribute: "inline-block max-w-full" }, ngImport: i0, template: "<button\n type=\"button\"\n class=\"inline-flex min-w-0 max-w-full cursor-pointer items-center gap-1 rounded px-2 py-1 text-sm font-medium transition-colors hover:shadow-sm active:scale-95\"\n [class]=\"itemClass()\"\n [pTooltip]=\"hasRichTooltip() ? richTooltip : simpleTooltip()\"\n [tooltipDisabled]=\"!hasRichTooltip() && !simpleTooltip()\"\n [tooltipStyleClass]=\"tooltipStyleClass()\"\n [tooltipPosition]=\"'bottom'\"\n [showDelay]=\"300\"\n [hideDelay]=\"100\"\n [autoHide]=\"!hasRichTooltip()\"\n (click)=\"handleClick()\"\n>\n @if (type() === \"function\") {\n <span class=\"text-sm opacity-60\">f</span>\n }\n <span class=\"truncate\">{{ displayValue() }}</span>\n</button>\n\n<ng-template #richTooltip>\n <div class=\"space-y-3\">\n @if (normalizedSignature()) {\n <div class=\"rounded bg-slate-700 px-3 py-2\">\n <code\n class=\"block whitespace-pre-wrap break-words font-mono text-sm text-emerald-400\"\n >{{ normalizedSignature() }}</code\n >\n </div>\n }\n\n @if (shouldShowTemplate()) {\n <div class=\"space-y-1\">\n <span\n class=\"text-xs font-semibold uppercase tracking-wider text-slate-400\"\n >Insert</span\n >\n <div class=\"rounded bg-slate-800 px-3 py-2\">\n <code\n class=\"block whitespace-pre-wrap break-words font-mono text-xs text-amber-300\"\n >{{ normalizedTemplate() }}</code\n >\n </div>\n </div>\n }\n\n @if (normalizedDescription()) {\n <p class=\"text-sm leading-relaxed text-slate-200\">\n {{ normalizedDescription() }}\n </p>\n }\n\n @if (normalizedReturnType()) {\n <div class=\"flex items-center gap-2 text-xs text-slate-300\">\n <span class=\"font-semibold uppercase tracking-wider text-slate-400\"\n >Returns</span\n >\n <code class=\"rounded bg-slate-800 px-2 py-0.5 text-sky-300\">{{\n normalizedReturnType()\n }}</code>\n </div>\n }\n\n @if (normalizedParameters().length > 0) {\n <div class=\"space-y-2 border-t border-slate-600 pt-3\">\n <span\n class=\"text-xs font-semibold uppercase tracking-wider text-slate-400\"\n >Parameters</span\n >\n <div class=\"space-y-2\">\n @for (\n parameter of normalizedParameters();\n track parameter.name + parameter.type\n ) {\n <div class=\"rounded bg-slate-800/70 px-3 py-2\">\n <div class=\"flex flex-wrap items-center gap-2 text-xs\">\n @if (parameter.name) {\n <code class=\"font-mono text-emerald-300\">{{\n parameter.name\n }}</code>\n }\n @if (parameter.type) {\n <code class=\"text-sky-300\">{{ parameter.type }}</code>\n }\n </div>\n @if (parameter.description) {\n <p class=\"mt-1 text-xs leading-relaxed text-slate-400\">\n {{ parameter.description }}\n </p>\n }\n </div>\n }\n </div>\n </div>\n }\n\n @if (normalizedExamples().length > 0) {\n <div class=\"space-y-2 border-t border-slate-600 pt-3\">\n <span\n class=\"text-xs font-semibold uppercase tracking-wider text-slate-400\"\n >Examples</span\n >\n <div class=\"space-y-2\">\n @for (example of normalizedExamples(); track example.code + $index) {\n <div class=\"space-y-1\">\n <div class=\"rounded bg-slate-800 px-3 py-2\">\n <code\n class=\"block whitespace-pre-wrap break-words font-mono text-xs text-amber-300\"\n >{{ example.code }}</code\n >\n </div>\n @if (example.description) {\n <p class=\"px-1 text-xs leading-relaxed text-slate-400\">\n {{ example.description }}\n </p>\n }\n </div>\n }\n </div>\n </div>\n }\n\n @if (normalizedHints().length > 0) {\n <div class=\"space-y-2 border-t border-slate-600 pt-3\">\n <span\n class=\"text-xs font-semibold uppercase tracking-wider text-slate-400\"\n >Hints</span\n >\n <div class=\"space-y-1.5\">\n @for (hint of normalizedHints(); track hint) {\n <p\n class=\"rounded bg-slate-800/70 px-3 py-2 text-xs leading-relaxed text-slate-300\"\n >\n {{ hint }}\n </p>\n }\n </div>\n </div>\n }\n </div>\n</ng-template>\n", styles: [".mt-formula-toolbar-item-tooltip.p-tooltip,.mt-formula-toolbar-item-tooltip .p-tooltip-text{max-width:min(32rem,92vw)}.mt-formula-toolbar-item-tooltip-rich .p-tooltip-text{width:min(32rem,92vw);max-height:min(70vh,36rem);overflow-y:auto;padding:.75rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i1$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
1474
1836
|
}
|
|
1475
1837
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FormulaToolbarItem, decorators: [{
|
|
1476
1838
|
type: Component,
|
|
1477
|
-
args: [{ selector: 'mt-formula-toolbar-item', standalone: true, imports: [CommonModule, TooltipModule], host: {
|
|
1478
|
-
class: 'inline-block',
|
|
1479
|
-
}, template: "<button\
|
|
1480
|
-
}], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], display: [{ type: i0.Input, args: [{ isSignal: true, alias: "display", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], signature: [{ type: i0.Input, args: [{ isSignal: true, alias: "signature", required: false }] }], examples: [{ type: i0.Input, args: [{ isSignal: true, alias: "examples", required: false }] }], propertyType: [{ type: i0.Input, args: [{ isSignal: true, alias: "propertyType", required: false }] }], onInsert: [{ type: i0.Output, args: ["onInsert"] }] } });
|
|
1839
|
+
args: [{ selector: 'mt-formula-toolbar-item', standalone: true, imports: [CommonModule, TooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
|
|
1840
|
+
class: 'inline-block max-w-full',
|
|
1841
|
+
}, template: "<button\n type=\"button\"\n class=\"inline-flex min-w-0 max-w-full cursor-pointer items-center gap-1 rounded px-2 py-1 text-sm font-medium transition-colors hover:shadow-sm active:scale-95\"\n [class]=\"itemClass()\"\n [pTooltip]=\"hasRichTooltip() ? richTooltip : simpleTooltip()\"\n [tooltipDisabled]=\"!hasRichTooltip() && !simpleTooltip()\"\n [tooltipStyleClass]=\"tooltipStyleClass()\"\n [tooltipPosition]=\"'bottom'\"\n [showDelay]=\"300\"\n [hideDelay]=\"100\"\n [autoHide]=\"!hasRichTooltip()\"\n (click)=\"handleClick()\"\n>\n @if (type() === \"function\") {\n <span class=\"text-sm opacity-60\">f</span>\n }\n <span class=\"truncate\">{{ displayValue() }}</span>\n</button>\n\n<ng-template #richTooltip>\n <div class=\"space-y-3\">\n @if (normalizedSignature()) {\n <div class=\"rounded bg-slate-700 px-3 py-2\">\n <code\n class=\"block whitespace-pre-wrap break-words font-mono text-sm text-emerald-400\"\n >{{ normalizedSignature() }}</code\n >\n </div>\n }\n\n @if (shouldShowTemplate()) {\n <div class=\"space-y-1\">\n <span\n class=\"text-xs font-semibold uppercase tracking-wider text-slate-400\"\n >Insert</span\n >\n <div class=\"rounded bg-slate-800 px-3 py-2\">\n <code\n class=\"block whitespace-pre-wrap break-words font-mono text-xs text-amber-300\"\n >{{ normalizedTemplate() }}</code\n >\n </div>\n </div>\n }\n\n @if (normalizedDescription()) {\n <p class=\"text-sm leading-relaxed text-slate-200\">\n {{ normalizedDescription() }}\n </p>\n }\n\n @if (normalizedReturnType()) {\n <div class=\"flex items-center gap-2 text-xs text-slate-300\">\n <span class=\"font-semibold uppercase tracking-wider text-slate-400\"\n >Returns</span\n >\n <code class=\"rounded bg-slate-800 px-2 py-0.5 text-sky-300\">{{\n normalizedReturnType()\n }}</code>\n </div>\n }\n\n @if (normalizedParameters().length > 0) {\n <div class=\"space-y-2 border-t border-slate-600 pt-3\">\n <span\n class=\"text-xs font-semibold uppercase tracking-wider text-slate-400\"\n >Parameters</span\n >\n <div class=\"space-y-2\">\n @for (\n parameter of normalizedParameters();\n track parameter.name + parameter.type\n ) {\n <div class=\"rounded bg-slate-800/70 px-3 py-2\">\n <div class=\"flex flex-wrap items-center gap-2 text-xs\">\n @if (parameter.name) {\n <code class=\"font-mono text-emerald-300\">{{\n parameter.name\n }}</code>\n }\n @if (parameter.type) {\n <code class=\"text-sky-300\">{{ parameter.type }}</code>\n }\n </div>\n @if (parameter.description) {\n <p class=\"mt-1 text-xs leading-relaxed text-slate-400\">\n {{ parameter.description }}\n </p>\n }\n </div>\n }\n </div>\n </div>\n }\n\n @if (normalizedExamples().length > 0) {\n <div class=\"space-y-2 border-t border-slate-600 pt-3\">\n <span\n class=\"text-xs font-semibold uppercase tracking-wider text-slate-400\"\n >Examples</span\n >\n <div class=\"space-y-2\">\n @for (example of normalizedExamples(); track example.code + $index) {\n <div class=\"space-y-1\">\n <div class=\"rounded bg-slate-800 px-3 py-2\">\n <code\n class=\"block whitespace-pre-wrap break-words font-mono text-xs text-amber-300\"\n >{{ example.code }}</code\n >\n </div>\n @if (example.description) {\n <p class=\"px-1 text-xs leading-relaxed text-slate-400\">\n {{ example.description }}\n </p>\n }\n </div>\n }\n </div>\n </div>\n }\n\n @if (normalizedHints().length > 0) {\n <div class=\"space-y-2 border-t border-slate-600 pt-3\">\n <span\n class=\"text-xs font-semibold uppercase tracking-wider text-slate-400\"\n >Hints</span\n >\n <div class=\"space-y-1.5\">\n @for (hint of normalizedHints(); track hint) {\n <p\n class=\"rounded bg-slate-800/70 px-3 py-2 text-xs leading-relaxed text-slate-300\"\n >\n {{ hint }}\n </p>\n }\n </div>\n </div>\n }\n </div>\n</ng-template>\n", styles: [".mt-formula-toolbar-item-tooltip.p-tooltip,.mt-formula-toolbar-item-tooltip .p-tooltip-text{max-width:min(32rem,92vw)}.mt-formula-toolbar-item-tooltip-rich .p-tooltip-text{width:min(32rem,92vw);max-height:min(70vh,36rem);overflow-y:auto;padding:.75rem}\n"] }]
|
|
1842
|
+
}], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], display: [{ type: i0.Input, args: [{ isSignal: true, alias: "display", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], signature: [{ type: i0.Input, args: [{ isSignal: true, alias: "signature", required: false }] }], examples: [{ type: i0.Input, args: [{ isSignal: true, alias: "examples", required: false }] }], template: [{ type: i0.Input, args: [{ isSignal: true, alias: "template", required: false }] }], templateCursorOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "templateCursorOffset", required: false }] }], hints: [{ type: i0.Input, args: [{ isSignal: true, alias: "hints", required: false }] }], returnType: [{ type: i0.Input, args: [{ isSignal: true, alias: "returnType", required: false }] }], parameters: [{ type: i0.Input, args: [{ isSignal: true, alias: "parameters", required: false }] }], propertyType: [{ type: i0.Input, args: [{ isSignal: true, alias: "propertyType", required: false }] }], onInsert: [{ type: i0.Output, args: ["onInsert"] }] } });
|
|
1481
1843
|
|
|
1482
1844
|
const DEFAULT_OPERATORS = [
|
|
1483
1845
|
// Arithmetic
|
|
@@ -1703,15 +2065,21 @@ class FormulaToolbar {
|
|
|
1703
2065
|
// ============ COMPUTED ============
|
|
1704
2066
|
/** Filtered categories based on search */
|
|
1705
2067
|
filteredCategories = computed(() => {
|
|
1706
|
-
const query = this.searchQuery().toLowerCase();
|
|
1707
|
-
|
|
1708
|
-
if (!query)
|
|
1709
|
-
return categories;
|
|
1710
|
-
return categories
|
|
2068
|
+
const query = this.searchQuery().trim().toLowerCase();
|
|
2069
|
+
return this.functionCategories()
|
|
1711
2070
|
.map((cat) => ({
|
|
1712
2071
|
...cat,
|
|
1713
|
-
functions: cat.functions.filter((fn) =>
|
|
1714
|
-
|
|
2072
|
+
functions: cat.functions.filter((fn) => {
|
|
2073
|
+
if (!query) {
|
|
2074
|
+
return true;
|
|
2075
|
+
}
|
|
2076
|
+
const functionName = fn.name.toLowerCase();
|
|
2077
|
+
const description = fn.description.toLowerCase();
|
|
2078
|
+
const categoryName = String(cat.displayName ?? cat.name).toLowerCase();
|
|
2079
|
+
return (functionName.includes(query) ||
|
|
2080
|
+
description.includes(query) ||
|
|
2081
|
+
categoryName.includes(query));
|
|
2082
|
+
}),
|
|
1715
2083
|
}))
|
|
1716
2084
|
.filter((cat) => cat.functions.length > 0);
|
|
1717
2085
|
}, ...(ngDevMode ? [{ debugName: "filteredCategories" }] : []));
|
|
@@ -1879,11 +2247,11 @@ class FormulaToolbar {
|
|
|
1879
2247
|
this.customValue.set('');
|
|
1880
2248
|
}
|
|
1881
2249
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FormulaToolbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1882
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: FormulaToolbar, isStandalone: true, selector: "mt-formula-toolbar", inputs: { knownProperties: { classPropertyName: "knownProperties", publicName: "knownProperties", isSignal: true, isRequired: false, transformFunction: null }, propertiesTemplate: { classPropertyName: "propertiesTemplate", publicName: "propertiesTemplate", isSignal: true, isRequired: false, transformFunction: null }, functionCategories: { classPropertyName: "functionCategories", publicName: "functionCategories", isSignal: true, isRequired: false, transformFunction: null }, operators: { classPropertyName: "operators", publicName: "operators", isSignal: true, isRequired: false, transformFunction: null }, initialTab: { classPropertyName: "initialTab", publicName: "initialTab", isSignal: true, isRequired: false, transformFunction: null }, visibleTabs: { classPropertyName: "visibleTabs", publicName: "visibleTabs", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onBlockInsert: "onBlockInsert", onTabChange: "onTabChange" }, queries: [{ propertyName: "projectedPropertiesTemplate", first: true, predicate: ["properties"], descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0, template: "<div\r\n *transloco=\"let t; prefix: 'components.formula.toolbar'\"\r\n class=\"flex flex-col border-b border-slate-200 dark:border-slate-700\"\r\n [class.h-52]=\"activeTab() !== 'properties' || !resolvedPropertiesTemplate()\"\r\n>\r\n <!-- Top Bar: Search + Quick Insert -->\r\n <div\r\n class=\"flex items-center gap-2 border-b border-slate-200 px-3 py-2 dark:border-slate-700\"\r\n >\r\n <!-- Search Field -->\r\n <div class=\"relative flex-1\">\r\n <svg\r\n class=\"absolute start-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\r\n />\r\n </svg>\r\n <input\r\n type=\"text\"\r\n class=\"w-full rounded-lg border border-slate-300 bg-white py-1.5 ps-9 pe-8 text-sm text-slate-700 placeholder:text-slate-400 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 transition-all duration-200 dark:border-slate-600 dark:bg-slate-900 dark:text-slate-200 dark:placeholder:text-slate-500\"\r\n [placeholder]=\"searchPlaceholder() || t('search')\"\r\n [(ngModel)]=\"searchQuery\"\r\n />\r\n @if (searchQuery()) {\r\n <button\r\n type=\"button\"\r\n class=\"absolute end-2 top-1/2 -translate-y-1/2 rounded p-0.5 text-slate-400 hover:bg-slate-200 hover:text-slate-600 transition-all duration-200 dark:hover:bg-slate-700\"\r\n (click)=\"clearSearch()\"\r\n >\r\n <svg\r\n class=\"h-4 w-4\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M6 18L18 6M6 6l12 12\"\r\n />\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Separator -->\r\n <div class=\"h-6 w-px bg-slate-200 dark:bg-slate-600\"></div>\r\n\r\n <!-- Quick Insert Input -->\r\n <div class=\"relative flex items-center gap-2\">\r\n <span\r\n class=\"text-sm font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wide\"\r\n >{{ t(\"insertLabel\") }}:</span\r\n >\r\n <div class=\"relative\">\r\n <input\r\n #customValueInput\r\n type=\"text\"\r\n class=\"w-32 rounded-md border border-dashed border-slate-300 bg-slate-50 px-3 py-1.5 text-sm font-mono text-slate-700 placeholder:text-slate-400 focus:border-primary focus:bg-white focus:outline-none focus:ring-2 focus:ring-primary/20 transition-all duration-200 dark:border-slate-600 dark:bg-slate-800 dark:text-slate-200 dark:placeholder:text-slate-500 dark:focus:bg-slate-900\"\r\n [placeholder]=\"t('customValuePlaceholder')\"\r\n [(ngModel)]=\"customValue\"\r\n (keydown.enter)=\"insertCustomValue()\"\r\n (keydown.escape)=\"clearCustomValue()\"\r\n />\r\n </div>\r\n\r\n <!-- Insert Button with Type Preview -->\r\n @if (customValue()) {\r\n <button\r\n type=\"button\"\r\n class=\"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-all duration-200 hover:shadow-sm active:scale-95\"\r\n [class]=\"customValueTypeClass()\"\r\n (click)=\"insertCustomValue()\"\r\n [title]=\"t('insertType', { type: customValueType() })\"\r\n >\r\n @switch (customValueType()) {\r\n @case (\"number\") {\r\n <svg\r\n class=\"h-3 w-3\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M7 20l4-16m2 16l4-16M6 9h14M4 15h14\"\r\n />\r\n </svg>\r\n }\r\n @case (\"string\") {\r\n <svg\r\n class=\"h-3 w-3\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 01.865-.501 48.172 48.172 0 003.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z\"\r\n />\r\n </svg>\r\n }\r\n @default {\r\n <svg\r\n class=\"h-3 w-3\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M12 4.5v15m7.5-7.5h-15\"\r\n />\r\n </svg>\r\n }\r\n }\r\n <span class=\"max-w-16 truncate\">{{ customValuePreview() }}</span>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Main Content Row -->\r\n <div class=\"flex flex-1 overflow-hidden\">\r\n <!-- Vertical Tabs -->\r\n <div\r\n class=\"flex w-44 flex-col border-e border-slate-200 dark:border-slate-700\"\r\n >\r\n @for (tab of resolvedTabOptions(); track tab.value) {\r\n <button\r\n type=\"button\"\r\n class=\"flex cursor-pointer items-center gap-2 px-3 py-2.5 text-start text-sm font-medium transition-all duration-200\"\r\n [class]=\"\r\n activeTab() === tab.value\r\n ? 'bg-white border-e-2 border-primary text-primary dark:bg-slate-900'\r\n : searchQuery() && !tabHasResults(tab.value)\r\n ? 'text-slate-300 dark:text-slate-600'\r\n : 'text-slate-600 hover:bg-slate-100 hover:text-slate-800 dark:text-slate-300 dark:hover:bg-slate-700'\r\n \"\r\n (click)=\"setActiveTab(tab.value)\"\r\n >\r\n <!-- Tab Icons -->\r\n @switch (tab.value) {\r\n @case (\"functions\") {\r\n <svg\r\n class=\"h-5 w-5 shrink-0\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5\"\r\n />\r\n </svg>\r\n }\r\n @case (\"properties\") {\r\n <svg\r\n class=\"h-5 w-5 shrink-0\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M9.568 3H5.25A2.25 2.25 0 003 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 005.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 009.568 3z\"\r\n />\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M6 6h.008v.008H6V6z\"\r\n />\r\n </svg>\r\n }\r\n @case (\"operators\") {\r\n <svg\r\n class=\"h-5 w-5 shrink-0\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M15.75 15.75V18m-7.5-6.75h.008v.008H8.25v-.008zm0 2.25h.008v.008H8.25V13.5zm0 2.25h.008v.008H8.25v-.008zm0 2.25h.008v.008H8.25V18zm2.498-6.75h.007v.008h-.007v-.008zm0 2.25h.007v.008h-.007V13.5zm0 2.25h.007v.008h-.007v-.008zm0 2.25h.007v.008h-.007V18zm2.504-6.75h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V13.5zm0 2.25h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V18zm2.498-6.75h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V13.5zM8.25 6h7.5v2.25h-7.5V6zM12 2.25c-1.892 0-3.758.11-5.593.322C5.307 2.7 4.5 3.65 4.5 4.757V19.5a2.25 2.25 0 002.25 2.25h10.5a2.25 2.25 0 002.25-2.25V4.757c0-1.108-.806-2.057-1.907-2.185A48.507 48.507 0 0012 2.25z\"\r\n />\r\n </svg>\r\n }\r\n }\r\n <!-- Label (always visible) -->\r\n <span class=\"truncate whitespace-nowrap text-sm\">{{\r\n t(tab.value)\r\n }}</span>\r\n <!-- Count Badge -->\r\n @if (tab.value !== \"properties\" || !resolvedPropertiesTemplate()) {\r\n <span\r\n class=\"ms-auto rounded-full px-1.5 py-0.5 text-xs font-semibold\"\r\n [class]=\"\r\n activeTab() === tab.value\r\n ? 'bg-primary/10 text-primary'\r\n : 'bg-slate-200 text-slate-600 dark:bg-slate-600 dark:text-slate-300'\r\n \"\r\n >\r\n {{ getTabCount(tab.value) }}\r\n </span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Content Area -->\r\n <div class=\"flex flex-1 flex-col overflow-hidden\">\r\n <!-- Items Grid -->\r\n <div\r\n class=\"flex-1 p-2.5\"\r\n [class.overflow-y-auto]=\"\r\n activeTab() !== 'properties' || !resolvedPropertiesTemplate()\r\n \"\r\n >\r\n @switch (activeTab()) {\r\n @case (\"functions\") {\r\n @if (filteredCategories().length === 0) {\r\n <div\r\n class=\"flex h-full items-center justify-center text-sm text-slate-400 dark:text-slate-500\"\r\n >\r\n {{ labels()?.noFunctionsFound ?? t(\"noFunctionsFound\") }}\r\n </div>\r\n } @else {\r\n <div class=\"space-y-3\">\r\n @for (category of filteredCategories(); track category.name) {\r\n <div>\r\n <div class=\"mb-2 flex items-center gap-2\">\r\n <span\r\n class=\"text-sm font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400\"\r\n >\r\n {{ category.name }}\r\n </span>\r\n <span\r\n class=\"h-px flex-1 bg-slate-200/60 dark:bg-slate-700/60\"\r\n ></span>\r\n <span\r\n class=\"text-sm text-slate-400 dark:text-slate-500\"\r\n >{{ category.functions.length }}</span\r\n >\r\n </div>\r\n <div class=\"flex flex-wrap gap-1.5\">\r\n @for (fn of category.functions; track fn.name) {\r\n <mt-formula-toolbar-item\r\n type=\"function\"\r\n [value]=\"fn.name\"\r\n [description]=\"fn.description\"\r\n [signature]=\"fn.signature\"\r\n [examples]=\"fn.examples ?? []\"\r\n (onInsert)=\"insertBlock($event)\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n @case (\"properties\") {\r\n @if (resolvedPropertiesTemplate(); as template) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"template\"\r\n [ngTemplateOutletContext]=\"propertiesTemplateContext\"\r\n />\r\n } @else {\r\n @if (filteredProperties().length === 0) {\r\n <div\r\n class=\"flex h-full items-center justify-center text-sm text-slate-400 dark:text-slate-500\"\r\n >\r\n {{\r\n labels()?.noPropertiesAvailable ??\r\n t(\"noPropertiesAvailable\")\r\n }}\r\n </div>\r\n } @else {\r\n <div class=\"flex flex-wrap gap-1.5\">\r\n @for (prop of filteredProperties(); track prop) {\r\n <mt-formula-toolbar-item\r\n type=\"property\"\r\n [value]=\"prop\"\r\n [description]=\"t('property') + ': ' + prop\"\r\n propertyType=\"current\"\r\n (onInsert)=\"insertBlock($event)\"\r\n />\r\n }\r\n </div>\r\n }\r\n }\r\n }\r\n @case (\"operators\") {\r\n @if (filteredOperators().length === 0) {\r\n <div\r\n class=\"flex h-full items-center justify-center text-sm text-slate-400 dark:text-slate-500\"\r\n >\r\n {{ labels()?.noOperatorsFound ?? t(\"noOperatorsFound\") }}\r\n </div>\r\n } @else {\r\n <div class=\"space-y-3\">\r\n @for (group of operatorGroups(); track group.type) {\r\n <div>\r\n <div class=\"mb-2 flex items-center gap-2\">\r\n <span\r\n class=\"text-sm font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400\"\r\n >\r\n {{ group.type }}\r\n </span>\r\n <span\r\n class=\"h-px flex-1 bg-slate-200/60 dark:bg-slate-700/60\"\r\n ></span>\r\n </div>\r\n <div class=\"flex flex-wrap gap-1.5\">\r\n @for (op of group.operators; track op.symbol) {\r\n <mt-formula-toolbar-item\r\n type=\"operator\"\r\n [value]=\"op.symbol\"\r\n [description]=\"op.description\"\r\n (onInsert)=\"insertBlock($event)\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "directive", type: i3.TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: FormulaToolbarItem, selector: "mt-formula-toolbar-item", inputs: ["type", "value", "display", "description", "signature", "examples", "propertyType"], outputs: ["onInsert"] }] });
|
|
2250
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: FormulaToolbar, isStandalone: true, selector: "mt-formula-toolbar", inputs: { knownProperties: { classPropertyName: "knownProperties", publicName: "knownProperties", isSignal: true, isRequired: false, transformFunction: null }, propertiesTemplate: { classPropertyName: "propertiesTemplate", publicName: "propertiesTemplate", isSignal: true, isRequired: false, transformFunction: null }, functionCategories: { classPropertyName: "functionCategories", publicName: "functionCategories", isSignal: true, isRequired: false, transformFunction: null }, operators: { classPropertyName: "operators", publicName: "operators", isSignal: true, isRequired: false, transformFunction: null }, initialTab: { classPropertyName: "initialTab", publicName: "initialTab", isSignal: true, isRequired: false, transformFunction: null }, visibleTabs: { classPropertyName: "visibleTabs", publicName: "visibleTabs", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onBlockInsert: "onBlockInsert", onTabChange: "onTabChange" }, queries: [{ propertyName: "projectedPropertiesTemplate", first: true, predicate: ["properties"], descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0, template: "<div\r\n *transloco=\"let t; prefix: 'components.formula.toolbar'\"\r\n class=\"flex flex-col border-b border-slate-200 dark:border-slate-700\"\r\n [class.h-52]=\"activeTab() !== 'properties' || !resolvedPropertiesTemplate()\"\r\n>\r\n <!-- Top Bar: Search + Quick Insert -->\r\n <div\r\n class=\"flex items-center gap-2 border-b border-slate-200 px-3 py-2 dark:border-slate-700\"\r\n >\r\n <!-- Search Field -->\r\n <div class=\"relative flex-1\">\r\n <svg\r\n class=\"absolute start-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\r\n />\r\n </svg>\r\n <input\r\n type=\"text\"\r\n class=\"w-full rounded-lg border border-slate-300 bg-white py-1.5 ps-9 pe-8 text-sm text-slate-700 placeholder:text-slate-400 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 transition-all duration-200 dark:border-slate-600 dark:bg-slate-900 dark:text-slate-200 dark:placeholder:text-slate-500\"\r\n [placeholder]=\"searchPlaceholder() || t('search')\"\r\n [(ngModel)]=\"searchQuery\"\r\n />\r\n @if (searchQuery()) {\r\n <button\r\n type=\"button\"\r\n class=\"absolute end-2 top-1/2 -translate-y-1/2 rounded p-0.5 text-slate-400 hover:bg-slate-200 hover:text-slate-600 transition-all duration-200 dark:hover:bg-slate-700\"\r\n (click)=\"clearSearch()\"\r\n >\r\n <svg\r\n class=\"h-4 w-4\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M6 18L18 6M6 6l12 12\"\r\n />\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Separator -->\r\n <div class=\"h-6 w-px bg-slate-200 dark:bg-slate-600\"></div>\r\n\r\n <!-- Quick Insert Input -->\r\n <div class=\"relative flex items-center gap-2\">\r\n <span\r\n class=\"text-sm font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wide\"\r\n >{{ t(\"insertLabel\") }}:</span\r\n >\r\n <div class=\"relative\">\r\n <input\r\n #customValueInput\r\n type=\"text\"\r\n class=\"w-32 rounded-md border border-dashed border-slate-300 bg-slate-50 px-3 py-1.5 text-sm font-mono text-slate-700 placeholder:text-slate-400 focus:border-primary focus:bg-white focus:outline-none focus:ring-2 focus:ring-primary/20 transition-all duration-200 dark:border-slate-600 dark:bg-slate-800 dark:text-slate-200 dark:placeholder:text-slate-500 dark:focus:bg-slate-900\"\r\n [placeholder]=\"t('customValuePlaceholder')\"\r\n [(ngModel)]=\"customValue\"\r\n (keydown.enter)=\"insertCustomValue()\"\r\n (keydown.escape)=\"clearCustomValue()\"\r\n />\r\n </div>\r\n\r\n <!-- Insert Button with Type Preview -->\r\n @if (customValue()) {\r\n <button\r\n type=\"button\"\r\n class=\"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-all duration-200 hover:shadow-sm active:scale-95\"\r\n [class]=\"customValueTypeClass()\"\r\n (click)=\"insertCustomValue()\"\r\n [title]=\"t('insertType', { type: customValueType() })\"\r\n >\r\n @switch (customValueType()) {\r\n @case (\"number\") {\r\n <svg\r\n class=\"h-3 w-3\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M7 20l4-16m2 16l4-16M6 9h14M4 15h14\"\r\n />\r\n </svg>\r\n }\r\n @case (\"string\") {\r\n <svg\r\n class=\"h-3 w-3\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 01.865-.501 48.172 48.172 0 003.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z\"\r\n />\r\n </svg>\r\n }\r\n @default {\r\n <svg\r\n class=\"h-3 w-3\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M12 4.5v15m7.5-7.5h-15\"\r\n />\r\n </svg>\r\n }\r\n }\r\n <span class=\"max-w-16 truncate\">{{ customValuePreview() }}</span>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Main Content Row -->\r\n <div class=\"flex flex-1 overflow-hidden\">\r\n <!-- Vertical Tabs -->\r\n <div\r\n class=\"flex w-44 flex-col border-e border-slate-200 dark:border-slate-700\"\r\n >\r\n @for (tab of resolvedTabOptions(); track tab.value) {\r\n <button\r\n type=\"button\"\r\n class=\"flex cursor-pointer items-center gap-2 px-3 py-2.5 text-start text-sm font-medium transition-all duration-200\"\r\n [class]=\"\r\n activeTab() === tab.value\r\n ? 'bg-white border-e-2 border-primary text-primary dark:bg-slate-900'\r\n : searchQuery() && !tabHasResults(tab.value)\r\n ? 'text-slate-300 dark:text-slate-600'\r\n : 'text-slate-600 hover:bg-slate-100 hover:text-slate-800 dark:text-slate-300 dark:hover:bg-slate-700'\r\n \"\r\n (click)=\"setActiveTab(tab.value)\"\r\n >\r\n <!-- Tab Icons -->\r\n @switch (tab.value) {\r\n @case (\"functions\") {\r\n <svg\r\n class=\"h-5 w-5 shrink-0\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5\"\r\n />\r\n </svg>\r\n }\r\n @case (\"properties\") {\r\n <svg\r\n class=\"h-5 w-5 shrink-0\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M9.568 3H5.25A2.25 2.25 0 003 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 005.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 009.568 3z\"\r\n />\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M6 6h.008v.008H6V6z\"\r\n />\r\n </svg>\r\n }\r\n @case (\"operators\") {\r\n <svg\r\n class=\"h-5 w-5 shrink-0\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M15.75 15.75V18m-7.5-6.75h.008v.008H8.25v-.008zm0 2.25h.008v.008H8.25V13.5zm0 2.25h.008v.008H8.25v-.008zm0 2.25h.008v.008H8.25V18zm2.498-6.75h.007v.008h-.007v-.008zm0 2.25h.007v.008h-.007V13.5zm0 2.25h.007v.008h-.007v-.008zm0 2.25h.007v.008h-.007V18zm2.504-6.75h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V13.5zm0 2.25h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V18zm2.498-6.75h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V13.5zM8.25 6h7.5v2.25h-7.5V6zM12 2.25c-1.892 0-3.758.11-5.593.322C5.307 2.7 4.5 3.65 4.5 4.757V19.5a2.25 2.25 0 002.25 2.25h10.5a2.25 2.25 0 002.25-2.25V4.757c0-1.108-.806-2.057-1.907-2.185A48.507 48.507 0 0012 2.25z\"\r\n />\r\n </svg>\r\n }\r\n }\r\n <!-- Label (always visible) -->\r\n <span class=\"truncate whitespace-nowrap text-sm\">{{\r\n t(tab.value)\r\n }}</span>\r\n <!-- Count Badge -->\r\n @if (tab.value !== \"properties\" || !resolvedPropertiesTemplate()) {\r\n <span\r\n class=\"ms-auto rounded-full px-1.5 py-0.5 text-xs font-semibold\"\r\n [class]=\"\r\n activeTab() === tab.value\r\n ? 'bg-primary/10 text-primary'\r\n : 'bg-slate-200 text-slate-600 dark:bg-slate-600 dark:text-slate-300'\r\n \"\r\n >\r\n {{ getTabCount(tab.value) }}\r\n </span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Content Area -->\r\n <div class=\"flex flex-1 flex-col overflow-hidden\">\r\n <!-- Items Grid -->\r\n <div\r\n class=\"flex-1 p-2.5\"\r\n [class.overflow-y-auto]=\"\r\n activeTab() !== 'properties' || !resolvedPropertiesTemplate()\r\n \"\r\n >\r\n @switch (activeTab()) {\r\n @case (\"functions\") {\r\n @if (filteredCategories().length === 0) {\r\n <div\r\n class=\"flex h-full items-center justify-center text-sm text-slate-400 dark:text-slate-500\"\r\n >\r\n {{ labels()?.noFunctionsFound ?? t(\"noFunctionsFound\") }}\r\n </div>\r\n } @else {\r\n <div class=\"space-y-3\">\r\n @for (category of filteredCategories(); track category.name) {\r\n <div>\r\n <div class=\"mb-2 flex items-center gap-2\">\r\n <span\r\n class=\"text-sm font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400\"\r\n >\r\n {{ category.displayName || category.name }}\r\n </span>\r\n <span\r\n class=\"h-px flex-1 bg-slate-200/60 dark:bg-slate-700/60\"\r\n ></span>\r\n <span\r\n class=\"text-sm text-slate-400 dark:text-slate-500\"\r\n >{{ category.functions.length }}</span\r\n >\r\n </div>\r\n <div class=\"flex flex-wrap gap-1.5\">\r\n @for (fn of category.functions; track fn.name) {\r\n <mt-formula-toolbar-item\r\n type=\"function\"\r\n [value]=\"fn.name\"\r\n [description]=\"fn.description\"\r\n [signature]=\"fn.signature\"\r\n [template]=\"fn.template\"\r\n [templateCursorOffset]=\"fn.templateCursorOffset\"\r\n [returnType]=\"fn.returnType\"\r\n [parameters]=\"fn.parameters ?? []\"\r\n [examples]=\"fn.examples ?? []\"\r\n [hints]=\"fn.hints ?? []\"\r\n (onInsert)=\"insertBlock($event)\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n @case (\"properties\") {\r\n @if (resolvedPropertiesTemplate(); as template) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"template\"\r\n [ngTemplateOutletContext]=\"propertiesTemplateContext\"\r\n />\r\n } @else {\r\n @if (filteredProperties().length === 0) {\r\n <div\r\n class=\"flex h-full items-center justify-center text-sm text-slate-400 dark:text-slate-500\"\r\n >\r\n {{\r\n labels()?.noPropertiesAvailable ??\r\n t(\"noPropertiesAvailable\")\r\n }}\r\n </div>\r\n } @else {\r\n <div class=\"flex flex-wrap gap-1.5\">\r\n @for (prop of filteredProperties(); track prop) {\r\n <mt-formula-toolbar-item\r\n type=\"property\"\r\n [value]=\"prop\"\r\n [description]=\"t('property') + ': ' + prop\"\r\n propertyType=\"current\"\r\n (onInsert)=\"insertBlock($event)\"\r\n />\r\n }\r\n </div>\r\n }\r\n }\r\n }\r\n @case (\"operators\") {\r\n @if (filteredOperators().length === 0) {\r\n <div\r\n class=\"flex h-full items-center justify-center text-sm text-slate-400 dark:text-slate-500\"\r\n >\r\n {{ labels()?.noOperatorsFound ?? t(\"noOperatorsFound\") }}\r\n </div>\r\n } @else {\r\n <div class=\"space-y-3\">\r\n @for (group of operatorGroups(); track group.type) {\r\n <div>\r\n <div class=\"mb-2 flex items-center gap-2\">\r\n <span\r\n class=\"text-sm font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400\"\r\n >\r\n {{ group.type }}\r\n </span>\r\n <span\r\n class=\"h-px flex-1 bg-slate-200/60 dark:bg-slate-700/60\"\r\n ></span>\r\n </div>\r\n <div class=\"flex flex-wrap gap-1.5\">\r\n @for (op of group.operators; track op.symbol) {\r\n <mt-formula-toolbar-item\r\n type=\"operator\"\r\n [value]=\"op.symbol\"\r\n [description]=\"op.description\"\r\n (onInsert)=\"insertBlock($event)\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "directive", type: i3.TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: FormulaToolbarItem, selector: "mt-formula-toolbar-item", inputs: ["type", "value", "display", "description", "signature", "examples", "template", "templateCursorOffset", "hints", "returnType", "parameters", "propertyType"], outputs: ["onInsert"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1883
2251
|
}
|
|
1884
2252
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FormulaToolbar, decorators: [{
|
|
1885
2253
|
type: Component,
|
|
1886
|
-
args: [{ selector: 'mt-formula-toolbar', standalone: true, imports: [CommonModule, FormsModule, TranslocoModule, FormulaToolbarItem], template: "<div\r\n *transloco=\"let t; prefix: 'components.formula.toolbar'\"\r\n class=\"flex flex-col border-b border-slate-200 dark:border-slate-700\"\r\n [class.h-52]=\"activeTab() !== 'properties' || !resolvedPropertiesTemplate()\"\r\n>\r\n <!-- Top Bar: Search + Quick Insert -->\r\n <div\r\n class=\"flex items-center gap-2 border-b border-slate-200 px-3 py-2 dark:border-slate-700\"\r\n >\r\n <!-- Search Field -->\r\n <div class=\"relative flex-1\">\r\n <svg\r\n class=\"absolute start-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\r\n />\r\n </svg>\r\n <input\r\n type=\"text\"\r\n class=\"w-full rounded-lg border border-slate-300 bg-white py-1.5 ps-9 pe-8 text-sm text-slate-700 placeholder:text-slate-400 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 transition-all duration-200 dark:border-slate-600 dark:bg-slate-900 dark:text-slate-200 dark:placeholder:text-slate-500\"\r\n [placeholder]=\"searchPlaceholder() || t('search')\"\r\n [(ngModel)]=\"searchQuery\"\r\n />\r\n @if (searchQuery()) {\r\n <button\r\n type=\"button\"\r\n class=\"absolute end-2 top-1/2 -translate-y-1/2 rounded p-0.5 text-slate-400 hover:bg-slate-200 hover:text-slate-600 transition-all duration-200 dark:hover:bg-slate-700\"\r\n (click)=\"clearSearch()\"\r\n >\r\n <svg\r\n class=\"h-4 w-4\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M6 18L18 6M6 6l12 12\"\r\n />\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Separator -->\r\n <div class=\"h-6 w-px bg-slate-200 dark:bg-slate-600\"></div>\r\n\r\n <!-- Quick Insert Input -->\r\n <div class=\"relative flex items-center gap-2\">\r\n <span\r\n class=\"text-sm font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wide\"\r\n >{{ t(\"insertLabel\") }}:</span\r\n >\r\n <div class=\"relative\">\r\n <input\r\n #customValueInput\r\n type=\"text\"\r\n class=\"w-32 rounded-md border border-dashed border-slate-300 bg-slate-50 px-3 py-1.5 text-sm font-mono text-slate-700 placeholder:text-slate-400 focus:border-primary focus:bg-white focus:outline-none focus:ring-2 focus:ring-primary/20 transition-all duration-200 dark:border-slate-600 dark:bg-slate-800 dark:text-slate-200 dark:placeholder:text-slate-500 dark:focus:bg-slate-900\"\r\n [placeholder]=\"t('customValuePlaceholder')\"\r\n [(ngModel)]=\"customValue\"\r\n (keydown.enter)=\"insertCustomValue()\"\r\n (keydown.escape)=\"clearCustomValue()\"\r\n />\r\n </div>\r\n\r\n <!-- Insert Button with Type Preview -->\r\n @if (customValue()) {\r\n <button\r\n type=\"button\"\r\n class=\"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-all duration-200 hover:shadow-sm active:scale-95\"\r\n [class]=\"customValueTypeClass()\"\r\n (click)=\"insertCustomValue()\"\r\n [title]=\"t('insertType', { type: customValueType() })\"\r\n >\r\n @switch (customValueType()) {\r\n @case (\"number\") {\r\n <svg\r\n class=\"h-3 w-3\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M7 20l4-16m2 16l4-16M6 9h14M4 15h14\"\r\n />\r\n </svg>\r\n }\r\n @case (\"string\") {\r\n <svg\r\n class=\"h-3 w-3\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 01.865-.501 48.172 48.172 0 003.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z\"\r\n />\r\n </svg>\r\n }\r\n @default {\r\n <svg\r\n class=\"h-3 w-3\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M12 4.5v15m7.5-7.5h-15\"\r\n />\r\n </svg>\r\n }\r\n }\r\n <span class=\"max-w-16 truncate\">{{ customValuePreview() }}</span>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Main Content Row -->\r\n <div class=\"flex flex-1 overflow-hidden\">\r\n <!-- Vertical Tabs -->\r\n <div\r\n class=\"flex w-44 flex-col border-e border-slate-200 dark:border-slate-700\"\r\n >\r\n @for (tab of resolvedTabOptions(); track tab.value) {\r\n <button\r\n type=\"button\"\r\n class=\"flex cursor-pointer items-center gap-2 px-3 py-2.5 text-start text-sm font-medium transition-all duration-200\"\r\n [class]=\"\r\n activeTab() === tab.value\r\n ? 'bg-white border-e-2 border-primary text-primary dark:bg-slate-900'\r\n : searchQuery() && !tabHasResults(tab.value)\r\n ? 'text-slate-300 dark:text-slate-600'\r\n : 'text-slate-600 hover:bg-slate-100 hover:text-slate-800 dark:text-slate-300 dark:hover:bg-slate-700'\r\n \"\r\n (click)=\"setActiveTab(tab.value)\"\r\n >\r\n <!-- Tab Icons -->\r\n @switch (tab.value) {\r\n @case (\"functions\") {\r\n <svg\r\n class=\"h-5 w-5 shrink-0\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5\"\r\n />\r\n </svg>\r\n }\r\n @case (\"properties\") {\r\n <svg\r\n class=\"h-5 w-5 shrink-0\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M9.568 3H5.25A2.25 2.25 0 003 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 005.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 009.568 3z\"\r\n />\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M6 6h.008v.008H6V6z\"\r\n />\r\n </svg>\r\n }\r\n @case (\"operators\") {\r\n <svg\r\n class=\"h-5 w-5 shrink-0\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M15.75 15.75V18m-7.5-6.75h.008v.008H8.25v-.008zm0 2.25h.008v.008H8.25V13.5zm0 2.25h.008v.008H8.25v-.008zm0 2.25h.008v.008H8.25V18zm2.498-6.75h.007v.008h-.007v-.008zm0 2.25h.007v.008h-.007V13.5zm0 2.25h.007v.008h-.007v-.008zm0 2.25h.007v.008h-.007V18zm2.504-6.75h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V13.5zm0 2.25h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V18zm2.498-6.75h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V13.5zM8.25 6h7.5v2.25h-7.5V6zM12 2.25c-1.892 0-3.758.11-5.593.322C5.307 2.7 4.5 3.65 4.5 4.757V19.5a2.25 2.25 0 002.25 2.25h10.5a2.25 2.25 0 002.25-2.25V4.757c0-1.108-.806-2.057-1.907-2.185A48.507 48.507 0 0012 2.25z\"\r\n />\r\n </svg>\r\n }\r\n }\r\n <!-- Label (always visible) -->\r\n <span class=\"truncate whitespace-nowrap text-sm\">{{\r\n t(tab.value)\r\n }}</span>\r\n <!-- Count Badge -->\r\n @if (tab.value !== \"properties\" || !resolvedPropertiesTemplate()) {\r\n <span\r\n class=\"ms-auto rounded-full px-1.5 py-0.5 text-xs font-semibold\"\r\n [class]=\"\r\n activeTab() === tab.value\r\n ? 'bg-primary/10 text-primary'\r\n : 'bg-slate-200 text-slate-600 dark:bg-slate-600 dark:text-slate-300'\r\n \"\r\n >\r\n {{ getTabCount(tab.value) }}\r\n </span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Content Area -->\r\n <div class=\"flex flex-1 flex-col overflow-hidden\">\r\n <!-- Items Grid -->\r\n <div\r\n class=\"flex-1 p-2.5\"\r\n [class.overflow-y-auto]=\"\r\n activeTab() !== 'properties' || !resolvedPropertiesTemplate()\r\n \"\r\n >\r\n @switch (activeTab()) {\r\n @case (\"functions\") {\r\n @if (filteredCategories().length === 0) {\r\n <div\r\n class=\"flex h-full items-center justify-center text-sm text-slate-400 dark:text-slate-500\"\r\n >\r\n {{ labels()?.noFunctionsFound ?? t(\"noFunctionsFound\") }}\r\n </div>\r\n } @else {\r\n <div class=\"space-y-3\">\r\n @for (category of filteredCategories(); track category.name) {\r\n <div>\r\n <div class=\"mb-2 flex items-center gap-2\">\r\n <span\r\n class=\"text-sm font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400\"\r\n >\r\n {{ category.name }}\r\n </span>\r\n <span\r\n class=\"h-px flex-1 bg-slate-200/60 dark:bg-slate-700/60\"\r\n ></span>\r\n <span\r\n class=\"text-sm text-slate-400 dark:text-slate-500\"\r\n >{{ category.functions.length }}</span\r\n >\r\n </div>\r\n <div class=\"flex flex-wrap gap-1.5\">\r\n @for (fn of category.functions; track fn.name) {\r\n <mt-formula-toolbar-item\r\n type=\"function\"\r\n [value]=\"fn.name\"\r\n [description]=\"fn.description\"\r\n [signature]=\"fn.signature\"\r\n [examples]=\"fn.examples ?? []\"\r\n (onInsert)=\"insertBlock($event)\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n @case (\"properties\") {\r\n @if (resolvedPropertiesTemplate(); as template) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"template\"\r\n [ngTemplateOutletContext]=\"propertiesTemplateContext\"\r\n />\r\n } @else {\r\n @if (filteredProperties().length === 0) {\r\n <div\r\n class=\"flex h-full items-center justify-center text-sm text-slate-400 dark:text-slate-500\"\r\n >\r\n {{\r\n labels()?.noPropertiesAvailable ??\r\n t(\"noPropertiesAvailable\")\r\n }}\r\n </div>\r\n } @else {\r\n <div class=\"flex flex-wrap gap-1.5\">\r\n @for (prop of filteredProperties(); track prop) {\r\n <mt-formula-toolbar-item\r\n type=\"property\"\r\n [value]=\"prop\"\r\n [description]=\"t('property') + ': ' + prop\"\r\n propertyType=\"current\"\r\n (onInsert)=\"insertBlock($event)\"\r\n />\r\n }\r\n </div>\r\n }\r\n }\r\n }\r\n @case (\"operators\") {\r\n @if (filteredOperators().length === 0) {\r\n <div\r\n class=\"flex h-full items-center justify-center text-sm text-slate-400 dark:text-slate-500\"\r\n >\r\n {{ labels()?.noOperatorsFound ?? t(\"noOperatorsFound\") }}\r\n </div>\r\n } @else {\r\n <div class=\"space-y-3\">\r\n @for (group of operatorGroups(); track group.type) {\r\n <div>\r\n <div class=\"mb-2 flex items-center gap-2\">\r\n <span\r\n class=\"text-sm font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400\"\r\n >\r\n {{ group.type }}\r\n </span>\r\n <span\r\n class=\"h-px flex-1 bg-slate-200/60 dark:bg-slate-700/60\"\r\n ></span>\r\n </div>\r\n <div class=\"flex flex-wrap gap-1.5\">\r\n @for (op of group.operators; track op.symbol) {\r\n <mt-formula-toolbar-item\r\n type=\"operator\"\r\n [value]=\"op.symbol\"\r\n [description]=\"op.description\"\r\n (onInsert)=\"insertBlock($event)\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n" }]
|
|
2254
|
+
args: [{ selector: 'mt-formula-toolbar', standalone: true, imports: [CommonModule, FormsModule, TranslocoModule, FormulaToolbarItem], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\r\n *transloco=\"let t; prefix: 'components.formula.toolbar'\"\r\n class=\"flex flex-col border-b border-slate-200 dark:border-slate-700\"\r\n [class.h-52]=\"activeTab() !== 'properties' || !resolvedPropertiesTemplate()\"\r\n>\r\n <!-- Top Bar: Search + Quick Insert -->\r\n <div\r\n class=\"flex items-center gap-2 border-b border-slate-200 px-3 py-2 dark:border-slate-700\"\r\n >\r\n <!-- Search Field -->\r\n <div class=\"relative flex-1\">\r\n <svg\r\n class=\"absolute start-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\r\n />\r\n </svg>\r\n <input\r\n type=\"text\"\r\n class=\"w-full rounded-lg border border-slate-300 bg-white py-1.5 ps-9 pe-8 text-sm text-slate-700 placeholder:text-slate-400 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 transition-all duration-200 dark:border-slate-600 dark:bg-slate-900 dark:text-slate-200 dark:placeholder:text-slate-500\"\r\n [placeholder]=\"searchPlaceholder() || t('search')\"\r\n [(ngModel)]=\"searchQuery\"\r\n />\r\n @if (searchQuery()) {\r\n <button\r\n type=\"button\"\r\n class=\"absolute end-2 top-1/2 -translate-y-1/2 rounded p-0.5 text-slate-400 hover:bg-slate-200 hover:text-slate-600 transition-all duration-200 dark:hover:bg-slate-700\"\r\n (click)=\"clearSearch()\"\r\n >\r\n <svg\r\n class=\"h-4 w-4\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M6 18L18 6M6 6l12 12\"\r\n />\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Separator -->\r\n <div class=\"h-6 w-px bg-slate-200 dark:bg-slate-600\"></div>\r\n\r\n <!-- Quick Insert Input -->\r\n <div class=\"relative flex items-center gap-2\">\r\n <span\r\n class=\"text-sm font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wide\"\r\n >{{ t(\"insertLabel\") }}:</span\r\n >\r\n <div class=\"relative\">\r\n <input\r\n #customValueInput\r\n type=\"text\"\r\n class=\"w-32 rounded-md border border-dashed border-slate-300 bg-slate-50 px-3 py-1.5 text-sm font-mono text-slate-700 placeholder:text-slate-400 focus:border-primary focus:bg-white focus:outline-none focus:ring-2 focus:ring-primary/20 transition-all duration-200 dark:border-slate-600 dark:bg-slate-800 dark:text-slate-200 dark:placeholder:text-slate-500 dark:focus:bg-slate-900\"\r\n [placeholder]=\"t('customValuePlaceholder')\"\r\n [(ngModel)]=\"customValue\"\r\n (keydown.enter)=\"insertCustomValue()\"\r\n (keydown.escape)=\"clearCustomValue()\"\r\n />\r\n </div>\r\n\r\n <!-- Insert Button with Type Preview -->\r\n @if (customValue()) {\r\n <button\r\n type=\"button\"\r\n class=\"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-all duration-200 hover:shadow-sm active:scale-95\"\r\n [class]=\"customValueTypeClass()\"\r\n (click)=\"insertCustomValue()\"\r\n [title]=\"t('insertType', { type: customValueType() })\"\r\n >\r\n @switch (customValueType()) {\r\n @case (\"number\") {\r\n <svg\r\n class=\"h-3 w-3\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M7 20l4-16m2 16l4-16M6 9h14M4 15h14\"\r\n />\r\n </svg>\r\n }\r\n @case (\"string\") {\r\n <svg\r\n class=\"h-3 w-3\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 01.865-.501 48.172 48.172 0 003.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z\"\r\n />\r\n </svg>\r\n }\r\n @default {\r\n <svg\r\n class=\"h-3 w-3\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M12 4.5v15m7.5-7.5h-15\"\r\n />\r\n </svg>\r\n }\r\n }\r\n <span class=\"max-w-16 truncate\">{{ customValuePreview() }}</span>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Main Content Row -->\r\n <div class=\"flex flex-1 overflow-hidden\">\r\n <!-- Vertical Tabs -->\r\n <div\r\n class=\"flex w-44 flex-col border-e border-slate-200 dark:border-slate-700\"\r\n >\r\n @for (tab of resolvedTabOptions(); track tab.value) {\r\n <button\r\n type=\"button\"\r\n class=\"flex cursor-pointer items-center gap-2 px-3 py-2.5 text-start text-sm font-medium transition-all duration-200\"\r\n [class]=\"\r\n activeTab() === tab.value\r\n ? 'bg-white border-e-2 border-primary text-primary dark:bg-slate-900'\r\n : searchQuery() && !tabHasResults(tab.value)\r\n ? 'text-slate-300 dark:text-slate-600'\r\n : 'text-slate-600 hover:bg-slate-100 hover:text-slate-800 dark:text-slate-300 dark:hover:bg-slate-700'\r\n \"\r\n (click)=\"setActiveTab(tab.value)\"\r\n >\r\n <!-- Tab Icons -->\r\n @switch (tab.value) {\r\n @case (\"functions\") {\r\n <svg\r\n class=\"h-5 w-5 shrink-0\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5\"\r\n />\r\n </svg>\r\n }\r\n @case (\"properties\") {\r\n <svg\r\n class=\"h-5 w-5 shrink-0\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M9.568 3H5.25A2.25 2.25 0 003 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 005.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 009.568 3z\"\r\n />\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M6 6h.008v.008H6V6z\"\r\n />\r\n </svg>\r\n }\r\n @case (\"operators\") {\r\n <svg\r\n class=\"h-5 w-5 shrink-0\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M15.75 15.75V18m-7.5-6.75h.008v.008H8.25v-.008zm0 2.25h.008v.008H8.25V13.5zm0 2.25h.008v.008H8.25v-.008zm0 2.25h.008v.008H8.25V18zm2.498-6.75h.007v.008h-.007v-.008zm0 2.25h.007v.008h-.007V13.5zm0 2.25h.007v.008h-.007v-.008zm0 2.25h.007v.008h-.007V18zm2.504-6.75h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V13.5zm0 2.25h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V18zm2.498-6.75h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V13.5zM8.25 6h7.5v2.25h-7.5V6zM12 2.25c-1.892 0-3.758.11-5.593.322C5.307 2.7 4.5 3.65 4.5 4.757V19.5a2.25 2.25 0 002.25 2.25h10.5a2.25 2.25 0 002.25-2.25V4.757c0-1.108-.806-2.057-1.907-2.185A48.507 48.507 0 0012 2.25z\"\r\n />\r\n </svg>\r\n }\r\n }\r\n <!-- Label (always visible) -->\r\n <span class=\"truncate whitespace-nowrap text-sm\">{{\r\n t(tab.value)\r\n }}</span>\r\n <!-- Count Badge -->\r\n @if (tab.value !== \"properties\" || !resolvedPropertiesTemplate()) {\r\n <span\r\n class=\"ms-auto rounded-full px-1.5 py-0.5 text-xs font-semibold\"\r\n [class]=\"\r\n activeTab() === tab.value\r\n ? 'bg-primary/10 text-primary'\r\n : 'bg-slate-200 text-slate-600 dark:bg-slate-600 dark:text-slate-300'\r\n \"\r\n >\r\n {{ getTabCount(tab.value) }}\r\n </span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Content Area -->\r\n <div class=\"flex flex-1 flex-col overflow-hidden\">\r\n <!-- Items Grid -->\r\n <div\r\n class=\"flex-1 p-2.5\"\r\n [class.overflow-y-auto]=\"\r\n activeTab() !== 'properties' || !resolvedPropertiesTemplate()\r\n \"\r\n >\r\n @switch (activeTab()) {\r\n @case (\"functions\") {\r\n @if (filteredCategories().length === 0) {\r\n <div\r\n class=\"flex h-full items-center justify-center text-sm text-slate-400 dark:text-slate-500\"\r\n >\r\n {{ labels()?.noFunctionsFound ?? t(\"noFunctionsFound\") }}\r\n </div>\r\n } @else {\r\n <div class=\"space-y-3\">\r\n @for (category of filteredCategories(); track category.name) {\r\n <div>\r\n <div class=\"mb-2 flex items-center gap-2\">\r\n <span\r\n class=\"text-sm font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400\"\r\n >\r\n {{ category.displayName || category.name }}\r\n </span>\r\n <span\r\n class=\"h-px flex-1 bg-slate-200/60 dark:bg-slate-700/60\"\r\n ></span>\r\n <span\r\n class=\"text-sm text-slate-400 dark:text-slate-500\"\r\n >{{ category.functions.length }}</span\r\n >\r\n </div>\r\n <div class=\"flex flex-wrap gap-1.5\">\r\n @for (fn of category.functions; track fn.name) {\r\n <mt-formula-toolbar-item\r\n type=\"function\"\r\n [value]=\"fn.name\"\r\n [description]=\"fn.description\"\r\n [signature]=\"fn.signature\"\r\n [template]=\"fn.template\"\r\n [templateCursorOffset]=\"fn.templateCursorOffset\"\r\n [returnType]=\"fn.returnType\"\r\n [parameters]=\"fn.parameters ?? []\"\r\n [examples]=\"fn.examples ?? []\"\r\n [hints]=\"fn.hints ?? []\"\r\n (onInsert)=\"insertBlock($event)\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n @case (\"properties\") {\r\n @if (resolvedPropertiesTemplate(); as template) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"template\"\r\n [ngTemplateOutletContext]=\"propertiesTemplateContext\"\r\n />\r\n } @else {\r\n @if (filteredProperties().length === 0) {\r\n <div\r\n class=\"flex h-full items-center justify-center text-sm text-slate-400 dark:text-slate-500\"\r\n >\r\n {{\r\n labels()?.noPropertiesAvailable ??\r\n t(\"noPropertiesAvailable\")\r\n }}\r\n </div>\r\n } @else {\r\n <div class=\"flex flex-wrap gap-1.5\">\r\n @for (prop of filteredProperties(); track prop) {\r\n <mt-formula-toolbar-item\r\n type=\"property\"\r\n [value]=\"prop\"\r\n [description]=\"t('property') + ': ' + prop\"\r\n propertyType=\"current\"\r\n (onInsert)=\"insertBlock($event)\"\r\n />\r\n }\r\n </div>\r\n }\r\n }\r\n }\r\n @case (\"operators\") {\r\n @if (filteredOperators().length === 0) {\r\n <div\r\n class=\"flex h-full items-center justify-center text-sm text-slate-400 dark:text-slate-500\"\r\n >\r\n {{ labels()?.noOperatorsFound ?? t(\"noOperatorsFound\") }}\r\n </div>\r\n } @else {\r\n <div class=\"space-y-3\">\r\n @for (group of operatorGroups(); track group.type) {\r\n <div>\r\n <div class=\"mb-2 flex items-center gap-2\">\r\n <span\r\n class=\"text-sm font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400\"\r\n >\r\n {{ group.type }}\r\n </span>\r\n <span\r\n class=\"h-px flex-1 bg-slate-200/60 dark:bg-slate-700/60\"\r\n ></span>\r\n </div>\r\n <div class=\"flex flex-wrap gap-1.5\">\r\n @for (op of group.operators; track op.symbol) {\r\n <mt-formula-toolbar-item\r\n type=\"operator\"\r\n [value]=\"op.symbol\"\r\n [description]=\"op.description\"\r\n (onInsert)=\"insertBlock($event)\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n" }]
|
|
1887
2255
|
}], ctorParameters: () => [], propDecorators: { knownProperties: [{ type: i0.Input, args: [{ isSignal: true, alias: "knownProperties", required: false }] }], propertiesTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "propertiesTemplate", required: false }] }], projectedPropertiesTemplate: [{ type: i0.ContentChild, args: ['properties', { ...{
|
|
1888
2256
|
read: TemplateRef,
|
|
1889
2257
|
}, isSignal: true }] }], functionCategories: [{ type: i0.Input, args: [{ isSignal: true, alias: "functionCategories", required: false }] }], operators: [{ type: i0.Input, args: [{ isSignal: true, alias: "operators", required: false }] }], initialTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialTab", required: false }] }], visibleTabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "visibleTabs", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], onBlockInsert: [{ type: i0.Output, args: ["onBlockInsert"] }], onTabChange: [{ type: i0.Output, args: ["onTabChange"] }] } });
|
|
@@ -3112,5 +3480,5 @@ function createRuntimeError(code, error) {
|
|
|
3112
3480
|
* Generated bundle index. Do not edit.
|
|
3113
3481
|
*/
|
|
3114
3482
|
|
|
3115
|
-
export { CONDITION_FUNCTION_CATEGORIES, CONDITION_OPERATORS, CONDITION_RUNTIME_CATALOG, DEFAULT_OPERATORS, FormulaEditor, FormulaEditorCode, FormulaRuntimeEngine, FormulaStatusBar, FormulaToolbar, FormulaToolbarItem, VALIDATION_FUNCTION_CATEGORIES, VALIDATION_OPERATORS, VALIDATION_RUNTIME_CATALOG, cloneBlock, cloneToken, cloneTokens, createFunctionBlock, createFunctionTokens, createLiteralBlock, createLiteralToken, createOperatorBlock, createOperatorToken, createPropertyBlock, createPropertyToken, findFunctionRange, generateFunctionId, generateSmartBlockId, generateTokenId, getArgumentIndexAtPosition, getFunctionTokens, isValidDropPosition, parseSignature, recalculateDepths, serializeTokens };
|
|
3483
|
+
export { CONDITION_FUNCTION_CATEGORIES, CONDITION_OPERATORS, CONDITION_RUNTIME_CATALOG, DEFAULT_OPERATORS, FormulaEditor, FormulaEditorCode, FormulaRuntimeEngine, FormulaStatusBar, FormulaToolbar, FormulaToolbarItem, VALIDATION_FUNCTION_CATEGORIES, VALIDATION_OPERATORS, VALIDATION_RUNTIME_CATALOG, cloneBlock, cloneToken, cloneTokens, createFunctionBlock, createFunctionTokens, createLiteralBlock, createLiteralToken, createOperatorBlock, createOperatorToken, createPropertyBlock, createPropertyToken, createTextToken, findFunctionRange, generateFunctionId, generateSmartBlockId, generateTokenId, getArgumentIndexAtPosition, getFunctionTokens, isValidDropPosition, parseSignature, recalculateDepths, serializeTokens, tokenizeFormulaTemplate };
|
|
3116
3484
|
//# sourceMappingURL=masterteam-components-formula.mjs.map
|