@makigamestudio/ui-core 0.1.7 → 0.2.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.
@@ -1,8 +1,8 @@
1
1
  import * as i0 from '@angular/core';
2
- import { signal, computed, Injectable, input, output, viewChild, ChangeDetectionStrategy, Component } from '@angular/core';
3
- import { IonCard, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCardContent, IonButton, IonIcon } from '@ionic/angular/standalone';
2
+ import { signal, computed, Injectable, input, output, viewChild, ChangeDetectionStrategy, Component, inject } from '@angular/core';
3
+ import { IonCard, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCardContent, IonButton, IonIcon, PopoverController, IonList, IonItem, IonLabel, IonSpinner } from '@ionic/angular/standalone';
4
4
  import { addIcons } from 'ionicons';
5
- import { trashOutline, createOutline } from 'ionicons/icons';
5
+ import { trashOutline, createOutline, chevronDownOutline } from 'ionicons/icons';
6
6
 
7
7
  /**
8
8
  * TestClass - Example class implementing TestInterface.
@@ -32,7 +32,7 @@ class TestClass {
32
32
  title,
33
33
  description,
34
34
  createdAt: new Date(),
35
- isActive: true,
35
+ isActive: true
36
36
  });
37
37
  }
38
38
  /**
@@ -41,7 +41,7 @@ class TestClass {
41
41
  update(changes) {
42
42
  return new TestClass({
43
43
  ...this,
44
- ...changes,
44
+ ...changes
45
45
  });
46
46
  }
47
47
  /**
@@ -60,11 +60,44 @@ class TestClass {
60
60
  description: this.description,
61
61
  createdAt: this.createdAt,
62
62
  isActive: this.isActive,
63
- metadata: this.metadata,
63
+ metadata: this.metadata
64
64
  };
65
65
  }
66
66
  }
67
67
 
68
+ /**
69
+ * @file Action button type enumeration
70
+ * @description Defines the display types for action buttons
71
+ */
72
+ /**
73
+ * Enumeration of action button display types.
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * const button: ActionButton = {
78
+ * id: 'save-btn',
79
+ * label: 'Save',
80
+ * type: ActionButtonType.Default,
81
+ * handler: () => console.log('Saved!')
82
+ * };
83
+ * ```
84
+ */
85
+ var ActionButtonType;
86
+ (function (ActionButtonType) {
87
+ /**
88
+ * Standard button with label and optional icon.
89
+ */
90
+ ActionButtonType["Default"] = "default";
91
+ /**
92
+ * Button displaying only an icon, no label.
93
+ */
94
+ ActionButtonType["IconOnly"] = "icon-only";
95
+ /**
96
+ * Button that opens a dropdown popover with child actions.
97
+ */
98
+ ActionButtonType["Dropdown"] = "dropdown";
99
+ })(ActionButtonType || (ActionButtonType = {}));
100
+
68
101
  /**
69
102
  * TestService - Example service using Angular Signals for reactive state management.
70
103
  * Provided in root, demonstrating singleton service pattern with signal-based state.
@@ -84,7 +117,7 @@ class TestService {
84
117
  /** Count of all items */
85
118
  count = computed(() => this._items().length, ...(ngDevMode ? [{ debugName: "count" }] : []));
86
119
  /** Only active items */
87
- activeItems = computed(() => this._items().filter((item) => item.isActive), ...(ngDevMode ? [{ debugName: "activeItems" }] : []));
120
+ activeItems = computed(() => this._items().filter(item => item.isActive), ...(ngDevMode ? [{ debugName: "activeItems" }] : []));
88
121
  /** Count of active items */
89
122
  activeCount = computed(() => this.activeItems().length, ...(ngDevMode ? [{ debugName: "activeCount" }] : []));
90
123
  /**
@@ -92,7 +125,7 @@ class TestService {
92
125
  */
93
126
  addItem(title, description) {
94
127
  const newItem = TestClass.create(title, description);
95
- this._items.update((items) => [...items, newItem]);
128
+ this._items.update(items => [...items, newItem]);
96
129
  this._error.set(null);
97
130
  return newItem;
98
131
  }
@@ -101,7 +134,7 @@ class TestService {
101
134
  */
102
135
  removeItem(id) {
103
136
  const currentItems = this._items();
104
- const filteredItems = currentItems.filter((item) => item.id !== id);
137
+ const filteredItems = currentItems.filter(item => item.id !== id);
105
138
  if (filteredItems.length === currentItems.length) {
106
139
  return false;
107
140
  }
@@ -113,7 +146,7 @@ class TestService {
113
146
  */
114
147
  updateItem(id, changes) {
115
148
  let updatedItem = null;
116
- this._items.update((items) => items.map((item) => {
149
+ this._items.update(items => items.map(item => {
117
150
  if (item.id === id) {
118
151
  updatedItem = item.update(changes);
119
152
  return updatedItem;
@@ -132,7 +165,7 @@ class TestService {
132
165
  * Gets an item by ID using computed lookup.
133
166
  */
134
167
  getItemById(id) {
135
- return this._items().find((item) => item.id === id);
168
+ return this._items().find(item => item.id === id);
136
169
  }
137
170
  /**
138
171
  * Clears all items from the store.
@@ -159,7 +192,7 @@ class TestService {
159
192
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TestService, decorators: [{
160
193
  type: Injectable,
161
194
  args: [{
162
- providedIn: 'root',
195
+ providedIn: 'root'
163
196
  }]
164
197
  }] });
165
198
 
@@ -196,7 +229,7 @@ class TestCardComponent {
196
229
  return dateObj.toLocaleDateString(undefined, {
197
230
  year: 'numeric',
198
231
  month: 'short',
199
- day: 'numeric',
232
+ day: 'numeric'
200
233
  });
201
234
  }, ...(ngDevMode ? [{ debugName: "formattedDate" }] : []));
202
235
  constructor() {
@@ -221,11 +254,7 @@ class TestCardComponent {
221
254
  }
222
255
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TestCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
223
256
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TestCardComponent, isStandalone: true, selector: "maki-test-card", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, showActions: { classPropertyName: "showActions", publicName: "showActions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemClick: "itemClick", editClick: "editClick", deleteClick: "deleteClick" }, viewQueries: [{ propertyName: "cardElement", first: true, predicate: ["cardElement"], descendants: true, isSignal: true }], ngImport: i0, template: `
224
- <ion-card
225
- #cardElement
226
- [class.inactive]="!item().isActive"
227
- (click)="onCardClick()"
228
- >
257
+ <ion-card #cardElement [class.inactive]="!item().isActive" (click)="onCardClick()">
229
258
  <ion-card-header>
230
259
  <ion-card-title>{{ item().title }}</ion-card-title>
231
260
  @if (formattedDate()) {
@@ -246,20 +275,11 @@ class TestCardComponent {
246
275
 
247
276
  @if (showActions()) {
248
277
  <ion-card-content class="card-actions">
249
- <ion-button
250
- fill="outline"
251
- size="small"
252
- (click)="onEditClick($event)"
253
- >
278
+ <ion-button fill="outline" size="small" (click)="onEditClick($event)">
254
279
  <ion-icon slot="start" name="create-outline"></ion-icon>
255
280
  Edit
256
281
  </ion-button>
257
- <ion-button
258
- fill="outline"
259
- size="small"
260
- color="danger"
261
- (click)="onDeleteClick($event)"
262
- >
282
+ <ion-button fill="outline" size="small" color="danger" (click)="onDeleteClick($event)">
263
283
  <ion-icon slot="start" name="trash-outline"></ion-icon>
264
284
  Delete
265
285
  </ion-button>
@@ -277,13 +297,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
277
297
  IonCardSubtitle,
278
298
  IonCardContent,
279
299
  IonButton,
280
- IonIcon,
300
+ IonIcon
281
301
  ], changeDetection: ChangeDetectionStrategy.OnPush, template: `
282
- <ion-card
283
- #cardElement
284
- [class.inactive]="!item().isActive"
285
- (click)="onCardClick()"
286
- >
302
+ <ion-card #cardElement [class.inactive]="!item().isActive" (click)="onCardClick()">
287
303
  <ion-card-header>
288
304
  <ion-card-title>{{ item().title }}</ion-card-title>
289
305
  @if (formattedDate()) {
@@ -304,20 +320,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
304
320
 
305
321
  @if (showActions()) {
306
322
  <ion-card-content class="card-actions">
307
- <ion-button
308
- fill="outline"
309
- size="small"
310
- (click)="onEditClick($event)"
311
- >
323
+ <ion-button fill="outline" size="small" (click)="onEditClick($event)">
312
324
  <ion-icon slot="start" name="create-outline"></ion-icon>
313
325
  Edit
314
326
  </ion-button>
315
- <ion-button
316
- fill="outline"
317
- size="small"
318
- color="danger"
319
- (click)="onDeleteClick($event)"
320
- >
327
+ <ion-button fill="outline" size="small" color="danger" (click)="onDeleteClick($event)">
321
328
  <ion-icon slot="start" name="trash-outline"></ion-icon>
322
329
  Delete
323
330
  </ion-button>
@@ -327,6 +334,411 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
327
334
  `, styles: [":host{display:block}ion-card{--background: var(--maki-card-background, var(--ion-card-background, #fff));--color: var(--maki-card-color, var(--ion-text-color));margin:var(--maki-card-margin, 16px);border-radius:var(--maki-card-border-radius, 12px);transition:opacity .2s ease,transform .2s ease;cursor:pointer}ion-card:hover{transform:translateY(-2px)}ion-card.inactive{opacity:.6}ion-card-title{color:var(--maki-primary, var(--ion-color-primary));font-weight:600}ion-card-subtitle{display:flex;align-items:center;gap:8px}.status-badge{font-size:.75rem;padding:2px 8px;border-radius:4px;font-weight:500}.status-badge.inactive{background-color:var(--ion-color-medium-tint);color:var(--ion-color-medium-contrast)}.card-actions{display:flex;gap:8px;padding-top:0}.card-actions ion-button{--border-color: var(--maki-primary, var(--ion-color-primary));--color: var(--maki-primary, var(--ion-color-primary))}\n"] }]
328
335
  }], ctorParameters: () => [], propDecorators: { item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], showActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showActions", required: false }] }], itemClick: [{ type: i0.Output, args: ["itemClick"] }], editClick: [{ type: i0.Output, args: ["editClick"] }], deleteClick: [{ type: i0.Output, args: ["deleteClick"] }], cardElement: [{ type: i0.ViewChild, args: ['cardElement', { isSignal: true }] }] } });
329
336
 
337
+ /**
338
+ * @file Action Button List Component
339
+ * @description Component for rendering a list of action buttons inside a popover
340
+ */
341
+ /**
342
+ * Component that renders a list of action buttons for use in popovers/dropdowns.
343
+ *
344
+ * This component is designed to be used as the content of an `ion-popover`
345
+ * created via `PopoverController`. When a button is selected, it dismisses
346
+ * the popover and returns the selected button.
347
+ *
348
+ * @example
349
+ * ```typescript
350
+ * // Used internally by maki-button for dropdowns
351
+ * // Can also be used standalone:
352
+ * const popover = await popoverCtrl.create({
353
+ * component: ActionButtonListComponent,
354
+ * componentProps: { buttons: myButtons },
355
+ * event: clickEvent
356
+ * });
357
+ * await popover.present();
358
+ *
359
+ * const { data } = await popover.onDidDismiss();
360
+ * if (data) {
361
+ * data.handler();
362
+ * }
363
+ * ```
364
+ *
365
+ * @usageNotes
366
+ * ### Inputs
367
+ * - `buttons` (required): Array of `ActionButton` objects to display
368
+ *
369
+ * ### Outputs
370
+ * - `buttonSelect`: Emits the selected `ActionButton` when clicked
371
+ */
372
+ class ActionButtonListComponent {
373
+ /**
374
+ * Reference to ActionButtonType.Dropdown for template comparison.
375
+ * @internal
376
+ */
377
+ dropdownType = ActionButtonType.Dropdown;
378
+ /**
379
+ * Popover controller for dismissing the popover when an item is selected.
380
+ */
381
+ popoverCtrl = inject(PopoverController, { optional: true });
382
+ /**
383
+ * Internal signal to store buttons passed via componentProps.
384
+ * PopoverController.create() sets properties directly, bypassing signal inputs.
385
+ */
386
+ _buttonsFromProps = signal([], ...(ngDevMode ? [{ debugName: "_buttonsFromProps" }] : []));
387
+ /**
388
+ * The list of action buttons to display (when used via template binding).
389
+ */
390
+ buttons = input([], ...(ngDevMode ? [{ debugName: "buttons" }] : []));
391
+ /**
392
+ * Computed button list that works with both signal input and componentProps.
393
+ * PopoverController passes buttons as a direct property, not through Angular's input system.
394
+ */
395
+ buttonList = () => {
396
+ const fromProps = this._buttonsFromProps();
397
+ const fromInput = this.buttons();
398
+ return fromProps.length > 0 ? fromProps : fromInput;
399
+ };
400
+ /**
401
+ * Setter to capture buttons passed via PopoverController.create({ componentProps }).
402
+ * This is called when Ionic sets the property directly on the component instance.
403
+ */
404
+ set buttonsFromPopover(value) {
405
+ this._buttonsFromProps.set(value);
406
+ }
407
+ /**
408
+ * Emits when a button is selected from the list.
409
+ */
410
+ buttonSelect = output();
411
+ constructor() {
412
+ // Register any icons that might be used
413
+ addIcons({});
414
+ }
415
+ /**
416
+ * Handles click on a button item.
417
+ * Emits the selected button and dismisses the popover.
418
+ *
419
+ * @param button - The clicked action button
420
+ */
421
+ onButtonClick(button) {
422
+ if (button.loading || button.config?.disabled) {
423
+ return;
424
+ }
425
+ this.buttonSelect.emit(button);
426
+ this.popoverCtrl?.dismiss(button, 'select');
427
+ }
428
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ActionButtonListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
429
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: ActionButtonListComponent, isStandalone: true, selector: "maki-action-button-list", inputs: { buttons: { classPropertyName: "buttons", publicName: "buttons", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { buttonSelect: "buttonSelect" }, ngImport: i0, template: `
430
+ <ion-list lines="none">
431
+ @for (button of buttonList(); track button.id) {
432
+ <ion-item
433
+ [button]="true"
434
+ [disabled]="button.loading || button.config?.disabled"
435
+ [detail]="button.type === dropdownType"
436
+ (click)="onButtonClick(button)"
437
+ >
438
+ @if (button.loading) {
439
+ <ion-spinner slot="start" name="crescent" />
440
+ } @else if (button.icon) {
441
+ <ion-icon slot="start" [name]="button.icon" />
442
+ }
443
+ @if (button.label) {
444
+ <ion-label>{{ button.label }}</ion-label>
445
+ }
446
+ </ion-item>
447
+ } @empty {
448
+ <ion-item>
449
+ <ion-label color="medium">No actions available</ion-label>
450
+ </ion-item>
451
+ }
452
+ </ion-list>
453
+ `, isInline: true, styles: [":host{display:block}ion-list{padding:0}ion-item{--padding-start: var(--maki-action-button-list-padding, 16px);--padding-end: var(--maki-action-button-list-padding, 16px);--min-height: var(--maki-action-button-list-item-height, 44px);cursor:pointer;width:fit-content}ion-label{white-space:nowrap}ion-icon{font-size:var(--maki-action-button-list-icon-size, 20px);margin-inline-end:var(--maki-action-button-list-icon-gap, 12px)}ion-spinner{width:var(--maki-action-button-list-icon-size, 20px);height:var(--maki-action-button-list-icon-size, 20px);margin-inline-end:var(--maki-action-button-list-icon-gap, 12px)}\n"], dependencies: [{ kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
454
+ }
455
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ActionButtonListComponent, decorators: [{
456
+ type: Component,
457
+ args: [{ selector: 'maki-action-button-list', standalone: true, imports: [IonList, IonItem, IonIcon, IonLabel, IonSpinner], changeDetection: ChangeDetectionStrategy.OnPush, template: `
458
+ <ion-list lines="none">
459
+ @for (button of buttonList(); track button.id) {
460
+ <ion-item
461
+ [button]="true"
462
+ [disabled]="button.loading || button.config?.disabled"
463
+ [detail]="button.type === dropdownType"
464
+ (click)="onButtonClick(button)"
465
+ >
466
+ @if (button.loading) {
467
+ <ion-spinner slot="start" name="crescent" />
468
+ } @else if (button.icon) {
469
+ <ion-icon slot="start" [name]="button.icon" />
470
+ }
471
+ @if (button.label) {
472
+ <ion-label>{{ button.label }}</ion-label>
473
+ }
474
+ </ion-item>
475
+ } @empty {
476
+ <ion-item>
477
+ <ion-label color="medium">No actions available</ion-label>
478
+ </ion-item>
479
+ }
480
+ </ion-list>
481
+ `, styles: [":host{display:block}ion-list{padding:0}ion-item{--padding-start: var(--maki-action-button-list-padding, 16px);--padding-end: var(--maki-action-button-list-padding, 16px);--min-height: var(--maki-action-button-list-item-height, 44px);cursor:pointer;width:fit-content}ion-label{white-space:nowrap}ion-icon{font-size:var(--maki-action-button-list-icon-size, 20px);margin-inline-end:var(--maki-action-button-list-icon-gap, 12px)}ion-spinner{width:var(--maki-action-button-list-icon-size, 20px);height:var(--maki-action-button-list-icon-size, 20px);margin-inline-end:var(--maki-action-button-list-icon-gap, 12px)}\n"] }]
482
+ }], ctorParameters: () => [], propDecorators: { buttons: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttons", required: false }] }], buttonSelect: [{ type: i0.Output, args: ["buttonSelect"] }] } });
483
+
484
+ /**
485
+ * @file Button Component
486
+ * @description Configurable button component that renders ion-button based on ActionButton configuration
487
+ */
488
+ /**
489
+ * A configurable button component that renders an `ion-button` based on
490
+ * an `ActionButton` configuration object.
491
+ *
492
+ * Features:
493
+ * - Three display types: Default (label + icon), IconOnly, and Dropdown
494
+ * - Automatic loading state management for async handlers
495
+ * - Dropdown support via PopoverController with child actions
496
+ * - Full Ionic button styling configuration (fill, size, color, shape, expand)
497
+ * - Automatic chevron icon for dropdown buttons
498
+ *
499
+ * @example
500
+ * ```html
501
+ * <!-- Simple button -->
502
+ * <maki-button [button]="saveButton" />
503
+ *
504
+ * <!-- Icon-only button -->
505
+ * <maki-button [button]="iconButton" />
506
+ *
507
+ * <!-- Dropdown button -->
508
+ * <maki-button [button]="menuButton" />
509
+ * ```
510
+ *
511
+ * @example
512
+ * ```typescript
513
+ * // In your component
514
+ * saveButton: ActionButton = {
515
+ * id: 'save',
516
+ * label: 'Save',
517
+ * icon: 'saveOutline',
518
+ * type: ActionButtonType.Default,
519
+ * config: { fill: 'solid', color: 'primary' },
520
+ * handler: async () => {
521
+ * await this.saveData();
522
+ * }
523
+ * };
524
+ *
525
+ * menuButton: ActionButton = {
526
+ * id: 'menu',
527
+ * label: 'Actions',
528
+ * type: ActionButtonType.Dropdown,
529
+ * handler: () => {},
530
+ * children: [
531
+ * { id: 'edit', label: 'Edit', icon: 'createOutline', type: ActionButtonType.Default, handler: () => this.edit() },
532
+ * { id: 'delete', label: 'Delete', icon: 'trashOutline', type: ActionButtonType.Default, handler: () => this.delete() }
533
+ * ]
534
+ * };
535
+ * ```
536
+ *
537
+ * @usageNotes
538
+ * ### Inputs
539
+ * - `button` (required): The `ActionButton` configuration object
540
+ *
541
+ * ### Outputs
542
+ * - `buttonClick`: Emits the button configuration when clicked (for non-dropdown buttons)
543
+ * - `childSelect`: Emits the selected child button (for dropdown buttons)
544
+ *
545
+ * ### Loading State
546
+ * The component automatically manages loading state for async handlers.
547
+ * When a handler returns a Promise, the button shows a spinner until it resolves/rejects.
548
+ * You can also manually control loading via the `button.loading` property.
549
+ *
550
+ * ### Dropdown Behavior
551
+ * For `ActionButtonType.Dropdown`, clicking the button opens an `ion-popover`
552
+ * containing an `ActionButtonListComponent` with the child buttons.
553
+ * When a child is selected, its handler is executed and `childSelect` is emitted.
554
+ */
555
+ class ButtonComponent {
556
+ /**
557
+ * Popover controller for creating dropdown popovers.
558
+ */
559
+ popoverCtrl = inject(PopoverController);
560
+ /**
561
+ * Internal loading state for async handler execution.
562
+ */
563
+ _isLoading = signal(false, ...(ngDevMode ? [{ debugName: "_isLoading" }] : []));
564
+ /**
565
+ * The action button configuration.
566
+ */
567
+ button = input.required(...(ngDevMode ? [{ debugName: "button" }] : []));
568
+ /**
569
+ * Emits when the button is clicked (for non-dropdown buttons).
570
+ * Emits the button configuration that was clicked.
571
+ */
572
+ buttonClick = output();
573
+ /**
574
+ * Emits when a child button is selected from a dropdown.
575
+ * Only emits for `ActionButtonType.Dropdown` buttons.
576
+ */
577
+ childSelect = output();
578
+ /**
579
+ * Combined loading state from external prop and internal async state.
580
+ */
581
+ isLoading = computed(() => this._isLoading() || (this.button().loading ?? false), ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
582
+ /**
583
+ * Whether the button is disabled (from config or loading state).
584
+ */
585
+ isDisabled = computed(() => this.isLoading() || (this.button().config?.disabled ?? false), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
586
+ /**
587
+ * Determines the slot for the icon based on button type and label presence.
588
+ */
589
+ iconSlot = computed(() => {
590
+ const btn = this.button();
591
+ if (btn.type === ActionButtonType.IconOnly) {
592
+ return 'icon-only';
593
+ }
594
+ return btn.label ? 'start' : 'icon-only';
595
+ }, ...(ngDevMode ? [{ debugName: "iconSlot" }] : []));
596
+ /**
597
+ * Whether to show the label text.
598
+ */
599
+ showLabel = computed(() => {
600
+ const btn = this.button();
601
+ return btn.type !== ActionButtonType.IconOnly && btn.label;
602
+ }, ...(ngDevMode ? [{ debugName: "showLabel" }] : []));
603
+ /**
604
+ * Whether to show the dropdown chevron icon.
605
+ */
606
+ showDropdownIcon = computed(() => {
607
+ const btn = this.button();
608
+ return btn.type === ActionButtonType.Dropdown && !btn.config?.hideDropdownIcon;
609
+ }, ...(ngDevMode ? [{ debugName: "showDropdownIcon" }] : []));
610
+ constructor() {
611
+ addIcons({ chevronDownOutline });
612
+ }
613
+ /**
614
+ * Handles button click events.
615
+ * For dropdown buttons, opens a popover with child actions.
616
+ * For other buttons, executes the handler with auto-loading management.
617
+ *
618
+ * @param event - The click event
619
+ */
620
+ async onClick(event) {
621
+ const btn = this.button();
622
+ if (this.isDisabled()) {
623
+ return;
624
+ }
625
+ if (btn.type === ActionButtonType.Dropdown) {
626
+ await this.openDropdown(event);
627
+ }
628
+ else {
629
+ await this.executeHandler(btn);
630
+ this.buttonClick.emit(btn);
631
+ }
632
+ }
633
+ /**
634
+ * Opens a dropdown popover with child action buttons.
635
+ *
636
+ * @param event - The triggering click event for popover positioning
637
+ */
638
+ async openDropdown(event) {
639
+ const btn = this.button();
640
+ const children = btn.children ?? [];
641
+ if (children.length === 0) {
642
+ return;
643
+ }
644
+ const popover = await this.popoverCtrl.create({
645
+ component: ActionButtonListComponent,
646
+ componentProps: {
647
+ buttonsFromPopover: children
648
+ },
649
+ event,
650
+ translucent: true,
651
+ dismissOnSelect: true,
652
+ side: 'bottom',
653
+ alignment: 'end',
654
+ arrow: false
655
+ });
656
+ await popover.present();
657
+ const { data, role } = await popover.onDidDismiss();
658
+ if (role === 'select' && data) {
659
+ await this.executeHandler(data);
660
+ this.childSelect.emit(data);
661
+ }
662
+ }
663
+ /**
664
+ * Executes a button's handler with automatic loading state management.
665
+ *
666
+ * @param btn - The button whose handler to execute
667
+ */
668
+ async executeHandler(btn) {
669
+ const result = btn.handler();
670
+ if (result instanceof Promise) {
671
+ this._isLoading.set(true);
672
+ try {
673
+ await result;
674
+ }
675
+ finally {
676
+ this._isLoading.set(false);
677
+ }
678
+ }
679
+ }
680
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
681
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: ButtonComponent, isStandalone: true, selector: "maki-button", inputs: { button: { classPropertyName: "button", publicName: "button", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { buttonClick: "buttonClick", childSelect: "childSelect" }, ngImport: i0, template: `
682
+ <ion-button
683
+ [fill]="button().config?.fill"
684
+ [size]="button().config?.size"
685
+ [color]="button().config?.color"
686
+ [shape]="button().config?.shape"
687
+ [expand]="button().config?.expand"
688
+ [strong]="button().config?.strong"
689
+ [disabled]="isDisabled()"
690
+ [attr.aria-label]="button().ariaLabel"
691
+ [title]="button().tooltip ?? ''"
692
+ (click)="onClick($event)"
693
+ >
694
+ @if (isLoading()) {
695
+ <ion-spinner name="crescent" />
696
+ } @else {
697
+ @if (button().icon) {
698
+ <ion-icon [name]="button().icon" [slot]="iconSlot()" />
699
+ }
700
+ @if (showLabel()) {
701
+ {{ button().label }}
702
+ }
703
+ @if (showDropdownIcon()) {
704
+ <ion-icon name="chevron-down-outline" slot="end" class="dropdown-icon" />
705
+ }
706
+ }
707
+ </ion-button>
708
+ `, isInline: true, styles: [":host{display:inline-block}ion-button{--padding-start: var(--maki-button-padding-start);--padding-end: var(--maki-button-padding-end)}ion-spinner{width:var(--maki-button-spinner-size, 20px);height:var(--maki-button-spinner-size, 20px)}.dropdown-icon{font-size:var(--maki-button-dropdown-icon-size, 16px);margin-inline-start:var(--maki-button-dropdown-icon-gap, 4px)}\n"], dependencies: [{ kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
709
+ }
710
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ButtonComponent, decorators: [{
711
+ type: Component,
712
+ args: [{ selector: 'maki-button', standalone: true, imports: [IonButton, IonIcon, IonSpinner], changeDetection: ChangeDetectionStrategy.OnPush, template: `
713
+ <ion-button
714
+ [fill]="button().config?.fill"
715
+ [size]="button().config?.size"
716
+ [color]="button().config?.color"
717
+ [shape]="button().config?.shape"
718
+ [expand]="button().config?.expand"
719
+ [strong]="button().config?.strong"
720
+ [disabled]="isDisabled()"
721
+ [attr.aria-label]="button().ariaLabel"
722
+ [title]="button().tooltip ?? ''"
723
+ (click)="onClick($event)"
724
+ >
725
+ @if (isLoading()) {
726
+ <ion-spinner name="crescent" />
727
+ } @else {
728
+ @if (button().icon) {
729
+ <ion-icon [name]="button().icon" [slot]="iconSlot()" />
730
+ }
731
+ @if (showLabel()) {
732
+ {{ button().label }}
733
+ }
734
+ @if (showDropdownIcon()) {
735
+ <ion-icon name="chevron-down-outline" slot="end" class="dropdown-icon" />
736
+ }
737
+ }
738
+ </ion-button>
739
+ `, styles: [":host{display:inline-block}ion-button{--padding-start: var(--maki-button-padding-start);--padding-end: var(--maki-button-padding-end)}ion-spinner{width:var(--maki-button-spinner-size, 20px);height:var(--maki-button-spinner-size, 20px)}.dropdown-icon{font-size:var(--maki-button-dropdown-icon-size, 16px);margin-inline-start:var(--maki-button-dropdown-icon-gap, 4px)}\n"] }]
740
+ }], ctorParameters: () => [], propDecorators: { button: [{ type: i0.Input, args: [{ isSignal: true, alias: "button", required: true }] }], buttonClick: [{ type: i0.Output, args: ["buttonClick"] }], childSelect: [{ type: i0.Output, args: ["childSelect"] }] } });
741
+
330
742
  /*
331
743
  * Public API Surface of @makigamestudio/ui-core
332
744
  */
@@ -336,5 +748,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
336
748
  * Generated bundle index. Do not edit.
337
749
  */
338
750
 
339
- export { TestCardComponent, TestClass, TestService };
751
+ export { ActionButtonListComponent, ActionButtonType, ButtonComponent, TestCardComponent, TestClass, TestService };
340
752
  //# sourceMappingURL=makigamestudio-ui-core.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"makigamestudio-ui-core.mjs","sources":["../../../projects/ui-core/src/lib/models/test.class.ts","../../../projects/ui-core/src/lib/services/test.service.ts","../../../projects/ui-core/src/lib/components/test-card/test-card.component.ts","../../../projects/ui-core/src/public-api.ts","../../../projects/ui-core/src/makigamestudio-ui-core.ts"],"sourcesContent":["import { TestInterface } from './test.interface';\n\n/**\n * TestClass - Example class implementing TestInterface.\n * Demonstrates the pattern for creating model classes with validation and factory methods.\n */\nexport class TestClass implements TestInterface {\n readonly id: string;\n readonly title: string;\n readonly description?: string;\n readonly createdAt: Date;\n readonly isActive: boolean;\n readonly metadata?: Record<string, unknown>;\n\n constructor(data: TestInterface) {\n this.id = data.id;\n this.title = data.title;\n this.description = data.description;\n this.createdAt = data.createdAt instanceof Date ? data.createdAt : new Date(data.createdAt);\n this.isActive = data.isActive;\n this.metadata = data.metadata;\n }\n\n /**\n * Factory method to create a new TestClass instance with a generated ID.\n */\n static create(title: string, description?: string): TestClass {\n return new TestClass({\n id: crypto.randomUUID(),\n title,\n description,\n createdAt: new Date(),\n isActive: true,\n });\n }\n\n /**\n * Creates a new instance with updated properties (immutable pattern).\n */\n update(changes: Partial<Omit<TestInterface, 'id' | 'createdAt'>>): TestClass {\n return new TestClass({\n ...this,\n ...changes,\n });\n }\n\n /**\n * Returns a deactivated copy of this instance.\n */\n deactivate(): TestClass {\n return this.update({ isActive: false });\n }\n\n /**\n * Converts the instance to a plain JSON object.\n */\n toJSON(): TestInterface {\n return {\n id: this.id,\n title: this.title,\n description: this.description,\n createdAt: this.createdAt,\n isActive: this.isActive,\n metadata: this.metadata,\n };\n }\n}\n","import { Injectable, computed, signal } from '@angular/core';\nimport { TestClass } from '../models/test.class';\nimport { TestInterface } from '../models/test.interface';\n\n/**\n * TestService - Example service using Angular Signals for reactive state management.\n * Provided in root, demonstrating singleton service pattern with signal-based state.\n */\n@Injectable({\n providedIn: 'root',\n})\nexport class TestService {\n // Private writable signal for internal state\n private readonly _items = signal<readonly TestClass[]>([]);\n private readonly _loading = signal<boolean>(false);\n private readonly _error = signal<string | null>(null);\n\n // Public readonly computed signals for consumers\n /** All items in the store */\n readonly items = this._items.asReadonly();\n\n /** Loading state indicator */\n readonly loading = this._loading.asReadonly();\n\n /** Current error message, if any */\n readonly error = this._error.asReadonly();\n\n /** Count of all items */\n readonly count = computed(() => this._items().length);\n\n /** Only active items */\n readonly activeItems = computed(() => this._items().filter((item) => item.isActive));\n\n /** Count of active items */\n readonly activeCount = computed(() => this.activeItems().length);\n\n /**\n * Adds a new item to the store.\n */\n addItem(title: string, description?: string): TestClass {\n const newItem = TestClass.create(title, description);\n this._items.update((items) => [...items, newItem]);\n this._error.set(null);\n return newItem;\n }\n\n /**\n * Removes an item by ID.\n */\n removeItem(id: string): boolean {\n const currentItems = this._items();\n const filteredItems = currentItems.filter((item) => item.id !== id);\n\n if (filteredItems.length === currentItems.length) {\n return false;\n }\n\n this._items.set(filteredItems);\n return true;\n }\n\n /**\n * Updates an existing item by ID.\n */\n updateItem(id: string, changes: Partial<Omit<TestInterface, 'id' | 'createdAt'>>): TestClass | null {\n let updatedItem: TestClass | null = null;\n\n this._items.update((items) =>\n items.map((item) => {\n if (item.id === id) {\n updatedItem = item.update(changes);\n return updatedItem;\n }\n return item;\n })\n );\n\n return updatedItem;\n }\n\n /**\n * Deactivates an item by ID.\n */\n deactivateItem(id: string): TestClass | null {\n return this.updateItem(id, { isActive: false });\n }\n\n /**\n * Gets an item by ID using computed lookup.\n */\n getItemById(id: string): TestClass | undefined {\n return this._items().find((item) => item.id === id);\n }\n\n /**\n * Clears all items from the store.\n */\n clearAll(): void {\n this._items.set([]);\n this._error.set(null);\n }\n\n /**\n * Sets the loading state.\n */\n setLoading(loading: boolean): void {\n this._loading.set(loading);\n }\n\n /**\n * Sets an error message.\n */\n setError(error: string | null): void {\n this._error.set(error);\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n ElementRef,\n input,\n output,\n viewChild,\n} from '@angular/core';\nimport { IonButton, IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, IonIcon } from '@ionic/angular/standalone';\nimport { addIcons } from 'ionicons';\nimport { createOutline, trashOutline } from 'ionicons/icons';\nimport { TestInterface } from '../../models/test.interface';\n\n/**\n * TestCardComponent - Standalone Ionic Card component demonstrating\n * Angular 21 best practices: OnPush, Signal inputs/outputs/queries.\n *\n * @example\n * ```html\n * <maki-test-card\n * [item]=\"myItem\"\n * [showActions]=\"true\"\n * (itemClick)=\"onItemClick($event)\"\n * (deleteClick)=\"onDelete($event)\"\n * />\n * ```\n */\n@Component({\n selector: 'maki-test-card',\n standalone: true,\n imports: [\n IonCard,\n IonCardHeader,\n IonCardTitle,\n IonCardSubtitle,\n IonCardContent,\n IonButton,\n IonIcon,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <ion-card\n #cardElement\n [class.inactive]=\"!item().isActive\"\n (click)=\"onCardClick()\"\n >\n <ion-card-header>\n <ion-card-title>{{ item().title }}</ion-card-title>\n @if (formattedDate()) {\n <ion-card-subtitle>\n {{ formattedDate() }}\n @if (!item().isActive) {\n <span class=\"status-badge inactive\">Inactive</span>\n }\n </ion-card-subtitle>\n }\n </ion-card-header>\n\n @if (item().description) {\n <ion-card-content>\n <p>{{ item().description }}</p>\n </ion-card-content>\n }\n\n @if (showActions()) {\n <ion-card-content class=\"card-actions\">\n <ion-button\n fill=\"outline\"\n size=\"small\"\n (click)=\"onEditClick($event)\"\n >\n <ion-icon slot=\"start\" name=\"create-outline\"></ion-icon>\n Edit\n </ion-button>\n <ion-button\n fill=\"outline\"\n size=\"small\"\n color=\"danger\"\n (click)=\"onDeleteClick($event)\"\n >\n <ion-icon slot=\"start\" name=\"trash-outline\"></ion-icon>\n Delete\n </ion-button>\n </ion-card-content>\n }\n </ion-card>\n `,\n styles: [`\n :host {\n display: block;\n }\n\n ion-card {\n --background: var(--maki-card-background, var(--ion-card-background, #fff));\n --color: var(--maki-card-color, var(--ion-text-color));\n margin: var(--maki-card-margin, 16px);\n border-radius: var(--maki-card-border-radius, 12px);\n transition: opacity 0.2s ease, transform 0.2s ease;\n cursor: pointer;\n\n &:hover {\n transform: translateY(-2px);\n }\n\n &.inactive {\n opacity: 0.6;\n }\n }\n\n ion-card-title {\n color: var(--maki-primary, var(--ion-color-primary));\n font-weight: 600;\n }\n\n ion-card-subtitle {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .status-badge {\n font-size: 0.75rem;\n padding: 2px 8px;\n border-radius: 4px;\n font-weight: 500;\n\n &.inactive {\n background-color: var(--ion-color-medium-tint);\n color: var(--ion-color-medium-contrast);\n }\n }\n\n .card-actions {\n display: flex;\n gap: 8px;\n padding-top: 0;\n\n ion-button {\n --border-color: var(--maki-primary, var(--ion-color-primary));\n --color: var(--maki-primary, var(--ion-color-primary));\n }\n }\n `],\n})\nexport class TestCardComponent {\n // Signal inputs\n readonly item = input.required<TestInterface>();\n readonly showActions = input<boolean>(false);\n\n // Signal outputs\n readonly itemClick = output<TestInterface>();\n readonly editClick = output<TestInterface>();\n readonly deleteClick = output<TestInterface>();\n\n // Signal queries\n readonly cardElement = viewChild<ElementRef<HTMLIonCardElement>>('cardElement');\n\n // Computed signals\n readonly formattedDate = computed(() => {\n const date = this.item().createdAt;\n if (!date) return '';\n\n const dateObj = date instanceof Date ? date : new Date(date);\n return dateObj.toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n });\n\n constructor() {\n addIcons({ createOutline, trashOutline });\n }\n\n protected onCardClick(): void {\n this.itemClick.emit(this.item());\n }\n\n protected onEditClick(event: Event): void {\n event.stopPropagation();\n this.editClick.emit(this.item());\n }\n\n protected onDeleteClick(event: Event): void {\n event.stopPropagation();\n this.deleteClick.emit(this.item());\n }\n\n /**\n * Public method to focus the card element programmatically.\n */\n focus(): void {\n this.cardElement()?.nativeElement?.focus();\n }\n}\n","/*\n * Public API Surface of @makigamestudio/ui-core\n */\n\n// Models\nexport { TestClass } from './lib/models/test.class';\nexport type { TestInterface } from './lib/models/test.interface';\n\n// Services\nexport { TestService } from './lib/services/test.service';\n\n// Components\nexport { TestCardComponent } from './lib/components/test-card/test-card.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"makigamestudio-ui-core.mjs","sources":["../../../projects/ui-core/src/lib/models/test.class.ts","../../../projects/ui-core/src/lib/models/action-button-type.enum.ts","../../../projects/ui-core/src/lib/services/test.service.ts","../../../projects/ui-core/src/lib/components/test-card/test-card.component.ts","../../../projects/ui-core/src/lib/components/action-button-list/action-button-list.component.ts","../../../projects/ui-core/src/lib/components/button/button.component.ts","../../../projects/ui-core/src/public-api.ts","../../../projects/ui-core/src/makigamestudio-ui-core.ts"],"sourcesContent":["import { TestInterface } from './test.interface';\n\n/**\n * TestClass - Example class implementing TestInterface.\n * Demonstrates the pattern for creating model classes with validation and factory methods.\n */\nexport class TestClass implements TestInterface {\n readonly id: string;\n readonly title: string;\n readonly description?: string;\n readonly createdAt: Date;\n readonly isActive: boolean;\n readonly metadata?: Record<string, unknown>;\n\n constructor(data: TestInterface) {\n this.id = data.id;\n this.title = data.title;\n this.description = data.description;\n this.createdAt = data.createdAt instanceof Date ? data.createdAt : new Date(data.createdAt);\n this.isActive = data.isActive;\n this.metadata = data.metadata;\n }\n\n /**\n * Factory method to create a new TestClass instance with a generated ID.\n */\n static create(title: string, description?: string): TestClass {\n return new TestClass({\n id: crypto.randomUUID(),\n title,\n description,\n createdAt: new Date(),\n isActive: true\n });\n }\n\n /**\n * Creates a new instance with updated properties (immutable pattern).\n */\n update(changes: Partial<Omit<TestInterface, 'id' | 'createdAt'>>): TestClass {\n return new TestClass({\n ...this,\n ...changes\n });\n }\n\n /**\n * Returns a deactivated copy of this instance.\n */\n deactivate(): TestClass {\n return this.update({ isActive: false });\n }\n\n /**\n * Converts the instance to a plain JSON object.\n */\n toJSON(): TestInterface {\n return {\n id: this.id,\n title: this.title,\n description: this.description,\n createdAt: this.createdAt,\n isActive: this.isActive,\n metadata: this.metadata\n };\n }\n}\n","/**\n * @file Action button type enumeration\n * @description Defines the display types for action buttons\n */\n\n/**\n * Enumeration of action button display types.\n *\n * @example\n * ```typescript\n * const button: ActionButton = {\n * id: 'save-btn',\n * label: 'Save',\n * type: ActionButtonType.Default,\n * handler: () => console.log('Saved!')\n * };\n * ```\n */\nexport enum ActionButtonType {\n /**\n * Standard button with label and optional icon.\n */\n Default = 'default',\n\n /**\n * Button displaying only an icon, no label.\n */\n IconOnly = 'icon-only',\n\n /**\n * Button that opens a dropdown popover with child actions.\n */\n Dropdown = 'dropdown'\n}\n","import { Injectable, computed, signal } from '@angular/core';\nimport { TestClass } from '../models/test.class';\nimport { TestInterface } from '../models/test.interface';\n\n/**\n * TestService - Example service using Angular Signals for reactive state management.\n * Provided in root, demonstrating singleton service pattern with signal-based state.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class TestService {\n // Private writable signal for internal state\n private readonly _items = signal<readonly TestClass[]>([]);\n private readonly _loading = signal<boolean>(false);\n private readonly _error = signal<string | null>(null);\n\n // Public readonly computed signals for consumers\n /** All items in the store */\n readonly items = this._items.asReadonly();\n\n /** Loading state indicator */\n readonly loading = this._loading.asReadonly();\n\n /** Current error message, if any */\n readonly error = this._error.asReadonly();\n\n /** Count of all items */\n readonly count = computed(() => this._items().length);\n\n /** Only active items */\n readonly activeItems = computed(() => this._items().filter(item => item.isActive));\n\n /** Count of active items */\n readonly activeCount = computed(() => this.activeItems().length);\n\n /**\n * Adds a new item to the store.\n */\n addItem(title: string, description?: string): TestClass {\n const newItem = TestClass.create(title, description);\n this._items.update(items => [...items, newItem]);\n this._error.set(null);\n return newItem;\n }\n\n /**\n * Removes an item by ID.\n */\n removeItem(id: string): boolean {\n const currentItems = this._items();\n const filteredItems = currentItems.filter(item => item.id !== id);\n\n if (filteredItems.length === currentItems.length) {\n return false;\n }\n\n this._items.set(filteredItems);\n return true;\n }\n\n /**\n * Updates an existing item by ID.\n */\n updateItem(\n id: string,\n changes: Partial<Omit<TestInterface, 'id' | 'createdAt'>>\n ): TestClass | null {\n let updatedItem: TestClass | null = null;\n\n this._items.update(items =>\n items.map(item => {\n if (item.id === id) {\n updatedItem = item.update(changes);\n return updatedItem;\n }\n return item;\n })\n );\n\n return updatedItem;\n }\n\n /**\n * Deactivates an item by ID.\n */\n deactivateItem(id: string): TestClass | null {\n return this.updateItem(id, { isActive: false });\n }\n\n /**\n * Gets an item by ID using computed lookup.\n */\n getItemById(id: string): TestClass | undefined {\n return this._items().find(item => item.id === id);\n }\n\n /**\n * Clears all items from the store.\n */\n clearAll(): void {\n this._items.set([]);\n this._error.set(null);\n }\n\n /**\n * Sets the loading state.\n */\n setLoading(loading: boolean): void {\n this._loading.set(loading);\n }\n\n /**\n * Sets an error message.\n */\n setError(error: string | null): void {\n this._error.set(error);\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n ElementRef,\n input,\n output,\n viewChild\n} from '@angular/core';\nimport {\n IonButton,\n IonCard,\n IonCardContent,\n IonCardHeader,\n IonCardSubtitle,\n IonCardTitle,\n IonIcon\n} from '@ionic/angular/standalone';\nimport { addIcons } from 'ionicons';\nimport { createOutline, trashOutline } from 'ionicons/icons';\nimport { TestInterface } from '../../models/test.interface';\n\n/**\n * TestCardComponent - Standalone Ionic Card component demonstrating\n * Angular 21 best practices: OnPush, Signal inputs/outputs/queries.\n *\n * @example\n * ```html\n * <maki-test-card\n * [item]=\"myItem\"\n * [showActions]=\"true\"\n * (itemClick)=\"onItemClick($event)\"\n * (deleteClick)=\"onDelete($event)\"\n * />\n * ```\n */\n@Component({\n selector: 'maki-test-card',\n standalone: true,\n imports: [\n IonCard,\n IonCardHeader,\n IonCardTitle,\n IonCardSubtitle,\n IonCardContent,\n IonButton,\n IonIcon\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <ion-card #cardElement [class.inactive]=\"!item().isActive\" (click)=\"onCardClick()\">\n <ion-card-header>\n <ion-card-title>{{ item().title }}</ion-card-title>\n @if (formattedDate()) {\n <ion-card-subtitle>\n {{ formattedDate() }}\n @if (!item().isActive) {\n <span class=\"status-badge inactive\">Inactive</span>\n }\n </ion-card-subtitle>\n }\n </ion-card-header>\n\n @if (item().description) {\n <ion-card-content>\n <p>{{ item().description }}</p>\n </ion-card-content>\n }\n\n @if (showActions()) {\n <ion-card-content class=\"card-actions\">\n <ion-button fill=\"outline\" size=\"small\" (click)=\"onEditClick($event)\">\n <ion-icon slot=\"start\" name=\"create-outline\"></ion-icon>\n Edit\n </ion-button>\n <ion-button fill=\"outline\" size=\"small\" color=\"danger\" (click)=\"onDeleteClick($event)\">\n <ion-icon slot=\"start\" name=\"trash-outline\"></ion-icon>\n Delete\n </ion-button>\n </ion-card-content>\n }\n </ion-card>\n `,\n styles: [\n `\n :host {\n display: block;\n }\n\n ion-card {\n --background: var(--maki-card-background, var(--ion-card-background, #fff));\n --color: var(--maki-card-color, var(--ion-text-color));\n margin: var(--maki-card-margin, 16px);\n border-radius: var(--maki-card-border-radius, 12px);\n transition:\n opacity 0.2s ease,\n transform 0.2s ease;\n cursor: pointer;\n\n &:hover {\n transform: translateY(-2px);\n }\n\n &.inactive {\n opacity: 0.6;\n }\n }\n\n ion-card-title {\n color: var(--maki-primary, var(--ion-color-primary));\n font-weight: 600;\n }\n\n ion-card-subtitle {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .status-badge {\n font-size: 0.75rem;\n padding: 2px 8px;\n border-radius: 4px;\n font-weight: 500;\n\n &.inactive {\n background-color: var(--ion-color-medium-tint);\n color: var(--ion-color-medium-contrast);\n }\n }\n\n .card-actions {\n display: flex;\n gap: 8px;\n padding-top: 0;\n\n ion-button {\n --border-color: var(--maki-primary, var(--ion-color-primary));\n --color: var(--maki-primary, var(--ion-color-primary));\n }\n }\n `\n ]\n})\nexport class TestCardComponent {\n // Signal inputs\n readonly item = input.required<TestInterface>();\n readonly showActions = input<boolean>(false);\n\n // Signal outputs\n readonly itemClick = output<TestInterface>();\n readonly editClick = output<TestInterface>();\n readonly deleteClick = output<TestInterface>();\n\n // Signal queries\n readonly cardElement = viewChild<ElementRef<HTMLIonCardElement>>('cardElement');\n\n // Computed signals\n readonly formattedDate = computed(() => {\n const date = this.item().createdAt;\n if (!date) return '';\n\n const dateObj = date instanceof Date ? date : new Date(date);\n return dateObj.toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric'\n });\n });\n\n constructor() {\n addIcons({ createOutline, trashOutline });\n }\n\n protected onCardClick(): void {\n this.itemClick.emit(this.item());\n }\n\n protected onEditClick(event: Event): void {\n event.stopPropagation();\n this.editClick.emit(this.item());\n }\n\n protected onDeleteClick(event: Event): void {\n event.stopPropagation();\n this.deleteClick.emit(this.item());\n }\n\n /**\n * Public method to focus the card element programmatically.\n */\n focus(): void {\n this.cardElement()?.nativeElement?.focus();\n }\n}\n","/**\n * @file Action Button List Component\n * @description Component for rendering a list of action buttons inside a popover\n */\n\nimport { ChangeDetectionStrategy, Component, inject, input, output, signal } from '@angular/core';\nimport { IonIcon, IonItem, IonLabel, IonList, IonSpinner, PopoverController } from '@ionic/angular/standalone';\nimport { addIcons } from 'ionicons';\n\nimport { ActionButtonType } from '../../models/action-button-type.enum';\nimport { ActionButton } from '../../models/action-button.interface';\n\n/**\n * Component that renders a list of action buttons for use in popovers/dropdowns.\n *\n * This component is designed to be used as the content of an `ion-popover`\n * created via `PopoverController`. When a button is selected, it dismisses\n * the popover and returns the selected button.\n *\n * @example\n * ```typescript\n * // Used internally by maki-button for dropdowns\n * // Can also be used standalone:\n * const popover = await popoverCtrl.create({\n * component: ActionButtonListComponent,\n * componentProps: { buttons: myButtons },\n * event: clickEvent\n * });\n * await popover.present();\n *\n * const { data } = await popover.onDidDismiss();\n * if (data) {\n * data.handler();\n * }\n * ```\n *\n * @usageNotes\n * ### Inputs\n * - `buttons` (required): Array of `ActionButton` objects to display\n *\n * ### Outputs\n * - `buttonSelect`: Emits the selected `ActionButton` when clicked\n */\n@Component({\n selector: 'maki-action-button-list',\n standalone: true,\n imports: [IonList, IonItem, IonIcon, IonLabel, IonSpinner],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <ion-list lines=\"none\">\n @for (button of buttonList(); track button.id) {\n <ion-item\n [button]=\"true\"\n [disabled]=\"button.loading || button.config?.disabled\"\n [detail]=\"button.type === dropdownType\"\n (click)=\"onButtonClick(button)\"\n >\n @if (button.loading) {\n <ion-spinner slot=\"start\" name=\"crescent\" />\n } @else if (button.icon) {\n <ion-icon slot=\"start\" [name]=\"button.icon\" />\n }\n @if (button.label) {\n <ion-label>{{ button.label }}</ion-label>\n }\n </ion-item>\n } @empty {\n <ion-item>\n <ion-label color=\"medium\">No actions available</ion-label>\n </ion-item>\n }\n </ion-list>\n `,\n styles: [\n `\n :host {\n display: block;\n }\n\n ion-list {\n padding: 0;\n }\n\n ion-item {\n --padding-start: var(--maki-action-button-list-padding, 16px);\n --padding-end: var(--maki-action-button-list-padding, 16px);\n --min-height: var(--maki-action-button-list-item-height, 44px);\n cursor: pointer;\n width: fit-content;\n }\n\n ion-label {\n white-space: nowrap;\n }\n\n ion-icon {\n font-size: var(--maki-action-button-list-icon-size, 20px);\n margin-inline-end: var(--maki-action-button-list-icon-gap, 12px);\n }\n\n ion-spinner {\n width: var(--maki-action-button-list-icon-size, 20px);\n height: var(--maki-action-button-list-icon-size, 20px);\n margin-inline-end: var(--maki-action-button-list-icon-gap, 12px);\n }\n `,\n ],\n})\nexport class ActionButtonListComponent {\n /**\n * Reference to ActionButtonType.Dropdown for template comparison.\n * @internal\n */\n protected readonly dropdownType = ActionButtonType.Dropdown;\n\n /**\n * Popover controller for dismissing the popover when an item is selected.\n */\n private readonly popoverCtrl = inject(PopoverController, { optional: true });\n\n /**\n * Internal signal to store buttons passed via componentProps.\n * PopoverController.create() sets properties directly, bypassing signal inputs.\n */\n private readonly _buttonsFromProps = signal<readonly ActionButton[]>([]);\n\n /**\n * The list of action buttons to display (when used via template binding).\n */\n readonly buttons = input<readonly ActionButton[]>([]);\n\n /**\n * Computed button list that works with both signal input and componentProps.\n * PopoverController passes buttons as a direct property, not through Angular's input system.\n */\n protected readonly buttonList = (): readonly ActionButton[] => {\n const fromProps = this._buttonsFromProps();\n const fromInput = this.buttons();\n return fromProps.length > 0 ? fromProps : fromInput;\n };\n\n /**\n * Setter to capture buttons passed via PopoverController.create({ componentProps }).\n * This is called when Ionic sets the property directly on the component instance.\n */\n set buttonsFromPopover(value: readonly ActionButton[]) {\n this._buttonsFromProps.set(value);\n }\n\n /**\n * Emits when a button is selected from the list.\n */\n readonly buttonSelect = output<ActionButton>();\n\n constructor() {\n // Register any icons that might be used\n addIcons({});\n }\n\n /**\n * Handles click on a button item.\n * Emits the selected button and dismisses the popover.\n *\n * @param button - The clicked action button\n */\n protected onButtonClick(button: ActionButton): void {\n if (button.loading || button.config?.disabled) {\n return;\n }\n\n this.buttonSelect.emit(button);\n this.popoverCtrl?.dismiss(button, 'select');\n }\n}\n","/**\n * @file Button Component\n * @description Configurable button component that renders ion-button based on ActionButton configuration\n */\n\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n input,\n output,\n signal\n} from '@angular/core';\nimport { IonButton, IonIcon, IonSpinner, PopoverController } from '@ionic/angular/standalone';\nimport { addIcons } from 'ionicons';\nimport { chevronDownOutline } from 'ionicons/icons';\n\nimport { ActionButtonType } from '../../models/action-button-type.enum';\nimport { ActionButton } from '../../models/action-button.interface';\nimport { ActionButtonListComponent } from '../action-button-list/action-button-list.component';\n\n/**\n * A configurable button component that renders an `ion-button` based on\n * an `ActionButton` configuration object.\n *\n * Features:\n * - Three display types: Default (label + icon), IconOnly, and Dropdown\n * - Automatic loading state management for async handlers\n * - Dropdown support via PopoverController with child actions\n * - Full Ionic button styling configuration (fill, size, color, shape, expand)\n * - Automatic chevron icon for dropdown buttons\n *\n * @example\n * ```html\n * <!-- Simple button -->\n * <maki-button [button]=\"saveButton\" />\n *\n * <!-- Icon-only button -->\n * <maki-button [button]=\"iconButton\" />\n *\n * <!-- Dropdown button -->\n * <maki-button [button]=\"menuButton\" />\n * ```\n *\n * @example\n * ```typescript\n * // In your component\n * saveButton: ActionButton = {\n * id: 'save',\n * label: 'Save',\n * icon: 'saveOutline',\n * type: ActionButtonType.Default,\n * config: { fill: 'solid', color: 'primary' },\n * handler: async () => {\n * await this.saveData();\n * }\n * };\n *\n * menuButton: ActionButton = {\n * id: 'menu',\n * label: 'Actions',\n * type: ActionButtonType.Dropdown,\n * handler: () => {},\n * children: [\n * { id: 'edit', label: 'Edit', icon: 'createOutline', type: ActionButtonType.Default, handler: () => this.edit() },\n * { id: 'delete', label: 'Delete', icon: 'trashOutline', type: ActionButtonType.Default, handler: () => this.delete() }\n * ]\n * };\n * ```\n *\n * @usageNotes\n * ### Inputs\n * - `button` (required): The `ActionButton` configuration object\n *\n * ### Outputs\n * - `buttonClick`: Emits the button configuration when clicked (for non-dropdown buttons)\n * - `childSelect`: Emits the selected child button (for dropdown buttons)\n *\n * ### Loading State\n * The component automatically manages loading state for async handlers.\n * When a handler returns a Promise, the button shows a spinner until it resolves/rejects.\n * You can also manually control loading via the `button.loading` property.\n *\n * ### Dropdown Behavior\n * For `ActionButtonType.Dropdown`, clicking the button opens an `ion-popover`\n * containing an `ActionButtonListComponent` with the child buttons.\n * When a child is selected, its handler is executed and `childSelect` is emitted.\n */\n@Component({\n selector: 'maki-button',\n standalone: true,\n imports: [IonButton, IonIcon, IonSpinner],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <ion-button\n [fill]=\"button().config?.fill\"\n [size]=\"button().config?.size\"\n [color]=\"button().config?.color\"\n [shape]=\"button().config?.shape\"\n [expand]=\"button().config?.expand\"\n [strong]=\"button().config?.strong\"\n [disabled]=\"isDisabled()\"\n [attr.aria-label]=\"button().ariaLabel\"\n [title]=\"button().tooltip ?? ''\"\n (click)=\"onClick($event)\"\n >\n @if (isLoading()) {\n <ion-spinner name=\"crescent\" />\n } @else {\n @if (button().icon) {\n <ion-icon [name]=\"button().icon\" [slot]=\"iconSlot()\" />\n }\n @if (showLabel()) {\n {{ button().label }}\n }\n @if (showDropdownIcon()) {\n <ion-icon name=\"chevron-down-outline\" slot=\"end\" class=\"dropdown-icon\" />\n }\n }\n </ion-button>\n `,\n styles: [\n `\n :host {\n display: inline-block;\n }\n\n ion-button {\n --padding-start: var(--maki-button-padding-start);\n --padding-end: var(--maki-button-padding-end);\n }\n\n ion-spinner {\n width: var(--maki-button-spinner-size, 20px);\n height: var(--maki-button-spinner-size, 20px);\n }\n\n .dropdown-icon {\n font-size: var(--maki-button-dropdown-icon-size, 16px);\n margin-inline-start: var(--maki-button-dropdown-icon-gap, 4px);\n }\n `\n ]\n})\nexport class ButtonComponent {\n /**\n * Popover controller for creating dropdown popovers.\n */\n private readonly popoverCtrl = inject(PopoverController);\n\n /**\n * Internal loading state for async handler execution.\n */\n private readonly _isLoading = signal<boolean>(false);\n\n /**\n * The action button configuration.\n */\n readonly button = input.required<ActionButton>();\n\n /**\n * Emits when the button is clicked (for non-dropdown buttons).\n * Emits the button configuration that was clicked.\n */\n readonly buttonClick = output<ActionButton>();\n\n /**\n * Emits when a child button is selected from a dropdown.\n * Only emits for `ActionButtonType.Dropdown` buttons.\n */\n readonly childSelect = output<ActionButton>();\n\n /**\n * Combined loading state from external prop and internal async state.\n */\n readonly isLoading = computed(() => this._isLoading() || (this.button().loading ?? false));\n\n /**\n * Whether the button is disabled (from config or loading state).\n */\n readonly isDisabled = computed(\n () => this.isLoading() || (this.button().config?.disabled ?? false)\n );\n\n /**\n * Determines the slot for the icon based on button type and label presence.\n */\n readonly iconSlot = computed(() => {\n const btn = this.button();\n if (btn.type === ActionButtonType.IconOnly) {\n return 'icon-only';\n }\n return btn.label ? 'start' : 'icon-only';\n });\n\n /**\n * Whether to show the label text.\n */\n readonly showLabel = computed(() => {\n const btn = this.button();\n return btn.type !== ActionButtonType.IconOnly && btn.label;\n });\n\n /**\n * Whether to show the dropdown chevron icon.\n */\n readonly showDropdownIcon = computed(() => {\n const btn = this.button();\n return btn.type === ActionButtonType.Dropdown && !btn.config?.hideDropdownIcon;\n });\n\n constructor() {\n addIcons({ chevronDownOutline });\n }\n\n /**\n * Handles button click events.\n * For dropdown buttons, opens a popover with child actions.\n * For other buttons, executes the handler with auto-loading management.\n *\n * @param event - The click event\n */\n protected async onClick(event: Event): Promise<void> {\n const btn = this.button();\n\n if (this.isDisabled()) {\n return;\n }\n\n if (btn.type === ActionButtonType.Dropdown) {\n await this.openDropdown(event);\n } else {\n await this.executeHandler(btn);\n this.buttonClick.emit(btn);\n }\n }\n\n /**\n * Opens a dropdown popover with child action buttons.\n *\n * @param event - The triggering click event for popover positioning\n */\n private async openDropdown(event: Event): Promise<void> {\n const btn = this.button();\n const children = btn.children ?? [];\n\n if (children.length === 0) {\n return;\n }\n\n const popover = await this.popoverCtrl.create({\n component: ActionButtonListComponent,\n componentProps: {\n buttonsFromPopover: children\n },\n event,\n translucent: true,\n dismissOnSelect: true,\n side: 'bottom',\n alignment: 'end',\n arrow: false\n });\n\n await popover.present();\n\n const { data, role } = await popover.onDidDismiss<ActionButton>();\n\n if (role === 'select' && data) {\n await this.executeHandler(data);\n this.childSelect.emit(data);\n }\n }\n\n /**\n * Executes a button's handler with automatic loading state management.\n *\n * @param btn - The button whose handler to execute\n */\n private async executeHandler(btn: ActionButton): Promise<void> {\n const result = btn.handler();\n\n if (result instanceof Promise) {\n this._isLoading.set(true);\n try {\n await result;\n } finally {\n this._isLoading.set(false);\n }\n }\n }\n}\n","/*\n * Public API Surface of @makigamestudio/ui-core\n */\n\n// Models\nexport { TestClass } from './lib/models/test.class';\nexport type { TestInterface } from './lib/models/test.interface';\n\n// Action Button Models\nexport type { ActionButtonConfig } from './lib/models/action-button-config.interface';\nexport { ActionButtonType } from './lib/models/action-button-type.enum';\nexport type { ActionButton } from './lib/models/action-button.interface';\n\n// Services\nexport { TestService } from './lib/services/test.service';\n\n// Components\nexport { TestCardComponent } from './lib/components/test-card/test-card.component';\n\n// Action Button Components\nexport { ActionButtonListComponent } from './lib/components/action-button-list/action-button-list.component';\nexport { ButtonComponent } from './lib/components/button/button.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makigamestudio/ui-core",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "description": "Angular 21 + Ionic 8 component library with zoneless architecture and signal-based state management",
5
5
  "keywords": [
6
6
  "angular",
package/theme.css CHANGED
@@ -66,7 +66,8 @@
66
66
  --maki-radius-full: 9999px;
67
67
 
68
68
  /* Typography */
69
- --maki-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
69
+ --maki-font-family:
70
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
70
71
  --maki-font-size-xs: 0.75rem;
71
72
  --maki-font-size-sm: 0.875rem;
72
73
  --maki-font-size-md: 1rem;
@@ -78,6 +79,19 @@
78
79
  --maki-transition-fast: 150ms ease;
79
80
  --maki-transition-normal: 250ms ease;
80
81
  --maki-transition-slow: 350ms ease;
82
+
83
+ /* Button Component (maki-button) */
84
+ --maki-button-padding-start: 8px;
85
+ --maki-button-padding-end: 8px;
86
+ --maki-button-spinner-size: 20px;
87
+ --maki-button-dropdown-icon-size: 16px;
88
+ --maki-button-dropdown-icon-gap: 4px;
89
+
90
+ /* Action Button List Component (maki-action-button-list) */
91
+ --maki-action-button-list-padding: 16px;
92
+ --maki-action-button-list-item-height: 44px;
93
+ --maki-action-button-list-icon-size: 20px;
94
+ --maki-action-button-list-icon-gap: 12px;
81
95
  }
82
96
 
83
97
  /* Dark Mode Overrides */
package/theme.scss CHANGED
@@ -84,7 +84,8 @@
84
84
  // ==============================================
85
85
  // Typography
86
86
  // ==============================================
87
- --maki-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
87
+ --maki-font-family:
88
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
88
89
  --maki-font-size-xs: 0.75rem;
89
90
  --maki-font-size-sm: 0.875rem;
90
91
  --maki-font-size-md: 1rem;
@@ -98,6 +99,23 @@
98
99
  --maki-transition-fast: 150ms ease;
99
100
  --maki-transition-normal: 250ms ease;
100
101
  --maki-transition-slow: 350ms ease;
102
+
103
+ // ==============================================
104
+ // Button Component (maki-button)
105
+ // ==============================================
106
+ --maki-button-padding-start: 8px;
107
+ --maki-button-padding-end: 8px;
108
+ --maki-button-spinner-size: 20px;
109
+ --maki-button-dropdown-icon-size: 16px;
110
+ --maki-button-dropdown-icon-gap: 4px;
111
+
112
+ // ==============================================
113
+ // Action Button List Component (maki-action-button-list)
114
+ // ==============================================
115
+ --maki-action-button-list-padding: 16px;
116
+ --maki-action-button-list-item-height: 44px;
117
+ --maki-action-button-list-icon-size: 20px;
118
+ --maki-action-button-list-icon-gap: 12px;
101
119
  }
102
120
 
103
121
  // ==============================================
@@ -120,40 +138,3 @@
120
138
  --maki-card-box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
121
139
  }
122
140
  }
123
-
124
- // ==============================================
125
- // Utility Mixins (for SCSS consumers)
126
- // ==============================================
127
- @mixin maki-flex-center {
128
- display: flex;
129
- align-items: center;
130
- justify-content: center;
131
- }
132
-
133
- @mixin maki-flex-between {
134
- display: flex;
135
- align-items: center;
136
- justify-content: space-between;
137
- }
138
-
139
- @mixin maki-card-hover {
140
- transition: transform var(--maki-transition-fast), box-shadow var(--maki-transition-fast);
141
-
142
- &:hover {
143
- transform: translateY(-2px);
144
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
145
- }
146
- }
147
-
148
- @mixin maki-truncate($lines: 1) {
149
- @if $lines == 1 {
150
- white-space: nowrap;
151
- overflow: hidden;
152
- text-overflow: ellipsis;
153
- } @else {
154
- display: -webkit-box;
155
- -webkit-line-clamp: $lines;
156
- -webkit-box-orient: vertical;
157
- overflow: hidden;
158
- }
159
- }
@@ -50,6 +50,210 @@ declare class TestClass implements TestInterface {
50
50
  toJSON(): TestInterface;
51
51
  }
52
52
 
53
+ /**
54
+ * @file Action button configuration interface
55
+ * @description Configuration options for styling and behavior of action buttons
56
+ */
57
+ /**
58
+ * Configuration interface for action button styling and behavior.
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * const config: ActionButtonConfig = {
63
+ * fill: 'outline',
64
+ * size: 'small',
65
+ * color: 'primary',
66
+ * shape: 'round'
67
+ * };
68
+ * ```
69
+ */
70
+ interface ActionButtonConfig {
71
+ /**
72
+ * The fill style of the button.
73
+ * - `'clear'`: Transparent background
74
+ * - `'outline'`: Transparent background with border
75
+ * - `'solid'`: Filled background (default Ionic behavior)
76
+ * - `'default'`: Uses Ionic default
77
+ */
78
+ readonly fill?: 'clear' | 'outline' | 'solid' | 'default';
79
+ /**
80
+ * The size of the button.
81
+ * - `'small'`: Smaller button
82
+ * - `'default'`: Standard size
83
+ * - `'large'`: Larger button
84
+ */
85
+ readonly size?: 'small' | 'default' | 'large';
86
+ /**
87
+ * The color of the button. Can be an Ionic color name
88
+ * (primary, secondary, tertiary, success, warning, danger, light, medium, dark)
89
+ * or a custom color defined in your theme.
90
+ */
91
+ readonly color?: string;
92
+ /**
93
+ * The shape of the button.
94
+ * - `'round'`: Fully rounded corners
95
+ */
96
+ readonly shape?: 'round';
97
+ /**
98
+ * How the button should expand to fill its container.
99
+ * - `'block'`: Full width with rounded corners
100
+ * - `'full'`: Full width with square corners
101
+ */
102
+ readonly expand?: 'block' | 'full';
103
+ /**
104
+ * Whether the button is disabled.
105
+ */
106
+ readonly disabled?: boolean;
107
+ /**
108
+ * Whether to use a stronger font weight.
109
+ */
110
+ readonly strong?: boolean;
111
+ /**
112
+ * Whether to hide the dropdown chevron icon for dropdown type buttons.
113
+ * Only applies when `type` is `ActionButtonType.Dropdown`.
114
+ * @default false
115
+ */
116
+ readonly hideDropdownIcon?: boolean;
117
+ }
118
+
119
+ /**
120
+ * @file Action button type enumeration
121
+ * @description Defines the display types for action buttons
122
+ */
123
+ /**
124
+ * Enumeration of action button display types.
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * const button: ActionButton = {
129
+ * id: 'save-btn',
130
+ * label: 'Save',
131
+ * type: ActionButtonType.Default,
132
+ * handler: () => console.log('Saved!')
133
+ * };
134
+ * ```
135
+ */
136
+ declare enum ActionButtonType {
137
+ /**
138
+ * Standard button with label and optional icon.
139
+ */
140
+ Default = "default",
141
+ /**
142
+ * Button displaying only an icon, no label.
143
+ */
144
+ IconOnly = "icon-only",
145
+ /**
146
+ * Button that opens a dropdown popover with child actions.
147
+ */
148
+ Dropdown = "dropdown"
149
+ }
150
+
151
+ /**
152
+ * @file Action button interface
153
+ * @description Main interface for configuring action buttons
154
+ */
155
+
156
+ /**
157
+ * Interface representing an action button configuration.
158
+ *
159
+ * @typeParam T - The type of data passed to the handler function. Defaults to `void`.
160
+ *
161
+ * @example
162
+ * ```typescript
163
+ * // Simple button
164
+ * const saveButton: ActionButton = {
165
+ * id: 'save',
166
+ * label: 'Save',
167
+ * icon: 'saveOutline',
168
+ * type: ActionButtonType.Default,
169
+ * handler: () => console.log('Saved!')
170
+ * };
171
+ *
172
+ * // Async button with auto-loading
173
+ * const submitButton: ActionButton = {
174
+ * id: 'submit',
175
+ * label: 'Submit',
176
+ * type: ActionButtonType.Default,
177
+ * handler: async () => {
178
+ * await api.submit();
179
+ * }
180
+ * };
181
+ *
182
+ * // Dropdown button with children
183
+ * const menuButton: ActionButton = {
184
+ * id: 'menu',
185
+ * label: 'Actions',
186
+ * type: ActionButtonType.Dropdown,
187
+ * handler: () => {}, // Not called for dropdowns
188
+ * children: [
189
+ * { id: 'edit', label: 'Edit', type: ActionButtonType.Default, handler: () => edit() },
190
+ * { id: 'delete', label: 'Delete', type: ActionButtonType.Default, handler: () => delete() }
191
+ * ]
192
+ * };
193
+ *
194
+ * // Button with generic data parameter
195
+ * const itemButton: ActionButton<Item> = {
196
+ * id: 'select',
197
+ * label: 'Select',
198
+ * type: ActionButtonType.Default,
199
+ * handler: (item) => selectItem(item)
200
+ * };
201
+ * ```
202
+ */
203
+ interface ActionButton<T = void> {
204
+ /**
205
+ * Unique identifier for the button.
206
+ */
207
+ readonly id: string;
208
+ /**
209
+ * Icon name to display in the button.
210
+ * Should be an Ionicon name registered with `addIcons()`.
211
+ */
212
+ readonly icon?: string;
213
+ /**
214
+ * Text label to display in the button.
215
+ * Required for `Default` type, optional for `IconOnly` and `Dropdown`.
216
+ */
217
+ readonly label?: string;
218
+ /**
219
+ * Handler function called when the button is clicked.
220
+ * Can be synchronous or asynchronous. For async handlers,
221
+ * the component will automatically manage the loading state.
222
+ *
223
+ * @param data - Optional data parameter of type T
224
+ * @returns void or Promise<void>
225
+ */
226
+ readonly handler: (data?: T) => void | Promise<void>;
227
+ /**
228
+ * External loading state. When true, the button shows a loading spinner.
229
+ * The component also manages an internal loading state for async handlers.
230
+ * The button shows loading if either this or the internal state is true.
231
+ */
232
+ readonly loading?: boolean;
233
+ /**
234
+ * Child action buttons for dropdown menus.
235
+ * Only used when `type` is `ActionButtonType.Dropdown`.
236
+ */
237
+ readonly children?: readonly ActionButton<T>[];
238
+ /**
239
+ * The display type of the button.
240
+ */
241
+ readonly type: ActionButtonType;
242
+ /**
243
+ * Optional configuration for button styling.
244
+ */
245
+ readonly config?: ActionButtonConfig;
246
+ /**
247
+ * Accessible label for screen readers.
248
+ * Recommended for `IconOnly` buttons.
249
+ */
250
+ readonly ariaLabel?: string;
251
+ /**
252
+ * Tooltip text shown on hover.
253
+ */
254
+ readonly tooltip?: string;
255
+ }
256
+
53
257
  /**
54
258
  * TestService - Example service using Angular Signals for reactive state management.
55
259
  * Provided in root, demonstrating singleton service pattern with signal-based state.
@@ -140,5 +344,216 @@ declare class TestCardComponent {
140
344
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<TestCardComponent, "maki-test-card", never, { "item": { "alias": "item"; "required": true; "isSignal": true; }; "showActions": { "alias": "showActions"; "required": false; "isSignal": true; }; }, { "itemClick": "itemClick"; "editClick": "editClick"; "deleteClick": "deleteClick"; }, never, never, true, never>;
141
345
  }
142
346
 
143
- export { TestCardComponent, TestClass, TestService };
144
- export type { TestInterface };
347
+ /**
348
+ * Component that renders a list of action buttons for use in popovers/dropdowns.
349
+ *
350
+ * This component is designed to be used as the content of an `ion-popover`
351
+ * created via `PopoverController`. When a button is selected, it dismisses
352
+ * the popover and returns the selected button.
353
+ *
354
+ * @example
355
+ * ```typescript
356
+ * // Used internally by maki-button for dropdowns
357
+ * // Can also be used standalone:
358
+ * const popover = await popoverCtrl.create({
359
+ * component: ActionButtonListComponent,
360
+ * componentProps: { buttons: myButtons },
361
+ * event: clickEvent
362
+ * });
363
+ * await popover.present();
364
+ *
365
+ * const { data } = await popover.onDidDismiss();
366
+ * if (data) {
367
+ * data.handler();
368
+ * }
369
+ * ```
370
+ *
371
+ * @usageNotes
372
+ * ### Inputs
373
+ * - `buttons` (required): Array of `ActionButton` objects to display
374
+ *
375
+ * ### Outputs
376
+ * - `buttonSelect`: Emits the selected `ActionButton` when clicked
377
+ */
378
+ declare class ActionButtonListComponent {
379
+ /**
380
+ * Reference to ActionButtonType.Dropdown for template comparison.
381
+ * @internal
382
+ */
383
+ protected readonly dropdownType = ActionButtonType.Dropdown;
384
+ /**
385
+ * Popover controller for dismissing the popover when an item is selected.
386
+ */
387
+ private readonly popoverCtrl;
388
+ /**
389
+ * Internal signal to store buttons passed via componentProps.
390
+ * PopoverController.create() sets properties directly, bypassing signal inputs.
391
+ */
392
+ private readonly _buttonsFromProps;
393
+ /**
394
+ * The list of action buttons to display (when used via template binding).
395
+ */
396
+ readonly buttons: _angular_core.InputSignal<readonly ActionButton<void>[]>;
397
+ /**
398
+ * Computed button list that works with both signal input and componentProps.
399
+ * PopoverController passes buttons as a direct property, not through Angular's input system.
400
+ */
401
+ protected readonly buttonList: () => readonly ActionButton[];
402
+ /**
403
+ * Setter to capture buttons passed via PopoverController.create({ componentProps }).
404
+ * This is called when Ionic sets the property directly on the component instance.
405
+ */
406
+ set buttonsFromPopover(value: readonly ActionButton[]);
407
+ /**
408
+ * Emits when a button is selected from the list.
409
+ */
410
+ readonly buttonSelect: _angular_core.OutputEmitterRef<ActionButton<void>>;
411
+ constructor();
412
+ /**
413
+ * Handles click on a button item.
414
+ * Emits the selected button and dismisses the popover.
415
+ *
416
+ * @param button - The clicked action button
417
+ */
418
+ protected onButtonClick(button: ActionButton): void;
419
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ActionButtonListComponent, never>;
420
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ActionButtonListComponent, "maki-action-button-list", never, { "buttons": { "alias": "buttons"; "required": false; "isSignal": true; }; }, { "buttonSelect": "buttonSelect"; }, never, never, true, never>;
421
+ }
422
+
423
+ /**
424
+ * A configurable button component that renders an `ion-button` based on
425
+ * an `ActionButton` configuration object.
426
+ *
427
+ * Features:
428
+ * - Three display types: Default (label + icon), IconOnly, and Dropdown
429
+ * - Automatic loading state management for async handlers
430
+ * - Dropdown support via PopoverController with child actions
431
+ * - Full Ionic button styling configuration (fill, size, color, shape, expand)
432
+ * - Automatic chevron icon for dropdown buttons
433
+ *
434
+ * @example
435
+ * ```html
436
+ * <!-- Simple button -->
437
+ * <maki-button [button]="saveButton" />
438
+ *
439
+ * <!-- Icon-only button -->
440
+ * <maki-button [button]="iconButton" />
441
+ *
442
+ * <!-- Dropdown button -->
443
+ * <maki-button [button]="menuButton" />
444
+ * ```
445
+ *
446
+ * @example
447
+ * ```typescript
448
+ * // In your component
449
+ * saveButton: ActionButton = {
450
+ * id: 'save',
451
+ * label: 'Save',
452
+ * icon: 'saveOutline',
453
+ * type: ActionButtonType.Default,
454
+ * config: { fill: 'solid', color: 'primary' },
455
+ * handler: async () => {
456
+ * await this.saveData();
457
+ * }
458
+ * };
459
+ *
460
+ * menuButton: ActionButton = {
461
+ * id: 'menu',
462
+ * label: 'Actions',
463
+ * type: ActionButtonType.Dropdown,
464
+ * handler: () => {},
465
+ * children: [
466
+ * { id: 'edit', label: 'Edit', icon: 'createOutline', type: ActionButtonType.Default, handler: () => this.edit() },
467
+ * { id: 'delete', label: 'Delete', icon: 'trashOutline', type: ActionButtonType.Default, handler: () => this.delete() }
468
+ * ]
469
+ * };
470
+ * ```
471
+ *
472
+ * @usageNotes
473
+ * ### Inputs
474
+ * - `button` (required): The `ActionButton` configuration object
475
+ *
476
+ * ### Outputs
477
+ * - `buttonClick`: Emits the button configuration when clicked (for non-dropdown buttons)
478
+ * - `childSelect`: Emits the selected child button (for dropdown buttons)
479
+ *
480
+ * ### Loading State
481
+ * The component automatically manages loading state for async handlers.
482
+ * When a handler returns a Promise, the button shows a spinner until it resolves/rejects.
483
+ * You can also manually control loading via the `button.loading` property.
484
+ *
485
+ * ### Dropdown Behavior
486
+ * For `ActionButtonType.Dropdown`, clicking the button opens an `ion-popover`
487
+ * containing an `ActionButtonListComponent` with the child buttons.
488
+ * When a child is selected, its handler is executed and `childSelect` is emitted.
489
+ */
490
+ declare class ButtonComponent {
491
+ /**
492
+ * Popover controller for creating dropdown popovers.
493
+ */
494
+ private readonly popoverCtrl;
495
+ /**
496
+ * Internal loading state for async handler execution.
497
+ */
498
+ private readonly _isLoading;
499
+ /**
500
+ * The action button configuration.
501
+ */
502
+ readonly button: _angular_core.InputSignal<ActionButton<void>>;
503
+ /**
504
+ * Emits when the button is clicked (for non-dropdown buttons).
505
+ * Emits the button configuration that was clicked.
506
+ */
507
+ readonly buttonClick: _angular_core.OutputEmitterRef<ActionButton<void>>;
508
+ /**
509
+ * Emits when a child button is selected from a dropdown.
510
+ * Only emits for `ActionButtonType.Dropdown` buttons.
511
+ */
512
+ readonly childSelect: _angular_core.OutputEmitterRef<ActionButton<void>>;
513
+ /**
514
+ * Combined loading state from external prop and internal async state.
515
+ */
516
+ readonly isLoading: _angular_core.Signal<boolean>;
517
+ /**
518
+ * Whether the button is disabled (from config or loading state).
519
+ */
520
+ readonly isDisabled: _angular_core.Signal<boolean>;
521
+ /**
522
+ * Determines the slot for the icon based on button type and label presence.
523
+ */
524
+ readonly iconSlot: _angular_core.Signal<"icon-only" | "start">;
525
+ /**
526
+ * Whether to show the label text.
527
+ */
528
+ readonly showLabel: _angular_core.Signal<string | false | undefined>;
529
+ /**
530
+ * Whether to show the dropdown chevron icon.
531
+ */
532
+ readonly showDropdownIcon: _angular_core.Signal<boolean>;
533
+ constructor();
534
+ /**
535
+ * Handles button click events.
536
+ * For dropdown buttons, opens a popover with child actions.
537
+ * For other buttons, executes the handler with auto-loading management.
538
+ *
539
+ * @param event - The click event
540
+ */
541
+ protected onClick(event: Event): Promise<void>;
542
+ /**
543
+ * Opens a dropdown popover with child action buttons.
544
+ *
545
+ * @param event - The triggering click event for popover positioning
546
+ */
547
+ private openDropdown;
548
+ /**
549
+ * Executes a button's handler with automatic loading state management.
550
+ *
551
+ * @param btn - The button whose handler to execute
552
+ */
553
+ private executeHandler;
554
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ButtonComponent, never>;
555
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ButtonComponent, "maki-button", never, { "button": { "alias": "button"; "required": true; "isSignal": true; }; }, { "buttonClick": "buttonClick"; "childSelect": "childSelect"; }, never, never, true, never>;
556
+ }
557
+
558
+ export { ActionButtonListComponent, ActionButtonType, ButtonComponent, TestCardComponent, TestClass, TestService };
559
+ export type { ActionButton, ActionButtonConfig, TestInterface };