@mk-kit/ui 0.35.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -9
- package/THIRD_PARTY_NOTICES.md +28 -0
- package/fesm2022/mk-kit-ui-chat.mjs +490 -0
- package/fesm2022/mk-kit-ui-chat.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-core.mjs +446 -1
- package/fesm2022/mk-kit-ui-core.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-feedback.mjs +400 -8
- package/fesm2022/mk-kit-ui-feedback.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-forms.mjs +816 -6
- package/fesm2022/mk-kit-ui-forms.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-icon.mjs +444 -117
- package/fesm2022/mk-kit-ui-icon.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-layout.mjs +401 -0
- package/fesm2022/mk-kit-ui-layout.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-navigation.mjs +2 -2
- package/fesm2022/mk-kit-ui-navigation.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-query-builder.mjs +276 -0
- package/fesm2022/mk-kit-ui-query-builder.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-table.mjs +291 -20
- package/fesm2022/mk-kit-ui-table.mjs.map +1 -1
- package/fesm2022/mk-kit-ui.mjs +3 -0
- package/fesm2022/mk-kit-ui.mjs.map +1 -1
- package/package.json +13 -1
- package/styles/mk-kit.css +16 -0
- package/types/mk-kit-ui-chat.d.ts +288 -0
- package/types/mk-kit-ui-core.d.ts +228 -3
- package/types/mk-kit-ui-feedback.d.ts +111 -2
- package/types/mk-kit-ui-forms.d.ts +264 -3
- package/types/mk-kit-ui-icon.d.ts +6 -2
- package/types/mk-kit-ui-layout.d.ts +236 -0
- package/types/mk-kit-ui-query-builder.d.ts +98 -0
- package/types/mk-kit-ui-table.d.ts +145 -5
- package/types/mk-kit-ui.d.ts +3 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mk-kit-ui-chat.mjs","sources":["../../../projects/mk-kit/chat/prompt-box/prompt-box.ts","../../../projects/mk-kit/chat/prompt-box/prompt-box.html","../../../projects/mk-kit/chat/chat-message/chat-message.ts","../../../projects/mk-kit/chat/chat-message/chat-message.html","../../../projects/mk-kit/chat/chat/chat.ts","../../../projects/mk-kit/chat/chat/chat.html","../../../projects/mk-kit/chat/index.ts","../../../projects/mk-kit/chat/mk-kit-ui-chat.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n afterRenderEffect,\n booleanAttribute,\n computed,\n inject,\n input,\n model,\n numberAttribute,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { MK_I18N, mkUniqueId } from '@mk-kit/ui/core';\nimport { MkButton } from '@mk-kit/ui/button';\nimport { MkChip } from '@mk-kit/ui/chip';\nimport { MkIcon } from '@mk-kit/ui/icon';\nimport type { MkChatSend } from '../chat.types';\n\n/**\n * PromptBox — the composer of a chat: an auto-growing textarea, attachments\n * (button, drop, paste), quick-reply suggestions and a send button that turns\n * into *stop* while a reply is being generated.\n *\n * Enter sends, Shift+Enter breaks the line (`sendOnEnter=\"false\"` to swap).\n *\n * ```html\n * <mk-prompt-box\n * [(value)]=\"draft\"\n * [busy]=\"generating()\"\n * attachments\n * [suggestions]=\"['Summarise this page', 'Draft a reply']\"\n * (send)=\"ask($event)\"\n * (stop)=\"abort()\"\n * />\n * ```\n */\n@Component({\n selector: 'mk-prompt-box',\n templateUrl: './prompt-box.html',\n styleUrl: './prompt-box.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [MkButton, MkChip, MkIcon],\n host: {\n class: 'mk-prompt-box',\n '[class.mk-prompt-box--disabled]': 'disabled()',\n '[class.mk-prompt-box--dragging]': 'dragging()',\n '(dragover)': 'onDragOver($event)',\n '(dragleave)': 'dragging.set(false)',\n '(drop)': 'onDrop($event)',\n },\n})\nexport class MkPromptBox {\n protected readonly i18n = inject(MK_I18N);\n protected readonly id = mkUniqueId('mk-prompt');\n\n /** The draft text (two-way). */\n readonly value = model('');\n /** Placeholder of the textarea. */\n readonly placeholder = input(this.i18n.chatPlaceholder);\n /** Disable everything (e.g. while offline). */\n readonly disabled = input(false, { transform: booleanAttribute });\n /** A reply is being generated: the send button becomes *stop*, sending is paused. */\n readonly busy = input(false, { transform: booleanAttribute });\n /** Allow attaching files (button, drag & drop, paste). */\n readonly attachments = input(false, { transform: booleanAttribute });\n /** Accepted file types for the picker (`accept` attribute syntax). */\n readonly accept = input('');\n /** Maximum number of attached files (0 = unlimited). */\n readonly maxFiles = input(0, { transform: numberAttribute });\n /** Maximum draft length; shows a counter when set. */\n readonly maxLength = input(0, { transform: numberAttribute });\n /** Minimum visible rows. */\n readonly rows = input(1, { transform: numberAttribute });\n /** Rows the textarea grows to before scrolling. */\n readonly maxRows = input(8, { transform: numberAttribute });\n /** Enter sends and Shift+Enter breaks the line (default); `false` swaps them. */\n readonly sendOnEnter = input(true, { transform: booleanAttribute });\n /** Quick replies shown above the box; clicking one sends it. */\n readonly suggestions = input<readonly string[]>([]);\n /** Focus the textarea on render. */\n readonly autofocus = input(false, { transform: booleanAttribute });\n\n /** The user submitted the draft (and any files). */\n readonly send = output<MkChatSend>();\n /** The user pressed *stop* while `busy`. */\n readonly stop = output<void>();\n\n /** Files waiting to be sent. */\n readonly files = signal<File[]>([]);\n protected readonly dragging = signal(false);\n\n private readonly textarea = viewChild.required<ElementRef<HTMLTextAreaElement>>('textarea');\n private readonly fileInput = viewChild<ElementRef<HTMLInputElement>>('fileInput');\n\n protected readonly canSend = computed(\n () => !this.disabled() && !this.busy() && (this.value().trim().length > 0 || this.files().length > 0),\n );\n protected readonly remaining = computed(() => (this.maxLength() > 0 ? this.maxLength() - this.value().length : null));\n\n constructor() {\n afterRenderEffect(() => {\n this.value();\n this.rows();\n this.maxRows();\n this.autosize();\n });\n afterRenderEffect(() => {\n if (this.autofocus()) this.textarea().nativeElement.focus();\n });\n }\n\n /** Focus the textarea. */\n focus(): void {\n this.textarea().nativeElement.focus();\n }\n\n /** Empty the draft and the attachment list. */\n clear(): void {\n this.value.set('');\n this.files.set([]);\n }\n\n /** Submit the current draft (no-op when there is nothing to send or while busy). */\n submit(): void {\n if (!this.canSend()) return;\n this.send.emit({ text: this.value().trim(), files: this.files() });\n this.clear();\n }\n\n /** Add files, honouring `maxFiles` and `accept`. */\n addFiles(list: ArrayLike<File> | null | undefined): void {\n if (!list || !this.attachments() || this.disabled()) return;\n const accepted = Array.from(list).filter((f) => this.accepts(f));\n if (!accepted.length) return;\n this.files.update((current) => {\n const next = [...current, ...accepted];\n const max = this.maxFiles();\n return max > 0 ? next.slice(0, max) : next;\n });\n }\n\n removeFile(index: number): void {\n this.files.update((current) => current.filter((_, i) => i !== index));\n this.focus();\n }\n\n protected onInput(event: Event): void {\n this.value.set((event.target as HTMLTextAreaElement).value);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Enter' || event.isComposing) return;\n const plain = !event.shiftKey && !event.ctrlKey && !event.metaKey && !event.altKey;\n const sends = this.sendOnEnter() ? plain : event.ctrlKey || event.metaKey;\n if (!sends) return;\n event.preventDefault();\n if (this.busy()) return;\n this.submit();\n }\n\n protected onPaste(event: ClipboardEvent): void {\n const files = event.clipboardData?.files;\n if (files?.length && this.attachments()) {\n event.preventDefault();\n this.addFiles(files);\n }\n }\n\n protected onDragOver(event: DragEvent): void {\n if (!this.attachments() || this.disabled()) return;\n event.preventDefault();\n this.dragging.set(true);\n }\n\n protected onDrop(event: DragEvent): void {\n this.dragging.set(false);\n if (!this.attachments() || this.disabled()) return;\n event.preventDefault();\n this.addFiles(event.dataTransfer?.files);\n }\n\n protected openPicker(): void {\n this.fileInput()?.nativeElement.click();\n }\n\n protected onFilesPicked(event: Event): void {\n const el = event.target as HTMLInputElement;\n this.addFiles(el.files);\n el.value = '';\n }\n\n protected onSuggestion(text: string): void {\n if (this.disabled() || this.busy()) return;\n this.send.emit({ text, files: [] });\n }\n\n protected onPrimary(): void {\n if (this.busy()) this.stop.emit();\n else this.submit();\n }\n\n /** Human-readable size, e.g. `1.2 MB`. */\n protected formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} kB`;\n return `${(bytes / 1024 / 1024).toFixed(1)} MB`;\n }\n\n private accepts(file: File): boolean {\n const accept = this.accept().trim();\n if (!accept) return true;\n const ext = file.name.includes('.') ? '.' + file.name.split('.').pop()!.toLowerCase() : '';\n return accept.split(',').some((rule) => {\n const r = rule.trim().toLowerCase();\n if (!r) return false;\n if (r.startsWith('.')) return ext === r;\n if (r.endsWith('/*')) return file.type.toLowerCase().startsWith(r.slice(0, -1));\n return file.type.toLowerCase() === r;\n });\n }\n\n private autosize(): void {\n const el = this.textarea().nativeElement;\n const view = el.ownerDocument.defaultView;\n if (!view) return;\n const style = view.getComputedStyle(el);\n const line = parseFloat(style.lineHeight) || 20;\n const pad = (parseFloat(style.paddingTop) || 0) + (parseFloat(style.paddingBottom) || 0);\n const min = this.rows() * line + pad;\n const max = this.maxRows() * line + pad;\n el.style.height = 'auto';\n const next = Math.min(Math.max(el.scrollHeight, min), max);\n el.style.height = `${next}px`;\n el.style.overflowY = el.scrollHeight > max ? 'auto' : 'hidden';\n }\n}\n","@if (suggestions().length) {\n <div class=\"mk-prompt-box__suggestions\" role=\"group\" [attr.aria-label]=\"i18n.chatSuggestions\">\n @for (s of suggestions(); track s) {\n <button type=\"button\" class=\"mk-prompt-box__suggestion\" [disabled]=\"disabled() || busy()\" (click)=\"onSuggestion(s)\">\n {{ s }}\n </button>\n }\n </div>\n}\n\n<div class=\"mk-prompt-box__frame\">\n @if (files().length) {\n <ul class=\"mk-prompt-box__files\" [attr.aria-label]=\"i18n.chatAttachments\">\n @for (file of files(); track $index) {\n <li>\n <mk-chip size=\"sm\" removable [removeLabel]=\"i18n.chatRemoveAttachment\" (removed)=\"removeFile($index)\">\n <mk-icon [name]=\"file.type.startsWith('image/') ? 'image' : 'file'\" size=\"sm\" />\n {{ file.name }}\n <span class=\"mk-prompt-box__size\">{{ formatSize(file.size) }}</span>\n </mk-chip>\n </li>\n }\n </ul>\n }\n\n <textarea\n #textarea\n class=\"mk-prompt-box__input\"\n [id]=\"id\"\n [value]=\"value()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [attr.maxlength]=\"maxLength() > 0 ? maxLength() : null\"\n [attr.aria-label]=\"i18n.chatComposerLabel\"\n rows=\"1\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (paste)=\"onPaste($event)\"\n ></textarea>\n\n <div class=\"mk-prompt-box__actions\">\n @if (attachments()) {\n <input #fileInput type=\"file\" class=\"mk-prompt-box__file-input\" multiple [accept]=\"accept()\" tabindex=\"-1\" aria-hidden=\"true\" (change)=\"onFilesPicked($event)\" />\n <button\n mkButton\n type=\"button\"\n variant=\"ghost\"\n tone=\"neutral\"\n size=\"sm\"\n class=\"mk-prompt-box__attach\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"i18n.chatAttach\"\n (click)=\"openPicker()\"\n >\n <mk-icon name=\"paperclip\" />\n </button>\n }\n @if (remaining() !== null) {\n <span class=\"mk-prompt-box__counter\" [class.mk-prompt-box__counter--over]=\"remaining()! < 0\" aria-live=\"polite\">{{ remaining() }}</span>\n }\n <button\n mkButton\n type=\"button\"\n size=\"sm\"\n class=\"mk-prompt-box__send\"\n [tone]=\"busy() ? 'neutral' : 'primary'\"\n [disabled]=\"disabled() || (!busy() && !canSend())\"\n [attr.aria-label]=\"busy() ? i18n.chatStop : i18n.chatSend\"\n (click)=\"onPrimary()\"\n >\n <mk-icon [name]=\"busy() ? 'square' : 'send'\" />\n </button>\n </div>\n</div>\n","import {\n ChangeDetectionStrategy,\n Component,\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n} from '@angular/core';\nimport { MK_I18N } from '@mk-kit/ui/core';\nimport { MkButton } from '@mk-kit/ui/button';\nimport { MkIcon } from '@mk-kit/ui/icon';\nimport { MkAvatar, MkMarkdown } from '@mk-kit/ui/data';\nimport { MkCopyToClipboard } from '@mk-kit/ui/directives';\nimport type { MkChatAttachment, MkChatMessage, MkChatToolCall } from '../chat.types';\n\n/**\n * ChatMessage — one bubble: avatar, author and time, attachment previews,\n * tool-call cards, the text (Markdown for assistants) with a streaming\n * cursor, and copy / retry actions. `mk-chat` renders these for you; use it\n * directly to build your own list.\n */\n@Component({\n selector: 'mk-chat-message',\n templateUrl: './chat-message.html',\n styleUrl: './chat-message.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [MkAvatar, MkButton, MkCopyToClipboard, MkIcon, MkMarkdown],\n host: {\n class: 'mk-chat-message',\n role: 'article',\n '[class.mk-chat-message--own]': 'own()',\n '[class.mk-chat-message--system]': 'message().role === \"system\"',\n '[class.mk-chat-message--streaming]': 'message().streaming',\n '[class.mk-chat-message--error]': '!!message().error',\n '[attr.aria-label]': 'ariaLabel()',\n },\n})\nexport class MkChatMessageComponent {\n protected readonly i18n = inject(MK_I18N);\n\n /** The message to render. */\n readonly message = input.required<MkChatMessage>();\n /** Render on the \"own\" (end) side — `mk-chat` sets this from `ownRole`. */\n readonly own = input(false, { transform: booleanAttribute });\n /** Render `text` as Markdown (assistants only; user text is always plain). */\n readonly markdown = input(true, { transform: booleanAttribute });\n readonly showAvatar = input(true, { transform: booleanAttribute });\n readonly showTimestamp = input(true, { transform: booleanAttribute });\n /** Offer a copy button on hover / focus. */\n readonly copyable = input(true, { transform: booleanAttribute });\n\n /** The retry button of a failed message was pressed. */\n readonly retry = output<MkChatMessage>();\n\n protected readonly authorName = computed(() => {\n const m = this.message();\n if (m.author?.name) return m.author.name;\n return m.role === 'user' ? this.i18n.chatYou : m.role === 'assistant' ? this.i18n.chatAssistant : '';\n });\n\n protected readonly time = computed(() => {\n const ts = this.message().timestamp;\n if (ts == null) return '';\n const d = ts instanceof Date ? ts : new Date(ts);\n if (Number.isNaN(d.getTime())) return '';\n return new Intl.DateTimeFormat(undefined, { hour: '2-digit', minute: '2-digit' }).format(d);\n });\n\n protected readonly ariaLabel = computed(() => {\n const parts = [this.authorName(), this.time()].filter(Boolean);\n return parts.join(', ') || null;\n });\n\n protected readonly useMarkdown = computed(() => this.markdown() && this.message().role === 'assistant');\n\n protected isImage(a: MkChatAttachment): boolean {\n return !!a.url && !!a.type?.startsWith('image/');\n }\n\n protected formatSize(bytes: number | undefined): string {\n if (bytes == null) return '';\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} kB`;\n return `${(bytes / 1024 / 1024).toFixed(1)} MB`;\n }\n\n protected toolIcon(tool: MkChatToolCall): string {\n return tool.status === 'done' ? 'check' : tool.status === 'error' ? 'circle-alert' : 'loader';\n }\n\n protected toolStatus(tool: MkChatToolCall): string {\n return tool.status === 'done'\n ? this.i18n.chatToolDone\n : tool.status === 'error'\n ? this.i18n.chatToolError\n : this.i18n.chatToolRunning;\n }\n}\n","@let m = message();\n@if (m.role === 'system') {\n <div class=\"mk-chat-message__system\">{{ m.text }}</div>\n} @else {\n @if (showAvatar()) {\n <mk-avatar class=\"mk-chat-message__avatar\" [name]=\"authorName()\" [src]=\"m.author?.avatar\" size=\"sm\" />\n }\n <div class=\"mk-chat-message__body\">\n <div class=\"mk-chat-message__meta\">\n <span class=\"mk-chat-message__author\">{{ authorName() }}</span>\n @if (showTimestamp() && time()) {\n <span class=\"mk-chat-message__time\">{{ time() }}</span>\n }\n </div>\n\n <div class=\"mk-chat-message__bubble\">\n @if (m.tools?.length) {\n <div class=\"mk-chat-message__tools\">\n @for (tool of m.tools; track tool.id ?? $index) {\n <details class=\"mk-chat-message__tool\" [class.mk-chat-message__tool--error]=\"tool.status === 'error'\">\n <summary>\n <mk-icon [name]=\"toolIcon(tool)\" size=\"sm\" [class.mk-chat-message__spin]=\"tool.status === 'running'\" />\n <code>{{ tool.name }}</code>\n @if (tool.summary) { <span class=\"mk-chat-message__tool-summary\">{{ tool.summary }}</span> }\n <span class=\"mk-visually-hidden\">{{ toolStatus(tool) }}</span>\n </summary>\n @if (tool.input) { <pre class=\"mk-chat-message__tool-io\"><code>{{ tool.input }}</code></pre> }\n @if (tool.output) { <pre class=\"mk-chat-message__tool-io\"><code>{{ tool.output }}</code></pre> }\n </details>\n }\n </div>\n }\n\n @if (m.attachments?.length) {\n <ul class=\"mk-chat-message__attachments\" [attr.aria-label]=\"i18n.chatAttachments\">\n @for (a of m.attachments; track $index) {\n <li>\n @if (isImage(a)) {\n <a [href]=\"a.url\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"mk-chat-message__image\">\n <img [src]=\"a.url\" [alt]=\"a.name\" loading=\"lazy\" />\n </a>\n } @else {\n <a [href]=\"a.url || null\" [attr.download]=\"a.url ? a.name : null\" class=\"mk-chat-message__file\">\n <mk-icon name=\"file\" size=\"sm\" />\n <span>{{ a.name }}</span>\n @if (a.size != null) { <span class=\"mk-chat-message__file-size\">{{ formatSize(a.size) }}</span> }\n </a>\n }\n </li>\n }\n </ul>\n }\n\n @if (m.text || m.streaming) {\n <div class=\"mk-chat-message__text\">\n @if (useMarkdown()) {\n <mk-markdown [source]=\"m.text\" linkTarget=\"_blank\" />\n } @else {\n <p class=\"mk-chat-message__plain\">{{ m.text }}</p>\n }\n @if (m.streaming) {\n <span class=\"mk-chat-message__cursor\" aria-hidden=\"true\"></span>\n <span class=\"mk-visually-hidden\">{{ i18n.chatStreaming }}</span>\n }\n </div>\n }\n </div>\n\n @if (m.error) {\n <div class=\"mk-chat-message__error\" role=\"alert\">\n <mk-icon name=\"circle-alert\" size=\"sm\" />\n <span>{{ m.error }}</span>\n <button mkButton type=\"button\" variant=\"ghost\" tone=\"danger\" size=\"sm\" (click)=\"retry.emit(m)\">\n <mk-icon name=\"refresh-cw\" size=\"sm\" /> {{ i18n.chatRetry }}\n </button>\n </div>\n }\n\n @if (copyable() && m.text && !m.streaming) {\n <div class=\"mk-chat-message__actions\">\n <button mkButton type=\"button\" variant=\"ghost\" tone=\"neutral\" size=\"sm\" [mkCopyToClipboard]=\"m.text\" [attr.aria-label]=\"i18n.chatCopy\">\n <mk-icon name=\"copy\" size=\"sm\" />\n </button>\n </div>\n }\n </div>\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n Directive,\n ElementRef,\n TemplateRef,\n afterRenderEffect,\n booleanAttribute,\n computed,\n contentChild,\n inject,\n input,\n output,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\nimport { MK_I18N } from '@mk-kit/ui/core';\nimport { MkButton } from '@mk-kit/ui/button';\nimport { MkIcon } from '@mk-kit/ui/icon';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { MkChatMessageComponent } from '../chat-message/chat-message';\nimport { MkPromptBox } from '../prompt-box/prompt-box';\nimport type { MkChatMessage, MkChatRole, MkChatSend } from '../chat.types';\n\n/** Context handed to an `*mkChatMessageDef` template. */\nexport interface MkChatMessageContext {\n $implicit: MkChatMessage;\n own: boolean;\n index: number;\n}\n\n/**\n * Custom message rendering for `mk-chat`:\n *\n * ```html\n * <mk-chat [messages]=\"messages()\">\n * <ng-template mkChatMessageDef let-message let-own=\"own\">\n * <my-bubble [message]=\"message\" [mine]=\"own\" />\n * </ng-template>\n * </mk-chat>\n * ```\n */\n@Directive({ selector: 'ng-template[mkChatMessageDef]' })\nexport class MkChatMessageDef {\n readonly template = inject(TemplateRef<MkChatMessageContext>);\n static ngTemplateContextGuard(_dir: MkChatMessageDef, ctx: unknown): ctx is MkChatMessageContext {\n return true;\n }\n}\n\n/**\n * Chat — a conversation: a scrolling, screen-reader-announced log of\n * `mk-chat-message` bubbles that stays pinned to the newest message while\n * text streams in (with a *jump to latest* button once the reader scrolls\n * up), a typing indicator, and an `mk-prompt-box` composer.\n *\n * The component is presentational: hand it `messages` (replace the array or\n * a message object to update — signals compare by reference) and react to\n * `(send)` / `(stop)` / `(retry)` with your own transport.\n *\n * ```html\n * <mk-chat\n * [messages]=\"messages()\"\n * [busy]=\"generating()\"\n * [typing]=\"peerTyping()\"\n * attachments\n * [suggestions]=\"starters\"\n * (send)=\"ask($event)\"\n * (stop)=\"abort()\"\n * (retry)=\"resend($event)\"\n * style=\"height: 32rem\"\n * >\n * <div mkChatHeader>…title, model picker…</div>\n * <div mkChatEmpty>…first-run hint…</div>\n * </mk-chat>\n * ```\n *\n * Slots: `[mkChatHeader]`, `[mkChatEmpty]`, `[mkChatFooter]` (under the composer).\n */\n@Component({\n selector: 'mk-chat',\n templateUrl: './chat.html',\n styleUrl: './chat.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [MkButton, MkChatMessageComponent, MkIcon, MkPromptBox, NgTemplateOutlet],\n host: { class: 'mk-chat' },\n})\nexport class MkChat {\n protected readonly i18n = inject(MK_I18N);\n\n /** The conversation, oldest first. */\n readonly messages = input<readonly MkChatMessage[]>([]);\n /** Which role renders on the \"own\" side (default `user`). */\n readonly ownRole = input<MkChatRole>('user');\n /** Render assistant text as Markdown. */\n readonly markdown = input(true, { transform: booleanAttribute });\n readonly showAvatars = input(true, { transform: booleanAttribute });\n readonly showTimestamps = input(true, { transform: booleanAttribute });\n /** Someone else is typing (shows the dots indicator). */\n readonly typing = input(false, { transform: booleanAttribute });\n /** A reply is being generated: composer shows *stop*. */\n readonly busy = input(false, { transform: booleanAttribute });\n /** Disable the composer. */\n readonly disabled = input(false, { transform: booleanAttribute });\n /** Hide the composer entirely (read-only transcript). */\n readonly readonly = input(false, { transform: booleanAttribute });\n readonly placeholder = input<string | undefined>(undefined);\n /** Allow attachments in the composer. */\n readonly attachments = input(false, { transform: booleanAttribute });\n readonly accept = input('');\n readonly maxFiles = input(0);\n readonly maxLength = input(0);\n /** Quick replies above the composer; each click sends its text. */\n readonly suggestions = input<readonly string[]>([]);\n /** Text shown when there are no messages (or project `[mkChatEmpty]`). */\n readonly emptyMessage = input<string | undefined>(undefined);\n /** Keep the log pinned to the newest message while the reader is at the bottom. */\n readonly autoScroll = input(true, { transform: booleanAttribute });\n /** Accessible name of the log region. */\n readonly ariaLabel = input<string | undefined>(undefined);\n\n readonly send = output<MkChatSend>();\n readonly stop = output<void>();\n readonly retry = output<MkChatMessage>();\n\n protected readonly messageDef = contentChild(MkChatMessageDef);\n private readonly log = viewChild.required<ElementRef<HTMLElement>>('log');\n private readonly composer = viewChild(MkPromptBox);\n\n /** The reader is (near) the bottom of the log. */\n protected readonly atBottom = signal(true);\n /** Messages that arrived while scrolled up. */\n protected readonly unseen = signal(0);\n private lastCount = 0;\n\n protected readonly resolvedEmpty = computed(() => this.emptyMessage() ?? this.i18n.chatEmpty);\n protected readonly resolvedPlaceholder = computed(() => this.placeholder() ?? this.i18n.chatPlaceholder);\n\n constructor() {\n // Pin to the bottom on new content while the reader is there.\n afterRenderEffect(() => {\n const list = this.messages();\n const last = list[list.length - 1];\n // Track text growth of a streaming message as well as new messages.\n void last?.text.length;\n void this.typing();\n const count = list.length;\n untracked(() => {\n const grew = count > this.lastCount;\n this.lastCount = count;\n if (!this.autoScroll()) return;\n if (this.atBottom()) this.scrollToBottom();\n else if (grew) this.unseen.update((n) => n + 1);\n });\n });\n }\n\n /** Scroll the log to the newest message. */\n scrollToBottom(): void {\n const el = this.log().nativeElement;\n el.scrollTop = el.scrollHeight;\n this.atBottom.set(true);\n this.unseen.set(0);\n }\n\n /** Focus the composer. */\n focus(): void {\n this.composer()?.focus();\n }\n\n protected onScroll(): void {\n const el = this.log().nativeElement;\n const distance = el.scrollHeight - el.scrollTop - el.clientHeight;\n const near = distance < 48;\n this.atBottom.set(near);\n if (near) this.unseen.set(0);\n }\n\n protected isOwn(m: MkChatMessage): boolean {\n return m.role === this.ownRole();\n }\n\n protected trackMessage = (_: number, m: MkChatMessage): string => m.id;\n}\n","<div class=\"mk-chat__header\">\n <ng-content select=\"[mkChatHeader]\" />\n</div>\n\n<div class=\"mk-chat__viewport\">\n <div\n #log\n class=\"mk-chat__log\"\n role=\"log\"\n aria-live=\"polite\"\n aria-relevant=\"additions text\"\n [attr.aria-label]=\"ariaLabel() ?? i18n.chatLabel\"\n (scroll)=\"onScroll()\"\n >\n @if (!messages().length) {\n <div class=\"mk-chat__empty\">\n <ng-content select=\"[mkChatEmpty]\">\n <mk-icon name=\"message-circle\" size=\"lg\" />\n <p>{{ resolvedEmpty() }}</p>\n </ng-content>\n </div>\n }\n @for (m of messages(); track m.id; let i = $index) {\n @if (messageDef(); as def) {\n <ng-container *ngTemplateOutlet=\"def.template; context: { $implicit: m, own: isOwn(m), index: i }\" />\n } @else {\n <mk-chat-message\n [message]=\"m\"\n [own]=\"isOwn(m)\"\n [markdown]=\"markdown()\"\n [showAvatar]=\"showAvatars()\"\n [showTimestamp]=\"showTimestamps()\"\n (retry)=\"retry.emit($event)\"\n />\n }\n }\n @if (typing()) {\n <div class=\"mk-chat__typing\" [attr.aria-label]=\"i18n.chatTyping\" role=\"status\">\n <span></span><span></span><span></span>\n </div>\n }\n </div>\n\n @if (!atBottom()) {\n <button mkButton type=\"button\" size=\"sm\" variant=\"outline\" tone=\"neutral\" class=\"mk-chat__jump\" (click)=\"scrollToBottom()\">\n <mk-icon name=\"arrow-down\" size=\"sm\" />\n {{ i18n.chatJumpToLatest }}\n @if (unseen() > 0) { <span class=\"mk-chat__badge\">{{ unseen() }}</span> }\n </button>\n }\n</div>\n\n@if (!readonly()) {\n <div class=\"mk-chat__composer\">\n <mk-prompt-box\n [placeholder]=\"resolvedPlaceholder()\"\n [disabled]=\"disabled()\"\n [busy]=\"busy()\"\n [attachments]=\"attachments()\"\n [accept]=\"accept()\"\n [maxFiles]=\"maxFiles()\"\n [maxLength]=\"maxLength()\"\n [suggestions]=\"suggestions()\"\n (send)=\"send.emit($event)\"\n (stop)=\"stop.emit()\"\n />\n <div class=\"mk-chat__footer\">\n <ng-content select=\"[mkChatFooter]\" />\n </div>\n </div>\n}\n","/**\n * @mk-kit/ui/chat — conversational UI: `mk-chat` (log + composer),\n * `mk-chat-message` and `mk-prompt-box`.\n */\nexport * from './chat.types';\nexport * from './prompt-box/prompt-box';\nexport * from './chat-message/chat-message';\nexport * from './chat/chat';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -176,6 +176,142 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
176
176
|
args: [{ providedIn: 'root' }]
|
|
177
177
|
}], ctorParameters: () => [] });
|
|
178
178
|
|
|
179
|
+
/** The default scale (same numbers as Tailwind, so mental models transfer). */
|
|
180
|
+
const MK_DEFAULT_BREAKPOINTS = {
|
|
181
|
+
sm: 640,
|
|
182
|
+
md: 768,
|
|
183
|
+
lg: 1024,
|
|
184
|
+
xl: 1280,
|
|
185
|
+
'2xl': 1536,
|
|
186
|
+
};
|
|
187
|
+
/** Override the breakpoint scale app-wide: `{ provide: MK_BREAKPOINTS, useValue: { …, lg: 1100 } }`. */
|
|
188
|
+
const MK_BREAKPOINTS = new InjectionToken('MK_BREAKPOINTS', {
|
|
189
|
+
providedIn: 'root',
|
|
190
|
+
factory: () => MK_DEFAULT_BREAKPOINTS,
|
|
191
|
+
});
|
|
192
|
+
const ORDER = ['xs', 'sm', 'md', 'lg', 'xl', '2xl'];
|
|
193
|
+
/** True when `value` is a per-breakpoint map rather than a plain value. */
|
|
194
|
+
function mkIsResponsive(value) {
|
|
195
|
+
return (typeof value === 'object' &&
|
|
196
|
+
value !== null &&
|
|
197
|
+
!Array.isArray(value) &&
|
|
198
|
+
Object.keys(value).every((k) => ORDER.includes(k)));
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Reactive viewport breakpoints, one `matchMedia` listener per step.
|
|
202
|
+
*
|
|
203
|
+
* ```ts
|
|
204
|
+
* private readonly bp = inject(MkBreakpointService);
|
|
205
|
+
* readonly compact = this.bp.down('md'); // Signal<boolean>
|
|
206
|
+
* readonly columns = computed(() => this.bp.resolve({ xs: 1, md: 2, xl: 4 }));
|
|
207
|
+
* ```
|
|
208
|
+
*
|
|
209
|
+
* On the server (no `window`) everything reports `xs`.
|
|
210
|
+
*/
|
|
211
|
+
class MkBreakpointService {
|
|
212
|
+
/** The breakpoint scale in effect (see {@link MK_BREAKPOINTS}). */
|
|
213
|
+
breakpoints = inject(MK_BREAKPOINTS);
|
|
214
|
+
document = inject(DOCUMENT);
|
|
215
|
+
isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
216
|
+
/** `matches` per step, in scale order (`sm` first). */
|
|
217
|
+
steps = new Map();
|
|
218
|
+
cleanup = [];
|
|
219
|
+
queries = new Map();
|
|
220
|
+
ups = new Map();
|
|
221
|
+
downs = new Map();
|
|
222
|
+
/** The widest breakpoint the viewport currently satisfies. */
|
|
223
|
+
current;
|
|
224
|
+
constructor() {
|
|
225
|
+
for (const name of ORDER.slice(1)) {
|
|
226
|
+
const min = this.breakpoints[name];
|
|
227
|
+
this.steps.set(name, this.watch(`(min-width: ${min}px)`));
|
|
228
|
+
}
|
|
229
|
+
this.current = computed(() => {
|
|
230
|
+
let current = 'xs';
|
|
231
|
+
for (const name of ORDER.slice(1))
|
|
232
|
+
if (this.steps.get(name)())
|
|
233
|
+
current = name;
|
|
234
|
+
return current;
|
|
235
|
+
}, /* @ts-ignore */
|
|
236
|
+
...(ngDevMode ? [{ debugName: "current" }] : /* istanbul ignore next */ []));
|
|
237
|
+
}
|
|
238
|
+
/** True while the viewport is at least `bp` wide (`up('md')` = md, lg, xl, 2xl). */
|
|
239
|
+
up(bp) {
|
|
240
|
+
let s = this.ups.get(bp);
|
|
241
|
+
if (!s) {
|
|
242
|
+
s = bp === 'xs' ? signal(true).asReadonly() : this.steps.get(bp).asReadonly();
|
|
243
|
+
this.ups.set(bp, s);
|
|
244
|
+
}
|
|
245
|
+
return s;
|
|
246
|
+
}
|
|
247
|
+
/** True while the viewport is narrower than `bp` (`down('md')` = xs, sm). */
|
|
248
|
+
down(bp) {
|
|
249
|
+
let s = this.downs.get(bp);
|
|
250
|
+
if (!s) {
|
|
251
|
+
const up = this.up(bp);
|
|
252
|
+
s = computed(() => !up());
|
|
253
|
+
this.downs.set(bp, s);
|
|
254
|
+
}
|
|
255
|
+
return s;
|
|
256
|
+
}
|
|
257
|
+
/** True while the viewport is at least `from` and narrower than `to`. */
|
|
258
|
+
between(from, to) {
|
|
259
|
+
const lo = this.up(from);
|
|
260
|
+
const hi = this.down(to);
|
|
261
|
+
return computed(() => lo() && hi());
|
|
262
|
+
}
|
|
263
|
+
/** Any media query as a signal, e.g. `observe('(orientation: portrait)')`. */
|
|
264
|
+
observe(query) {
|
|
265
|
+
let s = this.queries.get(query);
|
|
266
|
+
if (!s) {
|
|
267
|
+
s = this.watch(query).asReadonly();
|
|
268
|
+
this.queries.set(query, s);
|
|
269
|
+
}
|
|
270
|
+
return s;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Pick the entry of a responsive value for the current viewport —
|
|
274
|
+
* mobile-first, so `{ xs: 1, md: 2 }` gives 1 on `sm` and 2 on `xl`.
|
|
275
|
+
* A plain value is returned as is. Read inside a `computed` to track it.
|
|
276
|
+
*/
|
|
277
|
+
resolve(value) {
|
|
278
|
+
if (!mkIsResponsive(value))
|
|
279
|
+
return value;
|
|
280
|
+
const idx = ORDER.indexOf(this.current());
|
|
281
|
+
for (let i = idx; i >= 0; i--) {
|
|
282
|
+
const v = value[ORDER[i]];
|
|
283
|
+
if (v !== undefined)
|
|
284
|
+
return v;
|
|
285
|
+
}
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
ngOnDestroy() {
|
|
289
|
+
for (const off of this.cleanup)
|
|
290
|
+
off();
|
|
291
|
+
this.cleanup.length = 0;
|
|
292
|
+
}
|
|
293
|
+
watch(query) {
|
|
294
|
+
const s = signal(false, /* @ts-ignore */
|
|
295
|
+
...(ngDevMode ? [{ debugName: "s" }] : /* istanbul ignore next */ []));
|
|
296
|
+
if (!this.isBrowser)
|
|
297
|
+
return s;
|
|
298
|
+
const mql = this.document.defaultView?.matchMedia?.(query);
|
|
299
|
+
if (!mql)
|
|
300
|
+
return s;
|
|
301
|
+
s.set(mql.matches);
|
|
302
|
+
const onChange = (e) => s.set(e.matches);
|
|
303
|
+
mql.addEventListener('change', onChange);
|
|
304
|
+
this.cleanup.push(() => mql.removeEventListener('change', onChange));
|
|
305
|
+
return s;
|
|
306
|
+
}
|
|
307
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkBreakpointService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
308
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkBreakpointService, providedIn: 'root' });
|
|
309
|
+
}
|
|
310
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkBreakpointService, decorators: [{
|
|
311
|
+
type: Injectable,
|
|
312
|
+
args: [{ providedIn: 'root' }]
|
|
313
|
+
}], ctorParameters: () => [] });
|
|
314
|
+
|
|
179
315
|
/**
|
|
180
316
|
* Announces messages to assistive technology via a visually-hidden
|
|
181
317
|
* `aria-live` region. Used by toasts, form validation, sort changes, etc.
|
|
@@ -1167,6 +1303,8 @@ const MK_DEFAULT_I18N = {
|
|
|
1167
1303
|
expandHeader: 'Expand',
|
|
1168
1304
|
expandRow: 'Expand row',
|
|
1169
1305
|
collapseRow: 'Collapse row',
|
|
1306
|
+
expandTreeRow: 'Show child rows',
|
|
1307
|
+
collapseTreeRow: 'Hide child rows',
|
|
1170
1308
|
expandGroup: 'Expand group',
|
|
1171
1309
|
collapseGroup: 'Collapse group',
|
|
1172
1310
|
groupCount: (count) => `${count} item${count === 1 ? '' : 's'}`,
|
|
@@ -1221,6 +1359,63 @@ const MK_DEFAULT_I18N = {
|
|
|
1221
1359
|
logFollow: 'Follow',
|
|
1222
1360
|
logCopyAll: 'Copy log',
|
|
1223
1361
|
logWrapLines: 'Wrap lines',
|
|
1362
|
+
chatLabel: 'Conversation',
|
|
1363
|
+
chatComposerLabel: 'Message',
|
|
1364
|
+
chatPlaceholder: 'Type a message…',
|
|
1365
|
+
chatSend: 'Send',
|
|
1366
|
+
chatStop: 'Stop generating',
|
|
1367
|
+
chatAttach: 'Attach files',
|
|
1368
|
+
chatRemoveAttachment: 'Remove attachment',
|
|
1369
|
+
chatAttachments: 'Attachments',
|
|
1370
|
+
chatSuggestions: 'Suggestions',
|
|
1371
|
+
chatCopy: 'Copy message',
|
|
1372
|
+
chatRetry: 'Retry',
|
|
1373
|
+
chatTyping: 'Typing…',
|
|
1374
|
+
chatStreaming: 'Generating…',
|
|
1375
|
+
chatJumpToLatest: 'Jump to latest',
|
|
1376
|
+
chatEmpty: 'No messages yet',
|
|
1377
|
+
chatYou: 'You',
|
|
1378
|
+
chatAssistant: 'Assistant',
|
|
1379
|
+
chatToolRunning: 'Running',
|
|
1380
|
+
chatToolDone: 'Done',
|
|
1381
|
+
chatToolError: 'Failed',
|
|
1382
|
+
queryBuilderLabel: 'Filter conditions',
|
|
1383
|
+
queryAddRule: 'Add rule',
|
|
1384
|
+
queryAddGroup: 'Add group',
|
|
1385
|
+
queryRemoveRule: 'Remove rule',
|
|
1386
|
+
queryRemoveGroup: 'Remove group',
|
|
1387
|
+
queryAnd: 'And',
|
|
1388
|
+
queryOr: 'Or',
|
|
1389
|
+
queryNot: 'Not',
|
|
1390
|
+
queryField: 'Field',
|
|
1391
|
+
queryOperator: 'Operator',
|
|
1392
|
+
queryValue: 'Value',
|
|
1393
|
+
queryValueFrom: 'From',
|
|
1394
|
+
queryValueTo: 'To',
|
|
1395
|
+
queryEmptyGroup: 'No conditions yet',
|
|
1396
|
+
queryTrue: 'True',
|
|
1397
|
+
queryFalse: 'False',
|
|
1398
|
+
queryOpEq: 'equals',
|
|
1399
|
+
queryOpNeq: 'does not equal',
|
|
1400
|
+
queryOpContains: 'contains',
|
|
1401
|
+
queryOpNotContains: 'does not contain',
|
|
1402
|
+
queryOpStartsWith: 'starts with',
|
|
1403
|
+
queryOpEndsWith: 'ends with',
|
|
1404
|
+
queryOpGt: 'greater than',
|
|
1405
|
+
queryOpGte: 'at least',
|
|
1406
|
+
queryOpLt: 'less than',
|
|
1407
|
+
queryOpLte: 'at most',
|
|
1408
|
+
queryOpBetween: 'between',
|
|
1409
|
+
queryOpIn: 'is any of',
|
|
1410
|
+
queryOpNotIn: 'is none of',
|
|
1411
|
+
queryOpBefore: 'before',
|
|
1412
|
+
queryOpAfter: 'after',
|
|
1413
|
+
queryOpEmpty: 'is empty',
|
|
1414
|
+
queryOpNotEmpty: 'is not empty',
|
|
1415
|
+
dialogMove: 'Move dialog (arrow keys; Home to reset)',
|
|
1416
|
+
dialogResize: 'Resize dialog (arrow keys; Home to reset)',
|
|
1417
|
+
listboxFilter: 'Filter options',
|
|
1418
|
+
listboxEmpty: 'No matching options',
|
|
1224
1419
|
eventCalendarGrabbed: (title, from, to) => `${title} grabbed, ${from} – ${to}. Use the arrow keys to move, ` +
|
|
1225
1420
|
'Shift with Up or Down to change the end time, Enter to save, ' +
|
|
1226
1421
|
'Escape to cancel.',
|
|
@@ -1588,9 +1783,259 @@ function mkFirstErrorMessage(errors, strings, overrides) {
|
|
|
1588
1783
|
return errors ? strings.unknown : null;
|
|
1589
1784
|
}
|
|
1590
1785
|
|
|
1786
|
+
/** Operators offered per field type, in menu order. */
|
|
1787
|
+
const MK_QUERY_OPERATORS = {
|
|
1788
|
+
string: ['contains', 'notContains', 'eq', 'neq', 'startsWith', 'endsWith', 'empty', 'notEmpty'],
|
|
1789
|
+
number: ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'between', 'empty', 'notEmpty'],
|
|
1790
|
+
boolean: ['eq'],
|
|
1791
|
+
date: ['eq', 'neq', 'before', 'after', 'between', 'empty', 'notEmpty'],
|
|
1792
|
+
select: ['eq', 'neq', 'in', 'notIn', 'empty', 'notEmpty'],
|
|
1793
|
+
};
|
|
1794
|
+
/** Operators that take no value. */
|
|
1795
|
+
const MK_QUERY_UNARY = new Set(['empty', 'notEmpty']);
|
|
1796
|
+
/** True for a group node (as opposed to a rule). */
|
|
1797
|
+
function mkIsQueryGroup(node) {
|
|
1798
|
+
return Array.isArray(node.rules);
|
|
1799
|
+
}
|
|
1800
|
+
/** A fresh empty group. */
|
|
1801
|
+
function mkCreateQueryGroup(init = {}) {
|
|
1802
|
+
return { id: mkUniqueId('mkq'), combinator: 'and', rules: [], ...init };
|
|
1803
|
+
}
|
|
1804
|
+
/** A fresh rule on `field`, using its first operator. */
|
|
1805
|
+
function mkCreateQueryRule(field) {
|
|
1806
|
+
const ops = mkQueryOperatorsFor(field);
|
|
1807
|
+
return { id: mkUniqueId('mkq'), field: field.key, operator: ops[0] ?? 'eq' };
|
|
1808
|
+
}
|
|
1809
|
+
/** Operators a field offers (its own list, else the defaults for its type). */
|
|
1810
|
+
function mkQueryOperatorsFor(field) {
|
|
1811
|
+
if (!field)
|
|
1812
|
+
return MK_QUERY_OPERATORS.string;
|
|
1813
|
+
return field.operators ?? MK_QUERY_OPERATORS[field.type ?? 'string'];
|
|
1814
|
+
}
|
|
1815
|
+
/** True when the tree holds no rule at all (empty groups only). */
|
|
1816
|
+
function mkQueryIsEmpty(group) {
|
|
1817
|
+
return group.rules.every((n) => (mkIsQueryGroup(n) ? mkQueryIsEmpty(n) : false));
|
|
1818
|
+
}
|
|
1819
|
+
/** Number of rules in the tree. */
|
|
1820
|
+
function mkQueryRuleCount(group) {
|
|
1821
|
+
return group.rules.reduce((n, r) => n + (mkIsQueryGroup(r) ? mkQueryRuleCount(r) : 1), 0);
|
|
1822
|
+
}
|
|
1823
|
+
/** Drop empty groups and rules that still need a value, so the API gets only complete conditions. */
|
|
1824
|
+
function mkQueryCompact(group) {
|
|
1825
|
+
const rules = group.rules
|
|
1826
|
+
.map((n) => (mkIsQueryGroup(n) ? mkQueryCompact(n) : n))
|
|
1827
|
+
.filter((n) => (mkIsQueryGroup(n) ? n.rules.length > 0 : mkQueryRuleIsComplete(n)));
|
|
1828
|
+
return { ...group, rules };
|
|
1829
|
+
}
|
|
1830
|
+
/** A rule is complete when its operator needs no value or has one. */
|
|
1831
|
+
function mkQueryRuleIsComplete(rule) {
|
|
1832
|
+
if (MK_QUERY_UNARY.has(rule.operator))
|
|
1833
|
+
return true;
|
|
1834
|
+
const v = rule.value;
|
|
1835
|
+
if (v == null || v === '')
|
|
1836
|
+
return false;
|
|
1837
|
+
if (rule.operator === 'between')
|
|
1838
|
+
return Array.isArray(v) && v.length === 2 && v.every((x) => x != null && x !== '');
|
|
1839
|
+
if (rule.operator === 'in' || rule.operator === 'notIn')
|
|
1840
|
+
return Array.isArray(v) && v.length > 0;
|
|
1841
|
+
return true;
|
|
1842
|
+
}
|
|
1843
|
+
/** Sortable / comparable primitive of a raw value for a field type. */
|
|
1844
|
+
function normalise(value, type) {
|
|
1845
|
+
if (value == null || value === '')
|
|
1846
|
+
return null;
|
|
1847
|
+
switch (type) {
|
|
1848
|
+
case 'number': {
|
|
1849
|
+
const n = typeof value === 'number' ? value : Number(value);
|
|
1850
|
+
return Number.isNaN(n) ? null : n;
|
|
1851
|
+
}
|
|
1852
|
+
case 'date': {
|
|
1853
|
+
const d = value instanceof Date ? value : new Date(value);
|
|
1854
|
+
return Number.isNaN(d.getTime()) ? null : d.getTime();
|
|
1855
|
+
}
|
|
1856
|
+
case 'boolean':
|
|
1857
|
+
return value === true || value === 'true';
|
|
1858
|
+
default:
|
|
1859
|
+
return typeof value === 'string' ? value : String(value);
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
/** Day precision for date equality: two timestamps on the same local day are equal. */
|
|
1863
|
+
function sameDay(a, b) {
|
|
1864
|
+
const da = new Date(a);
|
|
1865
|
+
const db = new Date(b);
|
|
1866
|
+
return da.getFullYear() === db.getFullYear() && da.getMonth() === db.getMonth() && da.getDate() === db.getDate();
|
|
1867
|
+
}
|
|
1868
|
+
function inferType(rule) {
|
|
1869
|
+
const v = Array.isArray(rule.value) ? rule.value[0] : rule.value;
|
|
1870
|
+
if (typeof v === 'number')
|
|
1871
|
+
return 'number';
|
|
1872
|
+
if (typeof v === 'boolean')
|
|
1873
|
+
return 'boolean';
|
|
1874
|
+
if (v instanceof Date)
|
|
1875
|
+
return 'date';
|
|
1876
|
+
return 'string';
|
|
1877
|
+
}
|
|
1878
|
+
/** Evaluate one rule against a row. */
|
|
1879
|
+
function mkQueryRuleMatches(rule, row, field) {
|
|
1880
|
+
const type = field?.type ?? inferType(rule);
|
|
1881
|
+
const raw = row[rule.field];
|
|
1882
|
+
const isEmpty = raw == null || raw === '' || (Array.isArray(raw) && raw.length === 0);
|
|
1883
|
+
if (rule.operator === 'empty')
|
|
1884
|
+
return isEmpty;
|
|
1885
|
+
if (rule.operator === 'notEmpty')
|
|
1886
|
+
return !isEmpty;
|
|
1887
|
+
if (!mkQueryRuleIsComplete(rule))
|
|
1888
|
+
return true; // an unfinished rule filters nothing
|
|
1889
|
+
if (isEmpty)
|
|
1890
|
+
return false;
|
|
1891
|
+
const cmpType = type === 'select' ? 'string' : type;
|
|
1892
|
+
const left = normalise(raw, cmpType);
|
|
1893
|
+
const collator = new Intl.Collator(undefined, { sensitivity: 'base' });
|
|
1894
|
+
const eqText = (a, b) => collator.compare(String(a), String(b)) === 0;
|
|
1895
|
+
switch (rule.operator) {
|
|
1896
|
+
case 'eq':
|
|
1897
|
+
case 'neq': {
|
|
1898
|
+
const right = normalise(rule.value, cmpType);
|
|
1899
|
+
let equal;
|
|
1900
|
+
if (left == null || right == null)
|
|
1901
|
+
equal = left === right;
|
|
1902
|
+
else if (cmpType === 'date')
|
|
1903
|
+
equal = sameDay(left, right);
|
|
1904
|
+
else if (cmpType === 'string')
|
|
1905
|
+
equal = eqText(left, right);
|
|
1906
|
+
else
|
|
1907
|
+
equal = left === right;
|
|
1908
|
+
return rule.operator === 'eq' ? equal : !equal;
|
|
1909
|
+
}
|
|
1910
|
+
case 'contains':
|
|
1911
|
+
case 'notContains': {
|
|
1912
|
+
const has = String(raw).toLocaleLowerCase().includes(String(rule.value).toLocaleLowerCase());
|
|
1913
|
+
return rule.operator === 'contains' ? has : !has;
|
|
1914
|
+
}
|
|
1915
|
+
case 'startsWith':
|
|
1916
|
+
return String(raw).toLocaleLowerCase().startsWith(String(rule.value).toLocaleLowerCase());
|
|
1917
|
+
case 'endsWith':
|
|
1918
|
+
return String(raw).toLocaleLowerCase().endsWith(String(rule.value).toLocaleLowerCase());
|
|
1919
|
+
case 'gt':
|
|
1920
|
+
case 'gte':
|
|
1921
|
+
case 'lt':
|
|
1922
|
+
case 'lte':
|
|
1923
|
+
case 'before':
|
|
1924
|
+
case 'after': {
|
|
1925
|
+
const right = normalise(rule.value, cmpType);
|
|
1926
|
+
if (left == null || right == null || typeof left === 'boolean' || typeof right === 'boolean')
|
|
1927
|
+
return false;
|
|
1928
|
+
const c = typeof left === 'number' && typeof right === 'number' ? left - right : collator.compare(String(left), String(right));
|
|
1929
|
+
switch (rule.operator) {
|
|
1930
|
+
case 'gt':
|
|
1931
|
+
case 'after':
|
|
1932
|
+
return c > 0;
|
|
1933
|
+
case 'gte':
|
|
1934
|
+
return c >= 0;
|
|
1935
|
+
case 'lt':
|
|
1936
|
+
case 'before':
|
|
1937
|
+
return c < 0;
|
|
1938
|
+
default:
|
|
1939
|
+
return c <= 0;
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
case 'between': {
|
|
1943
|
+
const [a, b] = rule.value;
|
|
1944
|
+
const lo = normalise(a, cmpType);
|
|
1945
|
+
const hi = normalise(b, cmpType);
|
|
1946
|
+
if (left == null || lo == null || hi == null)
|
|
1947
|
+
return false;
|
|
1948
|
+
if (typeof left === 'number' && typeof lo === 'number' && typeof hi === 'number')
|
|
1949
|
+
return left >= lo && left <= hi;
|
|
1950
|
+
return collator.compare(String(left), String(lo)) >= 0 && collator.compare(String(left), String(hi)) <= 0;
|
|
1951
|
+
}
|
|
1952
|
+
case 'in':
|
|
1953
|
+
case 'notIn': {
|
|
1954
|
+
const set = rule.value;
|
|
1955
|
+
const hit = Array.isArray(raw)
|
|
1956
|
+
? raw.some((r) => set.some((s) => eqText(r, s)))
|
|
1957
|
+
: set.some((s) => (cmpType === 'string' ? eqText(raw, s) : normalise(raw, cmpType) === normalise(s, cmpType)));
|
|
1958
|
+
return rule.operator === 'in' ? hit : !hit;
|
|
1959
|
+
}
|
|
1960
|
+
default:
|
|
1961
|
+
return true;
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
/**
|
|
1965
|
+
* Compile a query into a row predicate for client-side filtering
|
|
1966
|
+
* (`rows.filter(mkQueryToPredicate(query, fields))`). Unfinished rules are
|
|
1967
|
+
* ignored, so a half-edited query never blanks the table.
|
|
1968
|
+
*/
|
|
1969
|
+
function mkQueryToPredicate(group, fields = []) {
|
|
1970
|
+
const byKey = new Map(fields.map((f) => [f.key, f]));
|
|
1971
|
+
const evalGroup = (g, row) => {
|
|
1972
|
+
const active = g.rules.filter((n) => (mkIsQueryGroup(n) ? !mkQueryIsEmpty(n) : mkQueryRuleIsComplete(n)));
|
|
1973
|
+
let result;
|
|
1974
|
+
if (!active.length)
|
|
1975
|
+
result = true;
|
|
1976
|
+
else if (g.combinator === 'or')
|
|
1977
|
+
result = active.some((n) => evalNode(n, row));
|
|
1978
|
+
else
|
|
1979
|
+
result = active.every((n) => evalNode(n, row));
|
|
1980
|
+
return g.not ? !result : result;
|
|
1981
|
+
};
|
|
1982
|
+
const evalNode = (n, row) => mkIsQueryGroup(n) ? evalGroup(n, row) : mkQueryRuleMatches(n, row, byKey.get(n.field));
|
|
1983
|
+
return (row) => evalGroup(group, row);
|
|
1984
|
+
}
|
|
1985
|
+
/** Localised label of an operator. */
|
|
1986
|
+
function mkQueryOperatorLabel(op, i18n = MK_DEFAULT_I18N) {
|
|
1987
|
+
const key = `queryOp${op[0].toUpperCase()}${op.slice(1)}`;
|
|
1988
|
+
return i18n[key] ?? op;
|
|
1989
|
+
}
|
|
1990
|
+
/**
|
|
1991
|
+
* Human-readable sentence for a query, e.g.
|
|
1992
|
+
* `(Name contains "ada" and Orders at least 10) or Status is any of Active, Invited`.
|
|
1993
|
+
*/
|
|
1994
|
+
function mkQueryToText(group, fields = [], i18n = MK_DEFAULT_I18N) {
|
|
1995
|
+
const byKey = new Map(fields.map((f) => [f.key, f]));
|
|
1996
|
+
const fmt = (v, field) => {
|
|
1997
|
+
if (field?.type === 'select') {
|
|
1998
|
+
const opt = field.options?.find((o) => o.value === v);
|
|
1999
|
+
if (opt)
|
|
2000
|
+
return opt.label;
|
|
2001
|
+
}
|
|
2002
|
+
if (field?.type === 'boolean' || typeof v === 'boolean')
|
|
2003
|
+
return v === true || v === 'true' ? i18n.queryTrue : i18n.queryFalse;
|
|
2004
|
+
if (typeof v === 'number')
|
|
2005
|
+
return String(v);
|
|
2006
|
+
if (v instanceof Date)
|
|
2007
|
+
return v.toISOString().slice(0, 10);
|
|
2008
|
+
return `"${String(v)}"`;
|
|
2009
|
+
};
|
|
2010
|
+
const rule = (r) => {
|
|
2011
|
+
const f = byKey.get(r.field);
|
|
2012
|
+
const label = f?.label ?? r.field;
|
|
2013
|
+
const op = mkQueryOperatorLabel(r.operator, i18n);
|
|
2014
|
+
if (MK_QUERY_UNARY.has(r.operator))
|
|
2015
|
+
return `${label} ${op}`;
|
|
2016
|
+
if (r.operator === 'between' && Array.isArray(r.value))
|
|
2017
|
+
return `${label} ${op} ${fmt(r.value[0], f)} – ${fmt(r.value[1], f)}`;
|
|
2018
|
+
if (Array.isArray(r.value))
|
|
2019
|
+
return `${label} ${op} ${r.value.map((v) => fmt(v, f)).join(', ')}`;
|
|
2020
|
+
return `${label} ${op} ${fmt(r.value, f)}`;
|
|
2021
|
+
};
|
|
2022
|
+
const grp = (g, top) => {
|
|
2023
|
+
const parts = g.rules
|
|
2024
|
+
.filter((n) => (mkIsQueryGroup(n) ? !mkQueryIsEmpty(n) : mkQueryRuleIsComplete(n)))
|
|
2025
|
+
.map((n) => (mkIsQueryGroup(n) ? grp(n, false) : rule(n)));
|
|
2026
|
+
if (!parts.length)
|
|
2027
|
+
return '';
|
|
2028
|
+
const joiner = g.combinator === 'or' ? i18n.queryOr : i18n.queryAnd;
|
|
2029
|
+
const body = parts.join(` ${joiner.toLocaleLowerCase()} `);
|
|
2030
|
+
const wrapped = top && !g.not ? body : parts.length > 1 || g.not ? `(${body})` : body;
|
|
2031
|
+
return g.not ? `${i18n.queryNot.toLocaleLowerCase()} ${wrapped}` : wrapped;
|
|
2032
|
+
};
|
|
2033
|
+
return grp(group, true);
|
|
2034
|
+
}
|
|
2035
|
+
|
|
1591
2036
|
/**
|
|
1592
2037
|
* Generated bundle index. Do not edit.
|
|
1593
2038
|
*/
|
|
1594
2039
|
|
|
1595
|
-
export { MK_DEFAULT_DATE_NAMES, MK_DEFAULT_I18N, MK_DEFAULT_VALIDATION, MK_I18N, MK_OVERLAY_DATA, MkAnchoredPanel, MkFieldContext, MkFocusTrap, MkLiveAnnouncer, MkOverlayRef, MkOverlayService, MkThemeService, mkComputeAnchoredPosition, mkFirstErrorMessage, mkGetFocusable, mkHighlight, mkHighlightJson, mkUniqueId, mkValidatorChange, provideMkI18n };
|
|
2040
|
+
export { MK_BREAKPOINTS, MK_DEFAULT_BREAKPOINTS, MK_DEFAULT_DATE_NAMES, MK_DEFAULT_I18N, MK_DEFAULT_VALIDATION, MK_I18N, MK_OVERLAY_DATA, MK_QUERY_OPERATORS, MK_QUERY_UNARY, MkAnchoredPanel, MkBreakpointService, MkFieldContext, MkFocusTrap, MkLiveAnnouncer, MkOverlayRef, MkOverlayService, MkThemeService, mkComputeAnchoredPosition, mkCreateQueryGroup, mkCreateQueryRule, mkFirstErrorMessage, mkGetFocusable, mkHighlight, mkHighlightJson, mkIsQueryGroup, mkIsResponsive, mkQueryCompact, mkQueryIsEmpty, mkQueryOperatorLabel, mkQueryOperatorsFor, mkQueryRuleCount, mkQueryRuleIsComplete, mkQueryRuleMatches, mkQueryToPredicate, mkQueryToText, mkUniqueId, mkValidatorChange, provideMkI18n };
|
|
1596
2041
|
//# sourceMappingURL=mk-kit-ui-core.mjs.map
|