@makigamestudio/ui-ionic 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # UiIonic
2
+
3
+ This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.1.0.
4
+
5
+ ## Code scaffolding
6
+
7
+ Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
8
+
9
+ ```bash
10
+ ng generate component component-name
11
+ ```
12
+
13
+ For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
14
+
15
+ ```bash
16
+ ng generate --help
17
+ ```
18
+
19
+ ## Building
20
+
21
+ To build the library, run:
22
+
23
+ ```bash
24
+ ng build ui-ionic
25
+ ```
26
+
27
+ This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
28
+
29
+ ### Publishing the Library
30
+
31
+ Once the project is built, you can publish your library by following these steps:
32
+
33
+ 1. Navigate to the `dist` directory:
34
+ ```bash
35
+ cd dist/ui-ionic
36
+ ```
37
+
38
+ 2. Run the `npm publish` command to publish your library to the npm registry:
39
+ ```bash
40
+ npm publish
41
+ ```
42
+
43
+ ## Running unit tests
44
+
45
+ To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
46
+
47
+ ```bash
48
+ ng test
49
+ ```
50
+
51
+ ## Running end-to-end tests
52
+
53
+ For end-to-end (e2e) testing, run:
54
+
55
+ ```bash
56
+ ng e2e
57
+ ```
58
+
59
+ Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
60
+
61
+ ## Additional Resources
62
+
63
+ For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
@@ -0,0 +1,430 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, signal, input, output, computed, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { PopoverController, IonList, IonItem, IonIcon, IonLabel, IonSpinner, IonButton } from '@ionic/angular/standalone';
4
+ import { ActionButtonType, ActionButtonListService, ButtonStateService, ButtonDisplayService, ButtonHandlerService } from '@makigamestudio/ui-core';
5
+ import { addIcons } from 'ionicons';
6
+ import { chevronDownOutline } from 'ionicons/icons';
7
+
8
+ /**
9
+ * @file Action Button List Component
10
+ * @description Ionic implementation of action button list for popovers/dropdowns.
11
+ *
12
+ * This component is the Ionic-specific implementation that uses the shared
13
+ * ActionButtonListService from @makigamestudio/ui-core for all business logic.
14
+ * The component only handles:
15
+ * - Ionic template rendering (ion-list, ion-item, ion-icon, ion-spinner)
16
+ * - Ionic-specific popover dismissal via PopoverController
17
+ *
18
+ * All list logic, loading state checks, and button resolution is delegated
19
+ * to the injected service, making the logic reusable for other UI libraries.
20
+ */
21
+ /**
22
+ * Component that renders a list of action buttons for use in popovers/dropdowns.
23
+ *
24
+ * This component is designed to be used as the content of an `ion-popover`
25
+ * created via `PopoverController`. When a button is selected, it dismisses
26
+ * the popover and returns the selected button.
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * // Used internally by maki-button for dropdowns
31
+ * // Can also be used standalone:
32
+ * const popover = await popoverCtrl.create({
33
+ * component: ActionButtonListComponent,
34
+ * componentProps: { buttons: myButtons },
35
+ * event: clickEvent
36
+ * });
37
+ * await popover.present();
38
+ *
39
+ * const { data } = await popover.onDidDismiss();
40
+ * if (data) {
41
+ * data.handler();
42
+ * }
43
+ * ```
44
+ *
45
+ * @usageNotes
46
+ * ### Inputs
47
+ * - `buttons` (required): Array of `ActionButton` objects to display
48
+ *
49
+ * ### Outputs
50
+ * - `buttonSelect`: Emits the selected `ActionButton` when clicked
51
+ */
52
+ class ActionButtonListComponent {
53
+ /** Reference to ActionButtonType.Dropdown for template comparison. */
54
+ ActionButtonType = ActionButtonType;
55
+ /** Ionic PopoverController for dismissing the popover when an item is selected. */
56
+ popoverCtrl = inject(PopoverController, { optional: true });
57
+ /** Service for list logic. */
58
+ listService = inject(ActionButtonListService);
59
+ /** Internal signal to store buttons passed via componentProps. */
60
+ _buttonsFromProps = signal([], ...(ngDevMode ? [{ debugName: "_buttonsFromProps" }] : []));
61
+ /** Internal signal to store the Set of currently loading child button IDs. */
62
+ _loadingChildIdsFromProps = signal(null, ...(ngDevMode ? [{ debugName: "_loadingChildIdsFromProps" }] : []));
63
+ /** The list of action buttons to display (when used via template binding). */
64
+ buttons = input([], ...(ngDevMode ? [{ debugName: "buttons" }] : []));
65
+ /** Emits when a button is selected from the list. */
66
+ buttonSelect = output();
67
+ /** Computed button list that works with both signal input and componentProps. */
68
+ buttonList = computed(() => this.listService.resolveButtonList(this._buttonsFromProps(), this.buttons()), ...(ngDevMode ? [{ debugName: "buttonList" }] : []));
69
+ /**
70
+ * Setter to capture buttons passed via PopoverController.create({ componentProps }).
71
+ * This is called when Ionic sets the property directly on the component instance.
72
+ */
73
+ set buttonsFromPopover(value) {
74
+ this._buttonsFromProps.set(value);
75
+ }
76
+ /**
77
+ * Setter to capture loading child IDs signal passed via PopoverController.create({ componentProps }).
78
+ */
79
+ set loadingChildIds(value) {
80
+ this._loadingChildIdsFromProps.set(value);
81
+ }
82
+ /** Checks if a button can be selected. */
83
+ canSelectButton(button) {
84
+ const loadingChildIds = this._loadingChildIdsFromProps()?.() ?? new Set();
85
+ return this.listService.canSelectButton(button, loadingChildIds);
86
+ }
87
+ /** Checks if a button should show its loading spinner. */
88
+ shouldShowSpinner(button) {
89
+ const loadingChildIds = this._loadingChildIdsFromProps()?.() ?? new Set();
90
+ return this.listService.shouldShowSpinner(button, loadingChildIds);
91
+ }
92
+ /**
93
+ * Handles click on a button item.
94
+ * Emits the selected button and dismisses the popover.
95
+ *
96
+ * @param button - The clicked action button
97
+ */
98
+ onButtonClick(button) {
99
+ const loadingChildIds = this._loadingChildIdsFromProps()?.() ?? new Set();
100
+ if (!this.listService.canSelectButton(button, loadingChildIds)) {
101
+ return;
102
+ }
103
+ this.buttonSelect.emit(button);
104
+ this.popoverCtrl?.dismiss(button, 'select');
105
+ }
106
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ActionButtonListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
107
+ 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" }, providers: [ActionButtonListService], ngImport: i0, template: `
108
+ <ion-list lines="none">
109
+ @for (button of buttonList(); track button.id) {
110
+ <ion-item
111
+ [button]="true"
112
+ [disabled]="!canSelectButton(button)"
113
+ [detail]="button.type === ActionButtonType.Dropdown"
114
+ (click)="onButtonClick(button)"
115
+ >
116
+ @if (shouldShowSpinner(button)) {
117
+ <ion-spinner slot="start" name="crescent" />
118
+ } @else if (button.icon) {
119
+ <ion-icon slot="start" [color]="button.config?.color" [name]="button.icon" />
120
+ }
121
+ @if (button.label) {
122
+ <ion-label [color]="button.config?.color">{{ button.label }}</ion-label>
123
+ }
124
+ </ion-item>
125
+ } @empty {
126
+ <ion-item>
127
+ <ion-label color="medium">No actions available</ion-label>
128
+ </ion-item>
129
+ }
130
+ </ion-list>
131
+ `, isInline: true, styles: [":host{display:block}::ng-deep ion-popover::part(content){width:fit-content}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:100%}ion-item[disabled]{cursor:not-allowed}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 });
132
+ }
133
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ActionButtonListComponent, decorators: [{
134
+ type: Component,
135
+ args: [{ selector: 'maki-action-button-list', standalone: true, imports: [IonList, IonItem, IonIcon, IonLabel, IonSpinner], providers: [ActionButtonListService], changeDetection: ChangeDetectionStrategy.OnPush, template: `
136
+ <ion-list lines="none">
137
+ @for (button of buttonList(); track button.id) {
138
+ <ion-item
139
+ [button]="true"
140
+ [disabled]="!canSelectButton(button)"
141
+ [detail]="button.type === ActionButtonType.Dropdown"
142
+ (click)="onButtonClick(button)"
143
+ >
144
+ @if (shouldShowSpinner(button)) {
145
+ <ion-spinner slot="start" name="crescent" />
146
+ } @else if (button.icon) {
147
+ <ion-icon slot="start" [color]="button.config?.color" [name]="button.icon" />
148
+ }
149
+ @if (button.label) {
150
+ <ion-label [color]="button.config?.color">{{ button.label }}</ion-label>
151
+ }
152
+ </ion-item>
153
+ } @empty {
154
+ <ion-item>
155
+ <ion-label color="medium">No actions available</ion-label>
156
+ </ion-item>
157
+ }
158
+ </ion-list>
159
+ `, styles: [":host{display:block}::ng-deep ion-popover::part(content){width:fit-content}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:100%}ion-item[disabled]{cursor:not-allowed}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"] }]
160
+ }], propDecorators: { buttons: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttons", required: false }] }], buttonSelect: [{ type: i0.Output, args: ["buttonSelect"] }] } });
161
+
162
+ /**
163
+ * @file Button Component
164
+ * @description Ionic implementation of a configurable button component.
165
+ *
166
+ * This component is the Ionic-specific implementation that uses the shared
167
+ * button services from @makigamestudio/ui-core for all business logic.
168
+ * The component only handles:
169
+ * - Ionic template rendering (ion-button, ion-icon, ion-spinner)
170
+ * - Ionic-specific popover creation via PopoverController
171
+ *
172
+ * All state management, display logic, and handler execution is delegated
173
+ * to the injected services, making the logic reusable for other UI libraries.
174
+ */
175
+ /**
176
+ * A configurable button component that renders an `ion-button` based on
177
+ * an `ActionButton` configuration object.
178
+ *
179
+ * Features:
180
+ * - Two button types: Default and Dropdown
181
+ * - Flexible display: icon-only, label-only, or icon+label (determined by properties)
182
+ * - Automatic loading state management for async handlers
183
+ * - Dropdown support via PopoverController with child actions
184
+ * - Full Ionic button styling configuration (fill, size, color, shape, expand)
185
+ * - Automatic chevron icon for dropdown buttons
186
+ *
187
+ * @example
188
+ * ```html
189
+ * <!-- Button with label and icon -->
190
+ * <maki-button [button]="saveButton" />
191
+ *
192
+ * <!-- Icon-only button (no label) -->
193
+ * <maki-button [button]="iconButton" />
194
+ *
195
+ * <!-- Label-only button (no icon) -->
196
+ * <maki-button [button]="textButton" />
197
+ *
198
+ * <!-- Dropdown button -->
199
+ * <maki-button [button]="menuButton" />
200
+ * ```
201
+ *
202
+ * @example
203
+ * ```typescript
204
+ * // In your component
205
+ * import { ActionButton, ActionButtonType } from '@makigamestudio/ui-core';
206
+ * import { IonicActionButton } from '@makigamestudio/ui-ionic';
207
+ *
208
+ * // Generic ActionButton (loose typing)
209
+ * saveButton: ActionButton = {
210
+ * id: 'save',
211
+ * label: 'Save',
212
+ * icon: 'save-outline',
213
+ * type: ActionButtonType.Default,
214
+ * config: { fill: 'solid', color: 'primary' },
215
+ * handler: async () => {
216
+ * await this.saveData();
217
+ * }
218
+ * };
219
+ *
220
+ * // IonicActionButton (strict typing for Ionic-specific config)
221
+ * ionicButton: IonicActionButton = {
222
+ * id: 'ionic',
223
+ * label: 'Ionic Button',
224
+ * type: ActionButtonType.Default,
225
+ * handler: () => {},
226
+ * config: {
227
+ * fill: 'solid', // Type-checked: 'clear' | 'outline' | 'solid' | 'default'
228
+ * size: 'large' // Type-checked: 'small' | 'default' | 'large'
229
+ * }
230
+ * };
231
+ *
232
+ * menuButton: ActionButton = {
233
+ * id: 'menu',
234
+ * label: 'Actions',
235
+ * type: ActionButtonType.Dropdown,
236
+ * handler: () => {},
237
+ * children: [
238
+ * { id: 'edit', label: 'Edit', icon: 'create-outline', type: ActionButtonType.Default, handler: () => this.edit() },
239
+ * { id: 'delete', label: 'Delete', icon: 'trash-outline', type: ActionButtonType.Default, handler: () => this.delete() }
240
+ * ]
241
+ * };
242
+ * ```
243
+ *
244
+ * @usageNotes
245
+ * ### Inputs
246
+ * - `button` (required): The `ActionButton` configuration object
247
+ *
248
+ * ### Outputs
249
+ * - `buttonClick`: Emits the button configuration when clicked (for non-dropdown buttons)
250
+ * - `childSelect`: Emits the selected child button (for dropdown buttons)
251
+ *
252
+ * ### Loading State
253
+ * The component automatically manages loading state for async handlers.
254
+ * When a handler returns a Promise, the button shows a spinner until it resolves/rejects.
255
+ * You can also manually control loading via the `button.loading` property.
256
+ *
257
+ * ### Dropdown Behavior
258
+ * For `ActionButtonType.Dropdown`, clicking the button opens an `ion-popover`
259
+ * containing an `ActionButtonListComponent` with the child buttons.
260
+ * When a child is selected, its handler is executed and `childSelect` is emitted.
261
+ */
262
+ class ButtonComponent {
263
+ /** Ionic PopoverController for dropdown popovers. */
264
+ popoverCtrl = inject(PopoverController);
265
+ /** Service for button state management. */
266
+ stateService = inject(ButtonStateService);
267
+ /** Service for button display logic. */
268
+ displayService = inject(ButtonDisplayService);
269
+ /** Service for handler execution. */
270
+ handlerService = inject(ButtonHandlerService);
271
+ /** The action button configuration. */
272
+ button = input.required(...(ngDevMode ? [{ debugName: "button" }] : []));
273
+ /** Emits when the button is clicked (for non-dropdown buttons). */
274
+ buttonClick = output();
275
+ /** Emits when a child button is selected from a dropdown. */
276
+ childSelect = output();
277
+ /** Whether the button is in a loading state. */
278
+ isLoading = computed(() => this.stateService.isLoading(this.button()), ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
279
+ /** Whether to display the loading spinner. */
280
+ showLoadingSpinner = computed(() => this.stateService.showLoadingSpinner(this.button()), ...(ngDevMode ? [{ debugName: "showLoadingSpinner" }] : []));
281
+ /** Whether the button is disabled. */
282
+ isDisabled = computed(() => this.stateService.isDisabled(this.button()), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
283
+ /** The slot for the icon based on label presence (Ionic-specific). */
284
+ iconSlot = computed(() => (this.button().label ? 'start' : 'icon-only'), ...(ngDevMode ? [{ debugName: "iconSlot" }] : []));
285
+ /** Whether to show the label text. */
286
+ showLabel = computed(() => this.displayService.shouldShowLabel(this.button()), ...(ngDevMode ? [{ debugName: "showLabel" }] : []));
287
+ /** Whether to show the dropdown chevron icon. */
288
+ showDropdownIcon = computed(() => this.displayService.shouldShowDropdownIcon(this.button()), ...(ngDevMode ? [{ debugName: "showDropdownIcon" }] : []));
289
+ constructor() {
290
+ addIcons({ chevronDownOutline });
291
+ }
292
+ /**
293
+ * Handles button click events.
294
+ * For dropdown buttons, opens a popover with child actions.
295
+ * For other buttons, executes the handler with auto-loading management.
296
+ *
297
+ * @param event - The click event
298
+ */
299
+ async onClick(event) {
300
+ event.stopPropagation();
301
+ const btn = this.button();
302
+ if (this.isDisabled()) {
303
+ return;
304
+ }
305
+ if (btn.type === ActionButtonType.Dropdown) {
306
+ await this.openDropdown(event);
307
+ }
308
+ else {
309
+ await this.handlerService.executeHandler(btn, loading => this.stateService.setLoading(loading));
310
+ this.buttonClick.emit(btn);
311
+ }
312
+ }
313
+ /**
314
+ * Opens a dropdown popover with child action buttons.
315
+ *
316
+ * @param event - The triggering click event for popover positioning
317
+ */
318
+ async openDropdown(event) {
319
+ const btn = this.button();
320
+ const children = btn.children ?? [];
321
+ if (children.length === 0) {
322
+ return;
323
+ }
324
+ const alignment = (btn.config?.dropdownAlignment ?? 'end');
325
+ const popover = await this.popoverCtrl.create({
326
+ component: ActionButtonListComponent,
327
+ componentProps: {
328
+ buttonsFromPopover: children,
329
+ loadingChildIds: this.stateService.loadingChildIds
330
+ },
331
+ event,
332
+ translucent: true,
333
+ dismissOnSelect: true,
334
+ side: 'bottom',
335
+ alignment,
336
+ arrow: false
337
+ });
338
+ await popover.present();
339
+ const { data, role } = await popover.onDidDismiss();
340
+ if (role === 'select' && data) {
341
+ await this.handlerService.executeChildHandler(data, (childId, loading) => {
342
+ if (loading) {
343
+ this.stateService.addLoadingChild(childId);
344
+ }
345
+ else {
346
+ this.stateService.removeLoadingChild(childId);
347
+ }
348
+ });
349
+ this.childSelect.emit(data);
350
+ }
351
+ }
352
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
353
+ 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" }, providers: [ButtonStateService, ButtonDisplayService, ButtonHandlerService], ngImport: i0, template: `
354
+ <ion-button
355
+ [fill]="button().config?.fill"
356
+ [size]="button().config?.size"
357
+ [color]="button().config?.color"
358
+ [shape]="button().config?.shape"
359
+ [expand]="button().config?.expand"
360
+ [strong]="button().config?.strong"
361
+ [disabled]="isDisabled()"
362
+ [attr.aria-label]="button().ariaLabel"
363
+ [title]="button().tooltip ?? ''"
364
+ (click)="onClick($event)"
365
+ >
366
+ @if (showLoadingSpinner()) {
367
+ <ion-spinner [slot]="iconSlot()" name="crescent" />
368
+ } @else if (button().icon) {
369
+ <ion-icon [name]="button().icon" [slot]="iconSlot()" class="button-icon" />
370
+ }
371
+ @if (showLabel()) {
372
+ {{ button().label }}
373
+ }
374
+ @if (showDropdownIcon()) {
375
+ <ion-icon name="chevron-down-outline" slot="end" class="dropdown-icon" />
376
+ }
377
+ </ion-button>
378
+ `, isInline: true, styles: ["ion-button{--padding-start: var(--maki-button-padding-start);--padding-end: var(--maki-button-padding-end);text-transform:none}ion-button.button-has-icon-only::part(native){padding:0}ion-spinner{width:var(--maki-button-spinner-size, 16px);height:var(--maki-button-spinner-size, 16px);margin:0 4px 0 0}.button-icon{font-size:var(--maki-button-icon-size, 16px);margin:0 4px 0 0}.button-icon[slot=icon-only]{margin:0}.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 });
379
+ }
380
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ButtonComponent, decorators: [{
381
+ type: Component,
382
+ args: [{ selector: 'maki-button', standalone: true, imports: [IonButton, IonIcon, IonSpinner], providers: [ButtonStateService, ButtonDisplayService, ButtonHandlerService], changeDetection: ChangeDetectionStrategy.OnPush, template: `
383
+ <ion-button
384
+ [fill]="button().config?.fill"
385
+ [size]="button().config?.size"
386
+ [color]="button().config?.color"
387
+ [shape]="button().config?.shape"
388
+ [expand]="button().config?.expand"
389
+ [strong]="button().config?.strong"
390
+ [disabled]="isDisabled()"
391
+ [attr.aria-label]="button().ariaLabel"
392
+ [title]="button().tooltip ?? ''"
393
+ (click)="onClick($event)"
394
+ >
395
+ @if (showLoadingSpinner()) {
396
+ <ion-spinner [slot]="iconSlot()" name="crescent" />
397
+ } @else if (button().icon) {
398
+ <ion-icon [name]="button().icon" [slot]="iconSlot()" class="button-icon" />
399
+ }
400
+ @if (showLabel()) {
401
+ {{ button().label }}
402
+ }
403
+ @if (showDropdownIcon()) {
404
+ <ion-icon name="chevron-down-outline" slot="end" class="dropdown-icon" />
405
+ }
406
+ </ion-button>
407
+ `, styles: ["ion-button{--padding-start: var(--maki-button-padding-start);--padding-end: var(--maki-button-padding-end);text-transform:none}ion-button.button-has-icon-only::part(native){padding:0}ion-spinner{width:var(--maki-button-spinner-size, 16px);height:var(--maki-button-spinner-size, 16px);margin:0 4px 0 0}.button-icon{font-size:var(--maki-button-icon-size, 16px);margin:0 4px 0 0}.button-icon[slot=icon-only]{margin:0}.dropdown-icon{font-size:var(--maki-button-dropdown-icon-size, 16px);margin-inline-start:var(--maki-button-dropdown-icon-gap, 4px)}\n"] }]
408
+ }], 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"] }] } });
409
+
410
+ /**
411
+ * @file Components Barrel Export
412
+ * @description Exports all Ionic components from ui-ionic.
413
+ */
414
+
415
+ /*
416
+ * Public API Surface of @makigamestudio/ui-ionic
417
+ *
418
+ * This package provides Ionic-specific implementations of UI components
419
+ * using the interfaces and services from @makigamestudio/ui-core.
420
+ */
421
+ // ============================================================================
422
+ // Ionic Components
423
+ // ============================================================================
424
+
425
+ /**
426
+ * Generated bundle index. Do not edit.
427
+ */
428
+
429
+ export { ActionButtonListComponent, ButtonComponent };
430
+ //# sourceMappingURL=makigamestudio-ui-ionic.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"makigamestudio-ui-ionic.mjs","sources":["../../../projects/ui-ionic/src/lib/components/action-button-list/action-button-list.component.ts","../../../projects/ui-ionic/src/lib/components/button/button.component.ts","../../../projects/ui-ionic/src/lib/components/index.ts","../../../projects/ui-ionic/src/public-api.ts","../../../projects/ui-ionic/src/makigamestudio-ui-ionic.ts"],"sourcesContent":["/**\n * @file Action Button List Component\n * @description Ionic implementation of action button list for popovers/dropdowns.\n *\n * This component is the Ionic-specific implementation that uses the shared\n * ActionButtonListService from @makigamestudio/ui-core for all business logic.\n * The component only handles:\n * - Ionic template rendering (ion-list, ion-item, ion-icon, ion-spinner)\n * - Ionic-specific popover dismissal via PopoverController\n *\n * All list logic, loading state checks, and button resolution is delegated\n * to the injected service, making the logic reusable for other UI libraries.\n */\n\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n input,\n output,\n signal,\n Signal\n} from '@angular/core';\nimport {\n IonIcon,\n IonItem,\n IonLabel,\n IonList,\n IonSpinner,\n PopoverController\n} from '@ionic/angular/standalone';\n\nimport { ActionButton, ActionButtonListService, ActionButtonType } from '@makigamestudio/ui-core';\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 providers: [ActionButtonListService],\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]=\"!canSelectButton(button)\"\n [detail]=\"button.type === ActionButtonType.Dropdown\"\n (click)=\"onButtonClick(button)\"\n >\n @if (shouldShowSpinner(button)) {\n <ion-spinner slot=\"start\" name=\"crescent\" />\n } @else if (button.icon) {\n <ion-icon slot=\"start\" [color]=\"button.config?.color\" [name]=\"button.icon\" />\n }\n @if (button.label) {\n <ion-label [color]=\"button.config?.color\">{{ 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 ::ng-deep ion-popover::part(content) {\n width: fit-content;\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\n --min-height: var(--maki-action-button-list-item-height, 44px);\n\n cursor: pointer;\n\n width: 100%;\n }\n\n ion-item[disabled] {\n cursor: not-allowed;\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 /** Reference to ActionButtonType.Dropdown for template comparison. */\n protected readonly ActionButtonType = ActionButtonType;\n\n /** Ionic PopoverController for dismissing the popover when an item is selected. */\n private readonly popoverCtrl = inject(PopoverController, { optional: true });\n\n /** Service for list logic. */\n private readonly listService = inject(ActionButtonListService);\n\n /** Internal signal to store buttons passed via componentProps. */\n private readonly _buttonsFromProps = signal<readonly ActionButton[]>([]);\n\n /** Internal signal to store the Set of currently loading child button IDs. */\n private readonly _loadingChildIdsFromProps = signal<Signal<ReadonlySet<string>> | null>(null);\n\n /** The list of action buttons to display (when used via template binding). */\n readonly buttons = input<readonly ActionButton[]>([]);\n\n /** Emits when a button is selected from the list. */\n readonly buttonSelect = output<ActionButton>();\n\n /** Computed button list that works with both signal input and componentProps. */\n protected readonly buttonList = computed(() =>\n this.listService.resolveButtonList(this._buttonsFromProps(), this.buttons())\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 * Setter to capture loading child IDs signal passed via PopoverController.create({ componentProps }).\n */\n set loadingChildIds(value: Signal<ReadonlySet<string>>) {\n this._loadingChildIdsFromProps.set(value);\n }\n\n /** Checks if a button can be selected. */\n protected canSelectButton(button: ActionButton): boolean {\n const loadingChildIds = this._loadingChildIdsFromProps()?.() ?? new Set<string>();\n return this.listService.canSelectButton(button, loadingChildIds);\n }\n\n /** Checks if a button should show its loading spinner. */\n protected shouldShowSpinner(button: ActionButton): boolean {\n const loadingChildIds = this._loadingChildIdsFromProps()?.() ?? new Set<string>();\n return this.listService.shouldShowSpinner(button, loadingChildIds);\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 const loadingChildIds = this._loadingChildIdsFromProps()?.() ?? new Set<string>();\n if (!this.listService.canSelectButton(button, loadingChildIds)) {\n return;\n }\n\n this.buttonSelect.emit(button);\n this.popoverCtrl?.dismiss(button, 'select');\n }\n}\n","/**\n * @file Button Component\n * @description Ionic implementation of a configurable button component.\n *\n * This component is the Ionic-specific implementation that uses the shared\n * button services from @makigamestudio/ui-core for all business logic.\n * The component only handles:\n * - Ionic template rendering (ion-button, ion-icon, ion-spinner)\n * - Ionic-specific popover creation via PopoverController\n *\n * All state management, display logic, and handler execution is delegated\n * to the injected services, making the logic reusable for other UI libraries.\n */\n\nimport { ChangeDetectionStrategy, Component, computed, inject, input, output } from '@angular/core';\nimport { IonButton, IonIcon, IonSpinner, PopoverController } from '@ionic/angular/standalone';\nimport { addIcons } from 'ionicons';\nimport { chevronDownOutline } from 'ionicons/icons';\n\nimport {\n ActionButton,\n ActionButtonType,\n ButtonDisplayService,\n ButtonHandlerService,\n ButtonStateService\n} from '@makigamestudio/ui-core';\n\nimport { IonicPopoverAlignment } from '../../types/ionic-action-button.types';\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 * - Two button types: Default and Dropdown\n * - Flexible display: icon-only, label-only, or icon+label (determined by properties)\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 * <!-- Button with label and icon -->\n * <maki-button [button]=\"saveButton\" />\n *\n * <!-- Icon-only button (no label) -->\n * <maki-button [button]=\"iconButton\" />\n *\n * <!-- Label-only button (no icon) -->\n * <maki-button [button]=\"textButton\" />\n *\n * <!-- Dropdown button -->\n * <maki-button [button]=\"menuButton\" />\n * ```\n *\n * @example\n * ```typescript\n * // In your component\n * import { ActionButton, ActionButtonType } from '@makigamestudio/ui-core';\n * import { IonicActionButton } from '@makigamestudio/ui-ionic';\n *\n * // Generic ActionButton (loose typing)\n * saveButton: ActionButton = {\n * id: 'save',\n * label: 'Save',\n * icon: 'save-outline',\n * type: ActionButtonType.Default,\n * config: { fill: 'solid', color: 'primary' },\n * handler: async () => {\n * await this.saveData();\n * }\n * };\n *\n * // IonicActionButton (strict typing for Ionic-specific config)\n * ionicButton: IonicActionButton = {\n * id: 'ionic',\n * label: 'Ionic Button',\n * type: ActionButtonType.Default,\n * handler: () => {},\n * config: {\n * fill: 'solid', // Type-checked: 'clear' | 'outline' | 'solid' | 'default'\n * size: 'large' // Type-checked: 'small' | 'default' | 'large'\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: 'create-outline', type: ActionButtonType.Default, handler: () => this.edit() },\n * { id: 'delete', label: 'Delete', icon: 'trash-outline', 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 providers: [ButtonStateService, ButtonDisplayService, ButtonHandlerService],\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 (showLoadingSpinner()) {\n <ion-spinner [slot]=\"iconSlot()\" name=\"crescent\" />\n } @else if (button().icon) {\n <ion-icon [name]=\"button().icon\" [slot]=\"iconSlot()\" class=\"button-icon\" />\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 </ion-button>\n `,\n styles: [\n `\n ion-button {\n --padding-start: var(--maki-button-padding-start);\n --padding-end: var(--maki-button-padding-end);\n text-transform: none;\n }\n\n ion-button.button-has-icon-only::part(native) {\n padding: 0;\n }\n\n ion-spinner {\n width: var(--maki-button-spinner-size, 16px);\n height: var(--maki-button-spinner-size, 16px);\n margin: 0 4px 0 0;\n }\n\n .button-icon {\n font-size: var(--maki-button-icon-size, 16px);\n margin: 0 4px 0 0;\n }\n\n .button-icon[slot='icon-only'] {\n margin: 0;\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 /** Ionic PopoverController for dropdown popovers. */\n private readonly popoverCtrl = inject(PopoverController);\n\n /** Service for button state management. */\n private readonly stateService = inject(ButtonStateService);\n\n /** Service for button display logic. */\n private readonly displayService = inject(ButtonDisplayService);\n\n /** Service for handler execution. */\n private readonly handlerService = inject(ButtonHandlerService);\n\n /** The action button configuration. */\n readonly button = input.required<ActionButton>();\n\n /** Emits when the button is clicked (for non-dropdown buttons). */\n readonly buttonClick = output<ActionButton>();\n\n /** Emits when a child button is selected from a dropdown. */\n readonly childSelect = output<ActionButton>();\n\n /** Whether the button is in a loading state. */\n readonly isLoading = computed(() => this.stateService.isLoading(this.button()));\n\n /** Whether to display the loading spinner. */\n readonly showLoadingSpinner = computed(() => this.stateService.showLoadingSpinner(this.button()));\n\n /** Whether the button is disabled. */\n readonly isDisabled = computed(() => this.stateService.isDisabled(this.button()));\n\n /** The slot for the icon based on label presence (Ionic-specific). */\n readonly iconSlot = computed(() => (this.button().label ? 'start' : 'icon-only'));\n\n /** Whether to show the label text. */\n readonly showLabel = computed(() => this.displayService.shouldShowLabel(this.button()));\n\n /** Whether to show the dropdown chevron icon. */\n readonly showDropdownIcon = computed(() =>\n this.displayService.shouldShowDropdownIcon(this.button())\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 event.stopPropagation();\n\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.handlerService.executeHandler(btn, loading =>\n this.stateService.setLoading(loading)\n );\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 alignment = (btn.config?.dropdownAlignment ?? 'end') as IonicPopoverAlignment;\n const popover = await this.popoverCtrl.create({\n component: ActionButtonListComponent,\n componentProps: {\n buttonsFromPopover: children,\n loadingChildIds: this.stateService.loadingChildIds\n },\n event,\n translucent: true,\n dismissOnSelect: true,\n side: 'bottom',\n alignment,\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.handlerService.executeChildHandler(data, (childId, loading) => {\n if (loading) {\n this.stateService.addLoadingChild(childId);\n } else {\n this.stateService.removeLoadingChild(childId);\n }\n });\n this.childSelect.emit(data);\n }\n }\n}\n","/**\n * @file Components Barrel Export\n * @description Exports all Ionic components from ui-ionic.\n */\n\nexport { ActionButtonListComponent } from './action-button-list/action-button-list.component';\nexport { ButtonComponent } from './button/button.component';\n\n","/*\n * Public API Surface of @makigamestudio/ui-ionic\n *\n * This package provides Ionic-specific implementations of UI components\n * using the interfaces and services from @makigamestudio/ui-core.\n */\n\n// ============================================================================\n// Ionic-Specific Types\n// ============================================================================\n\nexport type {\n IonicActionButton,\n IonicActionButtonConfig,\n IonicButtonExpand,\n IonicButtonFill,\n IonicButtonShape,\n IonicButtonSize,\n IonicPopoverAlignment\n} from './lib/types';\n\n// ============================================================================\n// Ionic Components\n// ============================================================================\n\nexport { ActionButtonListComponent, ButtonComponent } from './lib/components';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@makigamestudio/ui-ionic",
3
+ "version": "0.6.0",
4
+ "description": "Ionic implementation of @makigamestudio/ui-core. Provides Ionic-specific button components with signal-based state management.",
5
+ "keywords": [
6
+ "angular",
7
+ "ionic",
8
+ "zoneless",
9
+ "signals",
10
+ "standalone",
11
+ "components",
12
+ "ui-ionic"
13
+ ],
14
+ "author": "MakiGameStudio",
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/gdor/ui-core.git"
19
+ },
20
+ "peerDependencies": {
21
+ "@angular/common": "^21.0.0",
22
+ "@angular/core": "^21.0.0",
23
+ "@ionic/angular": "^8.0.0",
24
+ "@makigamestudio/ui-core": "^0.6.0",
25
+ "ionicons": "^7.0.0"
26
+ },
27
+ "dependencies": {
28
+ "tslib": "^2.6.0"
29
+ },
30
+ "sideEffects": false,
31
+ "module": "fesm2022/makigamestudio-ui-ionic.mjs",
32
+ "typings": "types/makigamestudio-ui-ionic.d.ts",
33
+ "exports": {
34
+ "./package.json": {
35
+ "default": "./package.json"
36
+ },
37
+ ".": {
38
+ "types": "./types/makigamestudio-ui-ionic.d.ts",
39
+ "default": "./fesm2022/makigamestudio-ui-ionic.mjs"
40
+ }
41
+ }
42
+ }
package/theme.css ADDED
@@ -0,0 +1,89 @@
1
+ /**
2
+ * @makigamestudio/maki-core Theme Variables
3
+ *
4
+ * Import this file in your global styles to customize the library components.
5
+ * Example: @use '@makigamestudio/maki-core/theme.scss';
6
+ *
7
+ * Override these variables in your own SCSS to customize the appearance.
8
+ */
9
+ :root {
10
+ --maki-primary: #3880ff;
11
+ --maki-primary-rgb: 56, 128, 255;
12
+ --maki-primary-contrast: #ffffff;
13
+ --maki-primary-shade: #3171e0;
14
+ --maki-primary-tint: #4c8dff;
15
+ --maki-secondary: #3dc2ff;
16
+ --maki-secondary-rgb: 61, 194, 255;
17
+ --maki-secondary-contrast: #000000;
18
+ --maki-secondary-shade: #36abe0;
19
+ --maki-secondary-tint: #50c8ff;
20
+ --maki-accent: #5260ff;
21
+ --maki-accent-rgb: 82, 96, 255;
22
+ --maki-accent-contrast: #ffffff;
23
+ --maki-accent-shade: #4854e0;
24
+ --maki-accent-tint: #6370ff;
25
+ --maki-success: #2dd36f;
26
+ --maki-warning: #ffc409;
27
+ --maki-danger: #eb445a;
28
+ --maki-info: #3dc2ff;
29
+ --maki-background: #f4f5f8;
30
+ --maki-background-rgb: 244, 245, 248;
31
+ --maki-surface: #ffffff;
32
+ --maki-surface-rgb: 255, 255, 255;
33
+ --maki-text-primary: #1a1a1a;
34
+ --maki-text-secondary: #666666;
35
+ --maki-text-muted: #999999;
36
+ --maki-text-inverse: #ffffff;
37
+ --maki-card-background: var(--maki-surface);
38
+ --maki-card-color: var(--maki-text-primary);
39
+ --maki-card-margin: 16px;
40
+ --maki-card-border-radius: 12px;
41
+ --maki-card-box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
42
+ --maki-spacing-xs: 4px;
43
+ --maki-spacing-sm: 8px;
44
+ --maki-spacing-md: 16px;
45
+ --maki-spacing-lg: 24px;
46
+ --maki-spacing-xl: 32px;
47
+ --maki-spacing-xxl: 48px;
48
+ --maki-radius-sm: 4px;
49
+ --maki-radius-md: 8px;
50
+ --maki-radius-lg: 12px;
51
+ --maki-radius-xl: 16px;
52
+ --maki-radius-full: 9999px;
53
+ --maki-font-family:
54
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
55
+ --maki-font-size-xs: 0.75rem;
56
+ --maki-font-size-sm: 0.875rem;
57
+ --maki-font-size-md: 1rem;
58
+ --maki-font-size-lg: 1.125rem;
59
+ --maki-font-size-xl: 1.25rem;
60
+ --maki-font-size-xxl: 1.5rem;
61
+ --maki-transition-fast: 150ms ease;
62
+ --maki-transition-normal: 250ms ease;
63
+ --maki-transition-slow: 350ms ease;
64
+ --maki-button-padding-start: 8px;
65
+ --maki-button-padding-end: 8px;
66
+ --maki-button-spinner-size: 16px;
67
+ --maki-button-dropdown-icon-size: 16px;
68
+ --maki-button-dropdown-icon-gap: 4px;
69
+ --maki-action-button-list-padding: 16px;
70
+ --maki-action-button-list-item-height: 44px;
71
+ --maki-action-button-list-icon-size: 20px;
72
+ --maki-action-button-list-icon-gap: 12px;
73
+ }
74
+
75
+ @media (prefers-color-scheme: dark) {
76
+ :root {
77
+ --maki-background: #1a1a1a;
78
+ --maki-background-rgb: 26, 26, 26;
79
+ --maki-surface: #2d2d2d;
80
+ --maki-surface-rgb: 45, 45, 45;
81
+ --maki-text-primary: #ffffff;
82
+ --maki-text-secondary: #b3b3b3;
83
+ --maki-text-muted: #808080;
84
+ --maki-text-inverse: #1a1a1a;
85
+ --maki-card-background: var(--maki-surface);
86
+ --maki-card-color: var(--maki-text-primary);
87
+ --maki-card-box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
88
+ }
89
+ }
package/theme.scss ADDED
@@ -0,0 +1,140 @@
1
+ /**
2
+ * @makigamestudio/maki-core Theme Variables
3
+ *
4
+ * Import this file in your global styles to customize the library components.
5
+ * Example: @use '@makigamestudio/maki-core/theme.scss';
6
+ *
7
+ * Override these variables in your own SCSS to customize the appearance.
8
+ */
9
+
10
+ :root {
11
+ // ==============================================
12
+ // Brand Colors
13
+ // ==============================================
14
+ --maki-primary: #3880ff;
15
+ --maki-primary-rgb: 56, 128, 255;
16
+ --maki-primary-contrast: #ffffff;
17
+ --maki-primary-shade: #3171e0;
18
+ --maki-primary-tint: #4c8dff;
19
+
20
+ --maki-secondary: #3dc2ff;
21
+ --maki-secondary-rgb: 61, 194, 255;
22
+ --maki-secondary-contrast: #000000;
23
+ --maki-secondary-shade: #36abe0;
24
+ --maki-secondary-tint: #50c8ff;
25
+
26
+ --maki-accent: #5260ff;
27
+ --maki-accent-rgb: 82, 96, 255;
28
+ --maki-accent-contrast: #ffffff;
29
+ --maki-accent-shade: #4854e0;
30
+ --maki-accent-tint: #6370ff;
31
+
32
+ // ==============================================
33
+ // Semantic Colors
34
+ // ==============================================
35
+ --maki-success: #2dd36f;
36
+ --maki-warning: #ffc409;
37
+ --maki-danger: #eb445a;
38
+ --maki-info: #3dc2ff;
39
+
40
+ // ==============================================
41
+ // Background & Surface
42
+ // ==============================================
43
+ --maki-background: #f4f5f8;
44
+ --maki-background-rgb: 244, 245, 248;
45
+ --maki-surface: #ffffff;
46
+ --maki-surface-rgb: 255, 255, 255;
47
+
48
+ // ==============================================
49
+ // Text Colors
50
+ // ==============================================
51
+ --maki-text-primary: #1a1a1a;
52
+ --maki-text-secondary: #666666;
53
+ --maki-text-muted: #999999;
54
+ --maki-text-inverse: #ffffff;
55
+
56
+ // ==============================================
57
+ // Card Component
58
+ // ==============================================
59
+ --maki-card-background: var(--maki-surface);
60
+ --maki-card-color: var(--maki-text-primary);
61
+ --maki-card-margin: 16px;
62
+ --maki-card-border-radius: 12px;
63
+ --maki-card-box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
64
+
65
+ // ==============================================
66
+ // Spacing Scale
67
+ // ==============================================
68
+ --maki-spacing-xs: 4px;
69
+ --maki-spacing-sm: 8px;
70
+ --maki-spacing-md: 16px;
71
+ --maki-spacing-lg: 24px;
72
+ --maki-spacing-xl: 32px;
73
+ --maki-spacing-xxl: 48px;
74
+
75
+ // ==============================================
76
+ // Border Radius Scale
77
+ // ==============================================
78
+ --maki-radius-sm: 4px;
79
+ --maki-radius-md: 8px;
80
+ --maki-radius-lg: 12px;
81
+ --maki-radius-xl: 16px;
82
+ --maki-radius-full: 9999px;
83
+
84
+ // ==============================================
85
+ // Typography
86
+ // ==============================================
87
+ --maki-font-family:
88
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
89
+ --maki-font-size-xs: 0.75rem;
90
+ --maki-font-size-sm: 0.875rem;
91
+ --maki-font-size-md: 1rem;
92
+ --maki-font-size-lg: 1.125rem;
93
+ --maki-font-size-xl: 1.25rem;
94
+ --maki-font-size-xxl: 1.5rem;
95
+
96
+ // ==============================================
97
+ // Transitions
98
+ // ==============================================
99
+ --maki-transition-fast: 150ms ease;
100
+ --maki-transition-normal: 250ms ease;
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: 16px;
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;
119
+ }
120
+
121
+ // ==============================================
122
+ // Dark Mode Overrides
123
+ // ==============================================
124
+ @media (prefers-color-scheme: dark) {
125
+ :root {
126
+ --maki-background: #1a1a1a;
127
+ --maki-background-rgb: 26, 26, 26;
128
+ --maki-surface: #2d2d2d;
129
+ --maki-surface-rgb: 45, 45, 45;
130
+
131
+ --maki-text-primary: #ffffff;
132
+ --maki-text-secondary: #b3b3b3;
133
+ --maki-text-muted: #808080;
134
+ --maki-text-inverse: #1a1a1a;
135
+
136
+ --maki-card-background: var(--maki-surface);
137
+ --maki-card-color: var(--maki-text-primary);
138
+ --maki-card-box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
139
+ }
140
+ }
@@ -0,0 +1,314 @@
1
+ import * as _makigamestudio_ui_core from '@makigamestudio/ui-core';
2
+ import { ActionButton, ActionButtonConfig, ActionButtonType } from '@makigamestudio/ui-core';
3
+ import * as _angular_core from '@angular/core';
4
+ import { Signal } from '@angular/core';
5
+
6
+ /**
7
+ * @file Ionic Action Button Types
8
+ * @description Ionic-specific type narrowing for ActionButtonConfig and ActionButton interfaces.
9
+ *
10
+ * This file provides type-safe Ionic implementations by narrowing the generic
11
+ * types from @makigamestudio/ui-core to Ionic-specific values.
12
+ */
13
+
14
+ /**
15
+ * Ionic button fill styles.
16
+ * @see https://ionicframework.com/docs/api/button#fill
17
+ */
18
+ type IonicButtonFill = 'clear' | 'outline' | 'solid' | 'default';
19
+ /**
20
+ * Ionic button sizes.
21
+ * @see https://ionicframework.com/docs/api/button#size
22
+ */
23
+ type IonicButtonSize = 'small' | 'default' | 'large';
24
+ /**
25
+ * Ionic button shapes.
26
+ * @see https://ionicframework.com/docs/api/button#shape
27
+ */
28
+ type IonicButtonShape = 'round';
29
+ /**
30
+ * Ionic button expansion modes.
31
+ * @see https://ionicframework.com/docs/api/button#expand
32
+ */
33
+ type IonicButtonExpand = 'block' | 'full';
34
+ /**
35
+ * Ionic popover alignment options.
36
+ * @see https://ionicframework.com/docs/api/popover#alignment
37
+ */
38
+ type IonicPopoverAlignment = 'start' | 'center' | 'end';
39
+ /**
40
+ * Ionic-specific action button configuration.
41
+ *
42
+ * This type narrows the generic ActionButtonConfig to Ionic's specific values,
43
+ * providing type safety for Ionic button properties.
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * const config: IonicActionButtonConfig = {
48
+ * fill: 'solid', // Type-checked: 'clear' | 'outline' | 'solid' | 'default'
49
+ * size: 'large', // Type-checked: 'small' | 'default' | 'large'
50
+ * color: 'primary', // Any string (Ionic color names or custom)
51
+ * shape: 'round', // Type-checked: 'round' | undefined
52
+ * expand: 'block', // Type-checked: 'block' | 'full' | undefined
53
+ * dropdownAlignment: 'end' // Type-checked: 'start' | 'center' | 'end'
54
+ * };
55
+ * ```
56
+ */
57
+ type IonicActionButtonConfig = ActionButtonConfig<IonicButtonFill, IonicButtonSize, IonicButtonShape, IonicButtonExpand, IonicPopoverAlignment>;
58
+ /**
59
+ * Ionic-specific action button interface.
60
+ *
61
+ * This type extends the generic ActionButton interface with Ionic-specific
62
+ * configuration types, providing full type safety for Ionic implementations.
63
+ *
64
+ * @typeParam T - The type of data passed to the handler function. Defaults to `void`.
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * import { IonicActionButton } from '@makigamestudio/ui-ionic';
69
+ * import { ActionButtonType } from '@makigamestudio/ui-core';
70
+ *
71
+ * const saveButton: IonicActionButton = {
72
+ * id: 'save',
73
+ * label: 'Save',
74
+ * icon: 'save-outline',
75
+ * type: ActionButtonType.Default,
76
+ * handler: () => console.log('Saved!'),
77
+ * config: {
78
+ * fill: 'solid', // Type-checked
79
+ * size: 'large', // Type-checked
80
+ * color: 'primary'
81
+ * }
82
+ * };
83
+ *
84
+ * // Dropdown with children
85
+ * const menuButton: IonicActionButton = {
86
+ * id: 'menu',
87
+ * label: 'Actions',
88
+ * type: ActionButtonType.Dropdown,
89
+ * handler: () => {},
90
+ * config: {
91
+ * fill: 'outline',
92
+ * dropdownAlignment: 'end' // Type-checked
93
+ * },
94
+ * children: [
95
+ * {
96
+ * id: 'edit',
97
+ * label: 'Edit',
98
+ * icon: 'create-outline',
99
+ * type: ActionButtonType.Default,
100
+ * handler: () => edit()
101
+ * }
102
+ * ]
103
+ * };
104
+ * ```
105
+ */
106
+ type IonicActionButton<T = void> = ActionButton<T, IonicActionButtonConfig>;
107
+
108
+ /**
109
+ * Component that renders a list of action buttons for use in popovers/dropdowns.
110
+ *
111
+ * This component is designed to be used as the content of an `ion-popover`
112
+ * created via `PopoverController`. When a button is selected, it dismisses
113
+ * the popover and returns the selected button.
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * // Used internally by maki-button for dropdowns
118
+ * // Can also be used standalone:
119
+ * const popover = await popoverCtrl.create({
120
+ * component: ActionButtonListComponent,
121
+ * componentProps: { buttons: myButtons },
122
+ * event: clickEvent
123
+ * });
124
+ * await popover.present();
125
+ *
126
+ * const { data } = await popover.onDidDismiss();
127
+ * if (data) {
128
+ * data.handler();
129
+ * }
130
+ * ```
131
+ *
132
+ * @usageNotes
133
+ * ### Inputs
134
+ * - `buttons` (required): Array of `ActionButton` objects to display
135
+ *
136
+ * ### Outputs
137
+ * - `buttonSelect`: Emits the selected `ActionButton` when clicked
138
+ */
139
+ declare class ActionButtonListComponent {
140
+ /** Reference to ActionButtonType.Dropdown for template comparison. */
141
+ protected readonly ActionButtonType: typeof ActionButtonType;
142
+ /** Ionic PopoverController for dismissing the popover when an item is selected. */
143
+ private readonly popoverCtrl;
144
+ /** Service for list logic. */
145
+ private readonly listService;
146
+ /** Internal signal to store buttons passed via componentProps. */
147
+ private readonly _buttonsFromProps;
148
+ /** Internal signal to store the Set of currently loading child button IDs. */
149
+ private readonly _loadingChildIdsFromProps;
150
+ /** The list of action buttons to display (when used via template binding). */
151
+ readonly buttons: _angular_core.InputSignal<readonly ActionButton<void, _makigamestudio_ui_core.ActionButtonConfig<string, string, string, string, string>>[]>;
152
+ /** Emits when a button is selected from the list. */
153
+ readonly buttonSelect: _angular_core.OutputEmitterRef<ActionButton<void, _makigamestudio_ui_core.ActionButtonConfig<string, string, string, string, string>>>;
154
+ /** Computed button list that works with both signal input and componentProps. */
155
+ protected readonly buttonList: Signal<readonly ActionButton<void, _makigamestudio_ui_core.ActionButtonConfig<string, string, string, string, string>>[]>;
156
+ /**
157
+ * Setter to capture buttons passed via PopoverController.create({ componentProps }).
158
+ * This is called when Ionic sets the property directly on the component instance.
159
+ */
160
+ set buttonsFromPopover(value: readonly ActionButton[]);
161
+ /**
162
+ * Setter to capture loading child IDs signal passed via PopoverController.create({ componentProps }).
163
+ */
164
+ set loadingChildIds(value: Signal<ReadonlySet<string>>);
165
+ /** Checks if a button can be selected. */
166
+ protected canSelectButton(button: ActionButton): boolean;
167
+ /** Checks if a button should show its loading spinner. */
168
+ protected shouldShowSpinner(button: ActionButton): boolean;
169
+ /**
170
+ * Handles click on a button item.
171
+ * Emits the selected button and dismisses the popover.
172
+ *
173
+ * @param button - The clicked action button
174
+ */
175
+ protected onButtonClick(button: ActionButton): void;
176
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ActionButtonListComponent, never>;
177
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ActionButtonListComponent, "maki-action-button-list", never, { "buttons": { "alias": "buttons"; "required": false; "isSignal": true; }; }, { "buttonSelect": "buttonSelect"; }, never, never, true, never>;
178
+ }
179
+
180
+ /**
181
+ * A configurable button component that renders an `ion-button` based on
182
+ * an `ActionButton` configuration object.
183
+ *
184
+ * Features:
185
+ * - Two button types: Default and Dropdown
186
+ * - Flexible display: icon-only, label-only, or icon+label (determined by properties)
187
+ * - Automatic loading state management for async handlers
188
+ * - Dropdown support via PopoverController with child actions
189
+ * - Full Ionic button styling configuration (fill, size, color, shape, expand)
190
+ * - Automatic chevron icon for dropdown buttons
191
+ *
192
+ * @example
193
+ * ```html
194
+ * <!-- Button with label and icon -->
195
+ * <maki-button [button]="saveButton" />
196
+ *
197
+ * <!-- Icon-only button (no label) -->
198
+ * <maki-button [button]="iconButton" />
199
+ *
200
+ * <!-- Label-only button (no icon) -->
201
+ * <maki-button [button]="textButton" />
202
+ *
203
+ * <!-- Dropdown button -->
204
+ * <maki-button [button]="menuButton" />
205
+ * ```
206
+ *
207
+ * @example
208
+ * ```typescript
209
+ * // In your component
210
+ * import { ActionButton, ActionButtonType } from '@makigamestudio/ui-core';
211
+ * import { IonicActionButton } from '@makigamestudio/ui-ionic';
212
+ *
213
+ * // Generic ActionButton (loose typing)
214
+ * saveButton: ActionButton = {
215
+ * id: 'save',
216
+ * label: 'Save',
217
+ * icon: 'save-outline',
218
+ * type: ActionButtonType.Default,
219
+ * config: { fill: 'solid', color: 'primary' },
220
+ * handler: async () => {
221
+ * await this.saveData();
222
+ * }
223
+ * };
224
+ *
225
+ * // IonicActionButton (strict typing for Ionic-specific config)
226
+ * ionicButton: IonicActionButton = {
227
+ * id: 'ionic',
228
+ * label: 'Ionic Button',
229
+ * type: ActionButtonType.Default,
230
+ * handler: () => {},
231
+ * config: {
232
+ * fill: 'solid', // Type-checked: 'clear' | 'outline' | 'solid' | 'default'
233
+ * size: 'large' // Type-checked: 'small' | 'default' | 'large'
234
+ * }
235
+ * };
236
+ *
237
+ * menuButton: ActionButton = {
238
+ * id: 'menu',
239
+ * label: 'Actions',
240
+ * type: ActionButtonType.Dropdown,
241
+ * handler: () => {},
242
+ * children: [
243
+ * { id: 'edit', label: 'Edit', icon: 'create-outline', type: ActionButtonType.Default, handler: () => this.edit() },
244
+ * { id: 'delete', label: 'Delete', icon: 'trash-outline', type: ActionButtonType.Default, handler: () => this.delete() }
245
+ * ]
246
+ * };
247
+ * ```
248
+ *
249
+ * @usageNotes
250
+ * ### Inputs
251
+ * - `button` (required): The `ActionButton` configuration object
252
+ *
253
+ * ### Outputs
254
+ * - `buttonClick`: Emits the button configuration when clicked (for non-dropdown buttons)
255
+ * - `childSelect`: Emits the selected child button (for dropdown buttons)
256
+ *
257
+ * ### Loading State
258
+ * The component automatically manages loading state for async handlers.
259
+ * When a handler returns a Promise, the button shows a spinner until it resolves/rejects.
260
+ * You can also manually control loading via the `button.loading` property.
261
+ *
262
+ * ### Dropdown Behavior
263
+ * For `ActionButtonType.Dropdown`, clicking the button opens an `ion-popover`
264
+ * containing an `ActionButtonListComponent` with the child buttons.
265
+ * When a child is selected, its handler is executed and `childSelect` is emitted.
266
+ */
267
+ declare class ButtonComponent {
268
+ /** Ionic PopoverController for dropdown popovers. */
269
+ private readonly popoverCtrl;
270
+ /** Service for button state management. */
271
+ private readonly stateService;
272
+ /** Service for button display logic. */
273
+ private readonly displayService;
274
+ /** Service for handler execution. */
275
+ private readonly handlerService;
276
+ /** The action button configuration. */
277
+ readonly button: _angular_core.InputSignal<ActionButton<void, _makigamestudio_ui_core.ActionButtonConfig<string, string, string, string, string>>>;
278
+ /** Emits when the button is clicked (for non-dropdown buttons). */
279
+ readonly buttonClick: _angular_core.OutputEmitterRef<ActionButton<void, _makigamestudio_ui_core.ActionButtonConfig<string, string, string, string, string>>>;
280
+ /** Emits when a child button is selected from a dropdown. */
281
+ readonly childSelect: _angular_core.OutputEmitterRef<ActionButton<void, _makigamestudio_ui_core.ActionButtonConfig<string, string, string, string, string>>>;
282
+ /** Whether the button is in a loading state. */
283
+ readonly isLoading: _angular_core.Signal<boolean>;
284
+ /** Whether to display the loading spinner. */
285
+ readonly showLoadingSpinner: _angular_core.Signal<boolean>;
286
+ /** Whether the button is disabled. */
287
+ readonly isDisabled: _angular_core.Signal<boolean>;
288
+ /** The slot for the icon based on label presence (Ionic-specific). */
289
+ readonly iconSlot: _angular_core.Signal<"start" | "icon-only">;
290
+ /** Whether to show the label text. */
291
+ readonly showLabel: _angular_core.Signal<boolean>;
292
+ /** Whether to show the dropdown chevron icon. */
293
+ readonly showDropdownIcon: _angular_core.Signal<boolean>;
294
+ constructor();
295
+ /**
296
+ * Handles button click events.
297
+ * For dropdown buttons, opens a popover with child actions.
298
+ * For other buttons, executes the handler with auto-loading management.
299
+ *
300
+ * @param event - The click event
301
+ */
302
+ protected onClick(event: Event): Promise<void>;
303
+ /**
304
+ * Opens a dropdown popover with child action buttons.
305
+ *
306
+ * @param event - The triggering click event for popover positioning
307
+ */
308
+ private openDropdown;
309
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ButtonComponent, never>;
310
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ButtonComponent, "maki-button", never, { "button": { "alias": "button"; "required": true; "isSignal": true; }; }, { "buttonClick": "buttonClick"; "childSelect": "childSelect"; }, never, never, true, never>;
311
+ }
312
+
313
+ export { ActionButtonListComponent, ButtonComponent };
314
+ export type { IonicActionButton, IonicActionButtonConfig, IonicButtonExpand, IonicButtonFill, IonicButtonShape, IonicButtonSize, IonicPopoverAlignment };