@muxima-ui/core 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,115 @@
1
+ /* ---------- Kanban ---------- */
2
+
3
+ .mx-kanban {
4
+ display: block;
5
+ width: 100%;
6
+ font-family: var(--mx-font-sans);
7
+ }
8
+ .mx-kanban-board {
9
+ display: flex;
10
+ align-items: flex-start;
11
+ gap: 12px;
12
+ padding-bottom: 6px;
13
+ overflow-x: auto;
14
+ }
15
+ .mx-kanban-column {
16
+ display: flex;
17
+ flex: 0 0 272px;
18
+ flex-direction: column;
19
+ gap: 8px;
20
+ box-sizing: border-box;
21
+ max-height: 100%;
22
+ padding: 10px;
23
+ border-radius: var(--mx-radius-surface);
24
+ background: var(--mx-subtle);
25
+ }
26
+ .mx-kanban-column-head {
27
+ display: flex;
28
+ align-items: center;
29
+ justify-content: space-between;
30
+ gap: 8px;
31
+ padding: 2px 4px;
32
+ }
33
+ .mx-kanban-column-title {
34
+ margin: 0;
35
+ color: var(--mx-ink);
36
+ font-size: var(--mx-text-sm);
37
+ font-weight: 600;
38
+ }
39
+ .mx-kanban-count {
40
+ padding: 1px 8px;
41
+ border-radius: 999px;
42
+ background: var(--mx-surface);
43
+ color: var(--mx-muted);
44
+ font-size: var(--mx-text-xs);
45
+ font-weight: 600;
46
+ font-variant-numeric: tabular-nums;
47
+ }
48
+ .mx-kanban-column[data-full] .mx-kanban-count {
49
+ background: color-mix(in oklab, var(--mx-warning) 14%, var(--mx-surface));
50
+ color: var(--mx-warning);
51
+ }
52
+ .mx-kanban-list {
53
+ display: flex;
54
+ flex-direction: column;
55
+ gap: 8px;
56
+ min-height: 56px;
57
+ margin: 0;
58
+ padding: 0;
59
+ overflow-y: auto;
60
+ list-style: none;
61
+ }
62
+ .mx-kanban-card {
63
+ box-sizing: border-box;
64
+ padding: 10px 12px;
65
+ border: var(--mx-border);
66
+ border-radius: var(--mx-radius);
67
+ background: var(--mx-surface);
68
+ color: var(--mx-ink);
69
+ font-size: var(--mx-text-sm);
70
+ line-height: 1.4;
71
+ box-shadow: 0 1px 2px rgb(0 0 0 / 0.05);
72
+ cursor: grab;
73
+ transition: box-shadow var(--mx-duration) var(--mx-ease), transform var(--mx-duration) var(--mx-ease);
74
+ }
75
+ .mx-kanban-card:hover {
76
+ border-color: var(--mx-stone-strong);
77
+ }
78
+ .mx-kanban-card:focus-visible {
79
+ outline: var(--mx-focus-width) solid var(--mx-accent);
80
+ outline-offset: var(--mx-focus-offset);
81
+ }
82
+ .mx-kanban-card[data-grabbed] {
83
+ border-color: var(--mx-accent);
84
+ box-shadow: var(--mx-shadow-overlay);
85
+ transform: rotate(-1.5deg);
86
+ }
87
+ .mx-kanban-card.cdk-drag-preview {
88
+ box-shadow: var(--mx-shadow-overlay);
89
+ cursor: grabbing;
90
+ }
91
+ .mx-kanban-card.cdk-drag-animating,
92
+ .mx-kanban-list.cdk-drop-list-dragging .mx-kanban-card:not(.cdk-drag-placeholder) {
93
+ transition: transform 200ms var(--mx-ease);
94
+ }
95
+ .mx-kanban-placeholder {
96
+ min-height: 44px;
97
+ border: 1.5px dashed var(--mx-accent);
98
+ border-radius: var(--mx-radius);
99
+ background: var(--mx-accent-soft);
100
+ }
101
+ .mx-kanban-empty {
102
+ padding: 16px 8px;
103
+ border: 1.5px dashed var(--mx-stone-strong);
104
+ border-radius: var(--mx-radius);
105
+ color: var(--mx-muted);
106
+ font-size: var(--mx-text-sm);
107
+ text-align: center;
108
+ }
109
+ .mx-kanban-list.cdk-drop-list-dragging .mx-kanban-empty,
110
+ .mx-kanban-list:has(.cdk-drag-placeholder) .mx-kanban-empty {
111
+ display: none;
112
+ }
113
+ .mx-kanban-column-foot {
114
+ padding-top: 2px;
115
+ }
@@ -0,0 +1,155 @@
1
+ /* ---------- Tour (visita guiada) ---------- */
2
+
3
+ .mx-tour-pane {
4
+ pointer-events: auto;
5
+ }
6
+ .mx-tour-layer {
7
+ position: fixed;
8
+ inset: 0;
9
+ display: block;
10
+ font-family: var(--mx-font-sans);
11
+ }
12
+ .mx-tour-dim {
13
+ position: fixed;
14
+ inset: 0;
15
+ background: var(--mx-backdrop);
16
+ animation: mx-fade-in var(--mx-duration) var(--mx-ease);
17
+ }
18
+ /* o recorte: uma sombra enorme escurece tudo à volta do elemento */
19
+ .mx-tour-spotlight {
20
+ position: fixed;
21
+ border-radius: var(--mx-radius);
22
+ box-shadow:
23
+ 0 0 0 2px var(--mx-accent),
24
+ 0 0 0 200vmax var(--mx-backdrop);
25
+ pointer-events: none;
26
+ transition:
27
+ top 200ms var(--mx-ease),
28
+ left 200ms var(--mx-ease),
29
+ width 200ms var(--mx-ease),
30
+ height 200ms var(--mx-ease);
31
+ }
32
+ .mx-tour-card {
33
+ position: fixed;
34
+ box-sizing: border-box;
35
+ width: min(340px, calc(100vw - 24px));
36
+ padding: 16px 16px 14px;
37
+ border: var(--mx-border);
38
+ border-radius: var(--mx-radius-surface);
39
+ background: var(--mx-surface);
40
+ box-shadow: var(--mx-shadow-overlay);
41
+ color: var(--mx-ink);
42
+ font-size: var(--mx-text-md);
43
+ line-height: 1.5;
44
+ outline: none;
45
+ animation: mx-fade-in var(--mx-duration) var(--mx-ease);
46
+ }
47
+ .mx-tour-card:focus,
48
+ .mx-tour-card:focus-visible {
49
+ outline: none;
50
+ }
51
+ .mx-tour-card[data-placement='center'] {
52
+ top: 50%;
53
+ left: 50%;
54
+ width: min(400px, calc(100vw - 24px));
55
+ transform: translate(-50%, -50%);
56
+ }
57
+ .mx-tour-head {
58
+ display: flex;
59
+ align-items: center;
60
+ justify-content: space-between;
61
+ gap: 8px;
62
+ min-height: 24px;
63
+ }
64
+ .mx-tour-progress {
65
+ margin: 0;
66
+ color: var(--mx-muted);
67
+ font-size: var(--mx-text-xs);
68
+ font-variant-numeric: tabular-nums;
69
+ }
70
+ .mx-tour-close {
71
+ display: inline-grid;
72
+ place-items: center;
73
+ width: 28px;
74
+ height: 28px;
75
+ margin: -4px -6px -4px 0;
76
+ padding: 0;
77
+ border: 0;
78
+ border-radius: var(--mx-radius);
79
+ background: transparent;
80
+ color: var(--mx-muted);
81
+ cursor: pointer;
82
+ }
83
+ .mx-tour-close:hover {
84
+ background: var(--mx-subtle);
85
+ color: var(--mx-ink);
86
+ }
87
+ .mx-tour-close svg {
88
+ width: 16px;
89
+ height: 16px;
90
+ }
91
+ .mx-tour-title {
92
+ margin: 6px 0 4px;
93
+ font-size: var(--mx-text-lg);
94
+ font-weight: 600;
95
+ line-height: 1.35;
96
+ }
97
+ .mx-tour-body {
98
+ color: var(--mx-muted);
99
+ }
100
+ .mx-tour-body > :first-child {
101
+ margin-top: 0;
102
+ }
103
+ .mx-tour-body > :last-child {
104
+ margin-bottom: 0;
105
+ }
106
+ .mx-tour-foot {
107
+ display: flex;
108
+ flex-wrap: wrap;
109
+ align-items: center;
110
+ justify-content: space-between;
111
+ gap: 10px;
112
+ margin-top: 14px;
113
+ }
114
+ .mx-tour-dots {
115
+ display: inline-flex;
116
+ gap: 5px;
117
+ }
118
+ .mx-tour-dot {
119
+ width: 6px;
120
+ height: 6px;
121
+ border-radius: 50%;
122
+ background: var(--mx-stone);
123
+ }
124
+ .mx-tour-dot[data-active] {
125
+ width: 16px;
126
+ border-radius: 3px;
127
+ background: var(--mx-accent);
128
+ }
129
+ .mx-tour-actions {
130
+ display: inline-flex;
131
+ gap: 6px;
132
+ margin-left: auto;
133
+ }
134
+
135
+ @media (prefers-reduced-motion: reduce) {
136
+ .mx-tour-spotlight {
137
+ transition: none;
138
+ }
139
+ .mx-tour-card,
140
+ .mx-tour-dim {
141
+ animation: none;
142
+ }
143
+ }
144
+ /* sem sombras em alto contraste: o destaque passa a ser um contorno */
145
+ @media (forced-colors: active) {
146
+ .mx-tour-spotlight {
147
+ outline: 3px solid Highlight;
148
+ }
149
+ .mx-tour-card {
150
+ border: 1px solid CanvasText;
151
+ }
152
+ .mx-tour-dot[data-active] {
153
+ background: Highlight;
154
+ }
155
+ }
package/styles/muxima.css CHANGED
@@ -25,6 +25,8 @@
25
25
  @import './components/tabs.css';
26
26
  @import './components/sidebar.css';
27
27
  @import './components/table.css';
28
+ @import './components/data-grid.css';
29
+ @import './components/kanban.css';
28
30
  @import './components/badge.css';
29
31
  @import './components/chips.css';
30
32
  @import './components/card.css';
@@ -50,8 +52,11 @@
50
52
  @import './components/date-picker.css';
51
53
  @import './components/time-picker.css';
52
54
  @import './components/color-picker.css';
55
+ @import './components/editor.css';
56
+ @import './components/tour.css';
53
57
  @import './components/otp.css';
54
58
  @import './components/file-upload.css';
59
+ @import './components/image-cropper.css';
55
60
  @import './components/stepper.css';
56
61
  @import './components/command.css';
57
62
  @import './motion.css';
@@ -0,0 +1,175 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { TemplateRef } from '@angular/core';
3
+
4
+ /** Coluna da grelha. */
5
+ interface MxGridColumn<T = any> {
6
+ key: string;
7
+ header: string;
8
+ /** Valor para ordenar e filtrar; por omissão row[key]. */
9
+ value?: (row: T) => unknown;
10
+ /** Texto mostrado; por omissão String(valor). */
11
+ format?: (value: any, row: T) => string;
12
+ /** Largura inicial em px. */
13
+ width?: number;
14
+ minWidth?: number;
15
+ sortable?: boolean;
16
+ /** Filtro no cabeçalho. `number` aceita 100, >100, <100 e 10-20. */
17
+ filter?: 'text' | 'number' | 'select' | false;
18
+ /** Opções do filtro `select`; por omissão, os valores que existem nos dados. */
19
+ filterOptions?: string[];
20
+ align?: 'start' | 'center' | 'end';
21
+ /** Fixa a coluna à esquerda (`start`) ou à direita (`end`) durante o scroll horizontal. */
22
+ pinned?: 'start' | 'end';
23
+ resizable?: boolean;
24
+ hidden?: boolean;
25
+ /** false: não aparece no menu de colunas. */
26
+ hideable?: boolean;
27
+ }
28
+ interface MxGridSort {
29
+ key: string | null;
30
+ direction: 'asc' | 'desc';
31
+ }
32
+ /**
33
+ * Modelo de uma célula: `<ng-template mxGridCell="estado" let-row let-value="value">`.
34
+ */
35
+ declare class MxGridCell {
36
+ readonly key: _angular_core.InputSignal<string>;
37
+ readonly template: TemplateRef<any>;
38
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<MxGridCell, never>;
39
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MxGridCell, "ng-template[mxGridCell]", never, { "key": { "alias": "mxGridCell"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
40
+ }
41
+ /** Conteúdo da barra por cima da grelha (pesquisa, botões). */
42
+ declare class MxGridToolbar {
43
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<MxGridToolbar, never>;
44
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MxGridToolbar, "[mxGridToolbar]", never, {}, {}, never, never, true, never>;
45
+ }
46
+ /**
47
+ * Grelha de dados (padrão WAI-ARIA Grid): ordenação, filtros por coluna, pesquisa,
48
+ * selecção, colunas redimensionáveis, fixas e escondíveis, e scroll virtual para
49
+ * milhares de linhas. Navega-se célula a célula com as setas.
50
+ *
51
+ * ```html
52
+ * <mx-data-grid [data]="facturas" [columns]="colunas" rowKey="numero"
53
+ * selection="multiple" [(selected)]="escolhidas" height="480px" aria-label="Facturas">
54
+ * <ng-template mxGridCell="estado" let-row>
55
+ * <span mxBadge [variant]="cor(row.estado)">{{ row.estado }}</span>
56
+ * </ng-template>
57
+ * </mx-data-grid>
58
+ * ```
59
+ *
60
+ * Com `manual`, a grelha não ordena nem filtra: emite [(sort)] e [(filters)] para o
61
+ * servidor e mostra `data` tal como chega (indica o total em `totalRows`).
62
+ */
63
+ declare class MxDataGrid<T = any> {
64
+ readonly data: _angular_core.InputSignal<readonly T[]>;
65
+ readonly columns: _angular_core.InputSignal<readonly MxGridColumn<T>[]>;
66
+ /** Chave única de cada linha: nome do campo ou função. */
67
+ readonly rowKey: _angular_core.InputSignal<string | ((row: T) => unknown)>;
68
+ readonly selection: _angular_core.InputSignal<"none" | "single" | "multiple">;
69
+ /** Chaves das linhas seleccionadas. */
70
+ readonly selected: _angular_core.ModelSignal<readonly unknown[]>;
71
+ readonly sort: _angular_core.ModelSignal<MxGridSort>;
72
+ /** Texto de cada filtro, por chave de coluna. */
73
+ readonly filters: _angular_core.ModelSignal<Record<string, string>>;
74
+ /** Pesquisa em todas as colunas visíveis. */
75
+ readonly query: _angular_core.InputSignal<string>;
76
+ /** Não ordena nem filtra: só emite sort e filters (dados do servidor). */
77
+ readonly manual: _angular_core.InputSignalWithTransform<boolean, unknown>;
78
+ /** Total de linhas no servidor (modo manual), para aria-rowcount e o estado. */
79
+ readonly totalRows: _angular_core.InputSignal<number | null>;
80
+ readonly height: _angular_core.InputSignal<string | null>;
81
+ readonly maxHeight: _angular_core.InputSignal<string | null>;
82
+ readonly rowHeight: _angular_core.InputSignalWithTransform<number, unknown>;
83
+ /** Scroll virtual: `auto` liga a partir de 100 linhas (precisa de height). */
84
+ readonly virtual: _angular_core.InputSignal<boolean | "auto">;
85
+ readonly density: _angular_core.InputSignal<"comfortable" | "compact">;
86
+ readonly loading: _angular_core.InputSignalWithTransform<boolean, unknown>;
87
+ readonly columnMenu: _angular_core.InputSignalWithTransform<boolean, unknown>;
88
+ readonly ariaLabel: _angular_core.InputSignal<string>;
89
+ /** Coluna cujo texto identifica a linha nos nomes ("Seleccionar FT 2026/0201"). */
90
+ readonly labelKey: _angular_core.InputSignal<string | null>;
91
+ readonly emptyText: _angular_core.InputSignal<string>;
92
+ readonly loadingText: _angular_core.InputSignal<string>;
93
+ readonly columnsText: _angular_core.InputSignal<string>;
94
+ readonly filterText: _angular_core.InputSignal<string>;
95
+ readonly allText: _angular_core.InputSignal<string>;
96
+ readonly selectAllText: _angular_core.InputSignal<string>;
97
+ readonly selectRowText: _angular_core.InputSignal<string>;
98
+ readonly selectText: _angular_core.InputSignal<string>;
99
+ readonly resizeHint: _angular_core.InputSignal<string>;
100
+ /** Enter ou duplo clique numa linha. */
101
+ readonly rowActivate: _angular_core.OutputEmitterRef<T>;
102
+ private readonly cells;
103
+ private readonly toolbarSlot;
104
+ private readonly scroller;
105
+ private readonly host;
106
+ private readonly injector;
107
+ private readonly announcer;
108
+ protected readonly checkWidth = 44;
109
+ protected readonly resizeHintId: string;
110
+ protected readonly radioName: string;
111
+ private readonly widths;
112
+ private readonly hiddenKeys;
113
+ private readonly scrollTop;
114
+ private readonly viewport;
115
+ /** Célula activa: linha e coluna na grelha (0 = cabeçalho). */
116
+ private readonly active;
117
+ protected readonly showToolbar: _angular_core.Signal<boolean>;
118
+ protected readonly offset: _angular_core.Signal<1 | 0>;
119
+ protected readonly hasFilters: _angular_core.Signal<boolean>;
120
+ protected readonly headerRows: _angular_core.Signal<number>;
121
+ protected readonly hideableColumns: _angular_core.Signal<MxGridColumn<T>[]>;
122
+ protected readonly visibleColumns: _angular_core.Signal<MxGridColumn<T>[]>;
123
+ protected readonly colCount: _angular_core.Signal<number>;
124
+ protected readonly tableWidth: _angular_core.Signal<number>;
125
+ /** Linhas depois de filtros, pesquisa e ordenação. */
126
+ readonly rows: _angular_core.Signal<readonly T[]>;
127
+ protected readonly isVirtual: _angular_core.Signal<boolean>;
128
+ protected readonly range: _angular_core.Signal<{
129
+ start: number;
130
+ end: number;
131
+ }>;
132
+ protected readonly rendered: _angular_core.Signal<T[]>;
133
+ protected readonly rowCount: _angular_core.Signal<number>;
134
+ private readonly selectedSet;
135
+ protected readonly allSelected: _angular_core.Signal<boolean>;
136
+ protected readonly someSelected: _angular_core.Signal<boolean>;
137
+ protected readonly statusText: _angular_core.Signal<string>;
138
+ constructor();
139
+ keyOf(row: T): unknown;
140
+ valueOf(c: MxGridColumn<T>, row: T): unknown;
141
+ protected text(c: MxGridColumn<T>, row: T): string;
142
+ protected rowLabel(row: T): string;
143
+ protected cellTemplate(key: string): TemplateRef<unknown> | null;
144
+ protected widthOf(c: MxGridColumn<T>): number;
145
+ protected pinOffset(c: MxGridColumn<T>): number;
146
+ protected pinOffsetEnd(c: MxGridColumn<T>): number;
147
+ protected optionsOf(c: MxGridColumn<T>): string[];
148
+ protected isHidden(c: MxGridColumn<T>): boolean;
149
+ protected ariaSort(c: MxGridColumn<T>): string | null;
150
+ isSelected(row: T): boolean;
151
+ toggleSort(c: MxGridColumn<T>): void;
152
+ setFilter(key: string, value: string): void;
153
+ toggleColumn(key: string): void;
154
+ toggleRow(row: T): void;
155
+ toggleAll(): void;
156
+ setWidth(key: string, width: number): void;
157
+ protected resetWidth(c: MxGridColumn<T>): void;
158
+ protected startResize(event: PointerEvent, c: MxGridColumn<T>): void;
159
+ private measure;
160
+ protected onScroll(): void;
161
+ private scrollTo;
162
+ private widgetIn;
163
+ private parseCell;
164
+ protected onFocusIn(event: FocusEvent): void;
165
+ private lastRow;
166
+ /** Move a célula activa e põe-lhe o foco (fazendo scroll se for preciso). */
167
+ focusCell(row: number, col: number): void;
168
+ protected onKeydown(event: KeyboardEvent): void;
169
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<MxDataGrid<any>, never>;
170
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<MxDataGrid<any>, "mx-data-grid", ["mxDataGrid"], { "data": { "alias": "data"; "required": false; "isSignal": true; }; "columns": { "alias": "columns"; "required": false; "isSignal": true; }; "rowKey": { "alias": "rowKey"; "required": false; "isSignal": true; }; "selection": { "alias": "selection"; "required": false; "isSignal": true; }; "selected": { "alias": "selected"; "required": false; "isSignal": true; }; "sort": { "alias": "sort"; "required": false; "isSignal": true; }; "filters": { "alias": "filters"; "required": false; "isSignal": true; }; "query": { "alias": "query"; "required": false; "isSignal": true; }; "manual": { "alias": "manual"; "required": false; "isSignal": true; }; "totalRows": { "alias": "totalRows"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "maxHeight": { "alias": "maxHeight"; "required": false; "isSignal": true; }; "rowHeight": { "alias": "rowHeight"; "required": false; "isSignal": true; }; "virtual": { "alias": "virtual"; "required": false; "isSignal": true; }; "density": { "alias": "density"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "columnMenu": { "alias": "columnMenu"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "aria-label"; "required": false; "isSignal": true; }; "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; "loadingText": { "alias": "loadingText"; "required": false; "isSignal": true; }; "columnsText": { "alias": "columnsText"; "required": false; "isSignal": true; }; "filterText": { "alias": "filterText"; "required": false; "isSignal": true; }; "allText": { "alias": "allText"; "required": false; "isSignal": true; }; "selectAllText": { "alias": "selectAllText"; "required": false; "isSignal": true; }; "selectRowText": { "alias": "selectRowText"; "required": false; "isSignal": true; }; "selectText": { "alias": "selectText"; "required": false; "isSignal": true; }; "resizeHint": { "alias": "resizeHint"; "required": false; "isSignal": true; }; }, { "selected": "selectedChange"; "sort": "sortChange"; "filters": "filtersChange"; "rowActivate": "rowActivate"; }, ["cells", "toolbarSlot"], ["[mxGridToolbar]", "[mxGridFooter]"], true, never>;
171
+ }
172
+ declare const MX_DATA_GRID: readonly [typeof MxDataGrid, typeof MxGridCell, typeof MxGridToolbar];
173
+
174
+ export { MX_DATA_GRID, MxDataGrid, MxGridCell, MxGridToolbar };
175
+ export type { MxGridColumn, MxGridSort };
@@ -0,0 +1,104 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { ControlValueAccessor } from '@angular/forms';
3
+ import { SafeHtml } from '@angular/platform-browser';
4
+ import { Editor } from '@tiptap/core';
5
+ import { MxFormFieldControl, MxFormField } from '@muxima-ui/core/form-field';
6
+
7
+ /** Botões possíveis da barra de formatação. `|` desenha um separador. */
8
+ type MxEditorTool = 'bold' | 'italic' | 'underline' | 'strike' | 'code' | 'heading2' | 'heading3' | 'paragraph' | 'bulletList' | 'orderedList' | 'blockquote' | 'link' | 'undo' | 'redo' | '|';
9
+ declare const MX_EDITOR_DEFAULT_TOOLS: MxEditorTool[];
10
+ interface ToolDef {
11
+ label: string;
12
+ shortcut?: string;
13
+ icon: string;
14
+ run: (e: Editor) => void;
15
+ active?: (e: Editor) => boolean;
16
+ enabled?: (e: Editor) => boolean;
17
+ toggle?: boolean;
18
+ }
19
+ /**
20
+ * Editor de texto com formatação, sobre o Tiptap (ProseMirror). O Tiptap é uma dependência
21
+ * opcional: instala-o só se usares este ponto de entrada.
22
+ *
23
+ * ```bash
24
+ * npm install @tiptap/core @tiptap/pm @tiptap/starter-kit @tiptap/extensions
25
+ * ```
26
+ * ```html
27
+ * <mx-form-field>
28
+ * <label mxLabel>Notas da factura</label>
29
+ * <mx-editor formControlName="notas" placeholder="Escreve aqui..." [maxLength]="2000" />
30
+ * </mx-form-field>
31
+ * ```
32
+ *
33
+ * O valor é HTML (string; vazio quando não há texto). A barra é um role=toolbar:
34
+ * setas entre botões, Alt + F10 vai do texto para a barra e Esc volta ao texto.
35
+ */
36
+ declare class MxEditor implements ControlValueAccessor, MxFormFieldControl {
37
+ readonly value: _angular_core.ModelSignal<string>;
38
+ readonly placeholder: _angular_core.InputSignal<string>;
39
+ readonly tools: Record<"bold" | "italic" | "underline" | "strike" | "code" | "heading2" | "heading3" | "paragraph" | "bulletList" | "orderedList" | "blockquote" | "link" | "undo" | "redo", ToolDef>;
40
+ readonly toolbar: _angular_core.InputSignal<MxEditorTool[]>;
41
+ /** Máximo de caracteres; mostra a contagem. */
42
+ readonly maxLength: _angular_core.InputSignalWithTransform<number | null, unknown>;
43
+ readonly minHeight: _angular_core.InputSignal<string>;
44
+ readonly maxHeight: _angular_core.InputSignal<string | null>;
45
+ readonly readonly: _angular_core.InputSignalWithTransform<boolean, unknown>;
46
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
47
+ readonly required: _angular_core.InputSignalWithTransform<boolean, unknown>;
48
+ readonly invalid: _angular_core.InputSignalWithTransform<boolean, unknown>;
49
+ readonly id: _angular_core.InputSignal<string | undefined>;
50
+ readonly ariaLabel: _angular_core.InputSignal<string | undefined>;
51
+ readonly toolbarLabel: _angular_core.InputSignal<string>;
52
+ readonly linkLabel: _angular_core.InputSignal<string>;
53
+ protected readonly field: MxFormField | null;
54
+ private readonly ngControl;
55
+ private readonly content;
56
+ private readonly toolbarEl;
57
+ private readonly linkInput;
58
+ private readonly injector;
59
+ private readonly generatedId;
60
+ readonly controlId: _angular_core.Signal<string>;
61
+ private readonly formDisabled;
62
+ protected readonly isDisabled: _angular_core.Signal<boolean>;
63
+ protected readonly toolList: _angular_core.Signal<MxEditorTool[]>;
64
+ protected readonly focusIndex: _angular_core.WritableSignal<number>;
65
+ /** Muda a cada transacção, para os botões reflectirem o estado. */
66
+ private readonly tick;
67
+ protected readonly count: _angular_core.WritableSignal<number>;
68
+ protected readonly linkOpen: _angular_core.WritableSignal<boolean>;
69
+ protected readonly linkUrl: _angular_core.WritableSignal<string>;
70
+ protected readonly linkActive: _angular_core.WritableSignal<boolean>;
71
+ /** O editor do Tiptap, para comandos próprios (editor()?.chain()...). */
72
+ readonly editor: _angular_core.WritableSignal<Editor | null>;
73
+ private writing;
74
+ private onChange;
75
+ private onTouched;
76
+ constructor();
77
+ private create;
78
+ private readonly sanitizer;
79
+ private readonly icons;
80
+ protected icon(t: Exclude<MxEditorTool, '|'>): SafeHtml;
81
+ protected isActive(t: Exclude<MxEditorTool, '|'>): boolean;
82
+ protected isEnabled(t: Exclude<MxEditorTool, '|'>): boolean;
83
+ protected runTool(t: Exclude<MxEditorTool, '|'>, index: number): void;
84
+ openLink(): void;
85
+ protected applyLink(): void;
86
+ protected removeLink(): void;
87
+ protected closeLink(): void;
88
+ private buttons;
89
+ focusToolbar(): void;
90
+ protected onToolbarKey(event: KeyboardEvent): void;
91
+ /** Põe o foco no texto. */
92
+ focus(): void;
93
+ errorState(): boolean;
94
+ isRequired(): boolean;
95
+ writeValue(value: string | null): void;
96
+ registerOnChange(fn: (v: string) => void): void;
97
+ registerOnTouched(fn: () => void): void;
98
+ setDisabledState(isDisabled: boolean): void;
99
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<MxEditor, never>;
100
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<MxEditor, "mx-editor", ["mxEditor"], { "value": { "alias": "value"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "toolbar": { "alias": "toolbar"; "required": false; "isSignal": true; }; "maxLength": { "alias": "maxLength"; "required": false; "isSignal": true; }; "minHeight": { "alias": "minHeight"; "required": false; "isSignal": true; }; "maxHeight": { "alias": "maxHeight"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "aria-label"; "required": false; "isSignal": true; }; "toolbarLabel": { "alias": "toolbarLabel"; "required": false; "isSignal": true; }; "linkLabel": { "alias": "linkLabel"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; }, never, never, true, never>;
101
+ }
102
+
103
+ export { MX_EDITOR_DEFAULT_TOOLS, MxEditor };
104
+ export type { MxEditorTool };