@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 +63 -0
- package/fesm2022/makigamestudio-ui-ionic.mjs +430 -0
- package/fesm2022/makigamestudio-ui-ionic.mjs.map +1 -0
- package/package.json +42 -0
- package/theme.css +89 -0
- package/theme.scss +140 -0
- package/types/makigamestudio-ui-ionic.d.ts +314 -0
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 };
|