{"version":3,"file":"mn-angular-lib-keyboard.mjs","sources":["../../../projects/mn-angular-lib/keyboard/src/mn-keyboard/mn-keyboard.component.ts","../../../projects/mn-angular-lib/keyboard/src/mn-keyboard/mn-keyboard.component.html","../../../projects/mn-angular-lib/keyboard/public-api.ts","../../../projects/mn-angular-lib/keyboard/mn-angular-lib-keyboard.ts"],"sourcesContent":["import {\n  Component,\n  EventEmitter,\n  HostBinding,\n  Input,\n  Output,\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { MnBottomSheet } from 'mn-angular-lib/bottom-sheet';\nimport { MnKeyboardLabels, MnKeyboardLayout, MnKeyboardPresentation } from './mn-keyboard.types';\n\n/** QWERTY letter rows, lower-case. Rendered upper-case when {@link MnKeyboard.uppercase} is set. */\nconst ALPHA_ROWS: readonly string[][] = [\n  ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'],\n  ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'],\n  ['z', 'x', 'c', 'v', 'b', 'n', 'm'],\n];\n\n/** The digit row that sits above the letters on an alphanumeric keyboard. */\nconst DIGIT_ROW: readonly string[] = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];\n\n/** Phone-style pad rows for the numeric layout. */\nconst NUMERIC_ROWS: readonly string[][] = [\n  ['1', '2', '3'],\n  ['4', '5', '6'],\n  ['7', '8', '9'],\n  ['0'],\n];\n\n/**\n * An on-screen keyboard for touch devices that have no keyboard of their own.\n *\n * It exists for unattended screens — a tablet on a wall, a kiosk by a door — where\n * the OS keyboard is either unavailable or unwanted, and where the same field may\n * need to take a membership number *or* a name. Hence one component with a\n * {@link layout} switch rather than a separate number pad and text pad.\n *\n * It is a controlled component: it never owns the text. The host passes {@link value}\n * and reacts to {@link valueChange}, exactly like a form control, so the same value\n * can also be filled by a barcode scanner or a real keyboard without this component\n * fighting it.\n *\n * With {@link presentation} set to `sheet` it mounts inside an {@link MnBottomSheet},\n * rising from the bottom of the screen and swipeable away — the shape a kiosk wants\n * so the keys do not permanently occupy half the display.\n *\n * The library ships no user-facing English, so every named key takes its caption\n * from {@link labels}.\n */\n@Component({\n  selector: 'mn-keyboard',\n  standalone: true,\n  imports: [MnBottomSheet, NgTemplateOutlet],\n  templateUrl: './mn-keyboard.component.html',\n  styleUrl: './mn-keyboard.component.css',\n})\nexport class MnKeyboard {\n  /** The current text. This component never mutates it — see {@link valueChange}. */\n  @Input() value = '';\n  /** Which keys to render. */\n  @Input() layout: MnKeyboardLayout = 'alphanumeric';\n  /** Whether the keys sit in the page or rise from the bottom as a sheet. */\n  @Input() presentation: MnKeyboardPresentation = 'inline';\n  /**\n   * Captions and accessible names for the named keys.\n   *\n   * The defaults are language-neutral glyphs rather than words, so a consumer that\n   * forgets to translate still ships no English (or Dutch) from the library. The\n   * accessible name for the keyboard as a whole has no neutral glyph, so it\n   * defaults to empty and the attribute is simply omitted until a host supplies one.\n   */\n  @Input() labels: MnKeyboardLabels = {\n    backspace: '⌫',\n    clear: '⨯',\n    space: '␣',\n    submit: '⏎',\n    keyboard: '',\n  };\n  /** Whether letters are rendered (and typed) upper-case. */\n  @Input() uppercase = false;\n  /** Whether to offer a space key. Off for a field that can never contain one. */\n  @Input() allowSpace = true;\n  /** Whether to offer a submit key. */\n  @Input() showSubmit = true;\n  /** Whether the submit key is currently actionable. */\n  @Input() submitDisabled = false;\n  /** Hard cap on the text length, or null for none. */\n  @Input() maxLength: number | null = null;\n  /** Cap on the sheet height as a fraction of the viewport, in vh (sheet presentation only). */\n  @Input() sheetMaxHeightVh = 60;\n\n  /** Emits the full text after every key press, so the host can drive its own field. */\n  @Output() valueChange = new EventEmitter<string>();\n  /** Emits when the submit key is pressed. */\n  @Output() submitted = new EventEmitter<string>();\n  /** Emits when a sheet-presented keyboard is swiped or tapped away. */\n  @Output() dismissed = new EventEmitter<void>();\n\n  @HostBinding('class') get hostClasses(): string {\n    return `mn-keyboard mn-keyboard-${this.layout}`;\n  }\n\n  /** The character rows to render, driven by {@link layout}. */\n  get rows(): readonly string[][] {\n    if (this.layout === 'numeric') {\n      return NUMERIC_ROWS;\n    }\n    const letters = this.uppercase\n      ? ALPHA_ROWS.map((row) => row.map((key) => key.toUpperCase()))\n      : ALPHA_ROWS;\n    return this.layout === 'alpha' ? letters : [[...DIGIT_ROW], ...letters];\n  }\n\n  /** Whether the space key should be offered (never on a digits-only pad). */\n  get spaceVisible(): boolean {\n    return this.allowSpace && this.layout !== 'numeric';\n  }\n\n  /**\n   * Appends a character, respecting {@link maxLength}.\n   * @param key The character pressed.\n   */\n  press(key: string): void {\n    this.emit(this.value + key);\n  }\n\n  /** Appends a space. */\n  pressSpace(): void {\n    this.emit(this.value + ' ');\n  }\n\n  /** Removes the last character. */\n  backspace(): void {\n    this.emit(this.value.slice(0, -1));\n  }\n\n  /** Empties the field. */\n  clear(): void {\n    this.emit('');\n  }\n\n  /** Reports a submit press. The host decides what submitting means. */\n  submit(): void {\n    if (this.submitDisabled) {\n      return;\n    }\n    this.submitted.emit(this.value);\n  }\n\n  /** Reports that a sheet-presented keyboard was dismissed. */\n  onDismiss(): void {\n    this.dismissed.emit();\n  }\n\n  /**\n   * Emits a new value, clamped to {@link maxLength}.\n   *\n   * Clamping happens here rather than in each key handler so a paste-like burst\n   * from a scanner and a tapped key are bounded the same way.\n   * @param next The candidate text.\n   */\n  private emit(next: string): void {\n    const clamped =\n      this.maxLength !== null && next.length > this.maxLength\n        ? next.slice(0, this.maxLength)\n        : next;\n    if (clamped === this.value) {\n      return;\n    }\n    this.value = clamped;\n    this.valueChange.emit(clamped);\n  }\n}\n","<!-- The key block. Rendered directly for `inline`, or projected into the sheet below. -->\n<ng-template #keys>\n  <div [attr.aria-label]=\"labels.keyboard || null\" class=\"mn-keyboard-keys flex flex-col gap-2 p-3\" role=\"group\">\n    @for (row of rows; track $index) {\n      <div class=\"flex justify-center gap-2\">\n        @for (key of row; track key) {\n          <button\n            (click)=\"press(key)\"\n            class=\"mn-keyboard-key flex-1 rounded-lg border border-base-300 bg-base-100 py-4 text-xl font-semibold text-base-content shadow-sm transition-colors hover:bg-base-200 active:bg-base-300\"\n            type=\"button\"\n          >\n            {{ key }}\n          </button>\n        }\n      </div>\n    }\n\n    <div class=\"flex justify-center gap-2\">\n      <button\n        (click)=\"backspace()\"\n        [attr.aria-label]=\"labels.backspace\"\n        class=\"mn-keyboard-key mn-keyboard-key-action flex-1 rounded-lg border border-base-300 bg-base-200 py-4 text-base font-medium text-base-content shadow-sm transition-colors hover:bg-base-300\"\n        type=\"button\"\n      >\n        {{ labels.backspace }}\n      </button>\n\n      @if (spaceVisible) {\n        <button\n          (click)=\"pressSpace()\"\n          [attr.aria-label]=\"labels.space\"\n          class=\"mn-keyboard-key mn-keyboard-key-space flex-1 rounded-lg border border-base-300 bg-base-200 py-4 text-base font-medium text-base-content shadow-sm transition-colors hover:bg-base-300\"\n          style=\"flex-grow: 3\"\n          type=\"button\"\n        >\n          {{ labels.space }}\n        </button>\n      }\n\n      <button\n        (click)=\"clear()\"\n        [attr.aria-label]=\"labels.clear\"\n        class=\"mn-keyboard-key mn-keyboard-key-action flex-1 rounded-lg border border-base-300 bg-base-200 py-4 text-base font-medium text-base-content shadow-sm transition-colors hover:bg-base-300\"\n        type=\"button\"\n      >\n        {{ labels.clear }}\n      </button>\n\n      @if (showSubmit) {\n        <button\n          (click)=\"submit()\"\n          [attr.aria-label]=\"labels.submit\"\n          [disabled]=\"submitDisabled\"\n          class=\"mn-keyboard-key mn-keyboard-key-submit flex-1 rounded-lg bg-primary py-4 text-base font-semibold text-primary-content shadow-sm transition-colors hover:opacity-90 disabled:opacity-50\"\n          style=\"flex-grow: 2\"\n          type=\"button\"\n        >\n          {{ labels.submit }}\n        </button>\n      }\n    </div>\n  </div>\n</ng-template>\n\n@if (presentation === 'sheet') {\n  <mn-bottom-sheet\n    (dismiss)=\"onDismiss()\"\n    [ariaLabel]=\"labels.keyboard || undefined\"\n    [maxHeightVh]=\"sheetMaxHeightVh\"\n  >\n    <ng-container [ngTemplateOutlet]=\"keys\"></ng-container>\n  </mn-bottom-sheet>\n} @else {\n  <ng-container [ngTemplateOutlet]=\"keys\"></ng-container>\n}\n","/**\n * Public API of the `mn-angular-lib/keyboard` entry point: the on-screen keyboard.\n *\n * Each entry point is its own module in the published package, so a consumer's bundler\n * splits it into the chunk that uses it instead of loading the whole library at startup.\n * The root `mn-angular-lib` entry re-exports every entry point.\n */\nexport * from './src/mn-keyboard';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}