capacitor-extended-dialog 0.0.14 → 0.0.16

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.
@@ -13,5 +13,6 @@ Pod::Spec.new do |s|
13
13
  s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
14
  s.ios.deployment_target = '15.0'
15
15
  s.dependency 'Capacitor'
16
+ s.dependency 'SVGKit', '~> 3.0'
16
17
  s.swift_version = '5.1'
17
18
  end
package/Package.swift CHANGED
@@ -10,14 +10,16 @@ let package = Package(
10
10
  targets: ["ExtendedDialogPlugin"])
11
11
  ],
12
12
  dependencies: [
13
- .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")
13
+ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0"),
14
+ .package(url: "https://github.com/SVGKit/SVGKit.git", from: "3.0.0")
14
15
  ],
15
16
  targets: [
16
17
  .target(
17
18
  name: "ExtendedDialogPlugin",
18
19
  dependencies: [
19
20
  .product(name: "Capacitor", package: "capacitor-swift-pm"),
20
- .product(name: "Cordova", package: "capacitor-swift-pm")
21
+ .product(name: "Cordova", package: "capacitor-swift-pm"),
22
+ .product(name: "SVGKit", package: "SVGKit")
21
23
  ],
22
24
  path: "ios/Sources/ExtendedDialogPlugin"),
23
25
  .testTarget(
package/README.md CHANGED
@@ -38,7 +38,7 @@ npx cap sync
38
38
  ### alert(...)
39
39
 
40
40
  ```typescript
41
- alert(options: AlertOptions) => Promise<void>
41
+ alert(options: AlertOptions) => any
42
42
  ```
43
43
 
44
44
  Show an alert dialog with a single dismiss button.
@@ -47,13 +47,15 @@ Show an alert dialog with a single dismiss button.
47
47
  | ------------- | ----------------------------------------------------- | ------------------------------ |
48
48
  | **`options`** | <code><a href="#alertoptions">AlertOptions</a></code> | - Alert configuration options. |
49
49
 
50
+ **Returns:** <code>any</code>
51
+
50
52
  --------------------
51
53
 
52
54
 
53
55
  ### confirm(...)
54
56
 
55
57
  ```typescript
56
- confirm(options: ConfirmOptions) => Promise<ConfirmResult>
58
+ confirm(options: ConfirmOptions) => any
57
59
  ```
58
60
 
59
61
  Show a confirmation dialog with OK and Cancel buttons.
@@ -62,7 +64,7 @@ Show a confirmation dialog with OK and Cancel buttons.
62
64
  | ------------- | --------------------------------------------------------- | -------------------------------- |
63
65
  | **`options`** | <code><a href="#confirmoptions">ConfirmOptions</a></code> | - Confirm configuration options. |
64
66
 
65
- **Returns:** <code>Promise&lt;<a href="#confirmresult">ConfirmResult</a>&gt;</code>
67
+ **Returns:** <code>any</code>
66
68
 
67
69
  --------------------
68
70
 
@@ -70,7 +72,7 @@ Show a confirmation dialog with OK and Cancel buttons.
70
72
  ### prompt(...)
71
73
 
72
74
  ```typescript
73
- prompt(options: PromptOptions) => Promise<PromptResult>
75
+ prompt(options: PromptOptions) => any
74
76
  ```
75
77
 
76
78
  Show a prompt dialog with a text input field.
@@ -79,7 +81,7 @@ Show a prompt dialog with a text input field.
79
81
  | ------------- | ------------------------------------------------------- | ------------------------------- |
80
82
  | **`options`** | <code><a href="#promptoptions">PromptOptions</a></code> | - Prompt configuration options. |
81
83
 
82
- **Returns:** <code>Promise&lt;<a href="#promptresult">PromptResult</a>&gt;</code>
84
+ **Returns:** <code>any</code>
83
85
 
84
86
  --------------------
85
87
 
@@ -87,7 +89,7 @@ Show a prompt dialog with a text input field.
87
89
  ### singleSelect(...)
88
90
 
89
91
  ```typescript
90
- singleSelect(options: SingleSelectOptions) => Promise<SingleSelectResult>
92
+ singleSelect(options: SingleSelectOptions) => any
91
93
  ```
92
94
 
93
95
  Show a single-select dialog where the user picks one option.
@@ -96,7 +98,7 @@ Show a single-select dialog where the user picks one option.
96
98
  | ------------- | ------------------------------------------------------------------- | -------------------------------------- |
97
99
  | **`options`** | <code><a href="#singleselectoptions">SingleSelectOptions</a></code> | - Single select configuration options. |
98
100
 
99
- **Returns:** <code>Promise&lt;<a href="#singleselectresult">SingleSelectResult</a>&gt;</code>
101
+ **Returns:** <code>any</code>
100
102
 
101
103
  --------------------
102
104
 
@@ -104,7 +106,7 @@ Show a single-select dialog where the user picks one option.
104
106
  ### multiSelect(...)
105
107
 
106
108
  ```typescript
107
- multiSelect(options: MultiSelectOptions) => Promise<MultiSelectResult>
109
+ multiSelect(options: MultiSelectOptions) => any
108
110
  ```
109
111
 
110
112
  Show a multi-select dialog where the user picks one or more options.
@@ -113,7 +115,7 @@ Show a multi-select dialog where the user picks one or more options.
113
115
  | ------------- | ----------------------------------------------------------------- | ------------------------------------- |
114
116
  | **`options`** | <code><a href="#multiselectoptions">MultiSelectOptions</a></code> | - Multi select configuration options. |
115
117
 
116
- **Returns:** <code>Promise&lt;<a href="#multiselectresult">MultiSelectResult</a>&gt;</code>
118
+ **Returns:** <code>any</code>
117
119
 
118
120
  --------------------
119
121
 
@@ -121,7 +123,7 @@ Show a multi-select dialog where the user picks one or more options.
121
123
  ### sheet(...)
122
124
 
123
125
  ```typescript
124
- sheet(options: SheetOptions) => Promise<SheetResult>
126
+ sheet(options: SheetOptions) => any
125
127
  ```
126
128
 
127
129
  Show a sheet dialog with a header, title, and structured data rows.
@@ -130,7 +132,7 @@ Show a sheet dialog with a header, title, and structured data rows.
130
132
  | ------------- | ----------------------------------------------------- | ------------------------------ |
131
133
  | **`options`** | <code><a href="#sheetoptions">SheetOptions</a></code> | - Sheet configuration options. |
132
134
 
133
- **Returns:** <code>Promise&lt;<a href="#sheetresult">SheetResult</a>&gt;</code>
135
+ **Returns:** <code>any</code>
134
136
 
135
137
  --------------------
136
138
 
@@ -138,7 +140,7 @@ Show a sheet dialog with a header, title, and structured data rows.
138
140
  ### messageSheet(...)
139
141
 
140
142
  ```typescript
141
- messageSheet(options: MessageSheetOptions) => Promise<MessageSheetResult>
143
+ messageSheet(options: MessageSheetOptions) => any
142
144
  ```
143
145
 
144
146
  Show a message sheet dialog with an optional header logo, title, and message.
@@ -147,7 +149,7 @@ Show a message sheet dialog with an optional header logo, title, and message.
147
149
  | ------------- | ------------------------------------------------------------------- | -------------------------------------- |
148
150
  | **`options`** | <code><a href="#messagesheetoptions">MessageSheetOptions</a></code> | - Message sheet configuration options. |
149
151
 
150
- **Returns:** <code>Promise&lt;<a href="#messagesheetresult">MessageSheetResult</a>&gt;</code>
152
+ **Returns:** <code>any</code>
151
153
 
152
154
  --------------------
153
155
 
@@ -162,13 +164,6 @@ Show a message sheet dialog with an optional header logo, title, and message.
162
164
  | **`buttonTitle`** | <code>string</code> | Title for the dismiss button. | <code>"OK"</code> |
163
165
 
164
166
 
165
- #### ConfirmResult
166
-
167
- | Prop | Type | Description |
168
- | ----------- | -------------------- | ----------------------------------------------------------- |
169
- | **`value`** | <code>boolean</code> | Whether the user confirmed (`true`) or cancelled (`false`). |
170
-
171
-
172
167
  #### ConfirmOptions
173
168
 
174
169
  | Prop | Type | Description | Default |
@@ -177,12 +172,11 @@ Show a message sheet dialog with an optional header logo, title, and message.
177
172
  | **`cancelButtonTitle`** | <code>string</code> | Title for the cancel button. | <code>"Cancel"</code> |
178
173
 
179
174
 
180
- #### PromptResult
175
+ #### ConfirmResult
181
176
 
182
- | Prop | Type | Description |
183
- | --------------- | -------------------- | -------------------------------------------------------- |
184
- | **`value`** | <code>string</code> | The text entered by the user. Empty string if cancelled. |
185
- | **`cancelled`** | <code>boolean</code> | Whether the user cancelled the dialog. |
177
+ | Prop | Type | Description |
178
+ | ----------- | -------------------- | ----------------------------------------------------------- |
179
+ | **`value`** | <code>boolean</code> | Whether the user confirmed (`true`) or cancelled (`false`). |
186
180
 
187
181
 
188
182
  #### PromptOptions
@@ -196,22 +190,22 @@ Show a message sheet dialog with an optional header logo, title, and message.
196
190
  | **`focusInput`** | <code>boolean</code> | Whether to automatically focus the input field and open the keyboard when the dialog appears. Note: On iOS, basic (non-fullscreen) dialogs always auto-focus the input field due to UIAlertController's built-in behavior. This option only takes effect on iOS when using fullscreen mode (`mode: 'fullscreen'`). On Android and web, this works for all modes. | <code>false</code> |
197
191
 
198
192
 
199
- #### SingleSelectResult
193
+ #### PromptResult
200
194
 
201
- | Prop | Type | Description |
202
- | --------------- | --------------------------- | --------------------------------------------------------- |
203
- | **`value`** | <code>string \| null</code> | The value of the selected option, or `null` if cancelled. |
204
- | **`cancelled`** | <code>boolean</code> | Whether the user cancelled the dialog. |
195
+ | Prop | Type | Description |
196
+ | --------------- | -------------------- | -------------------------------------------------------- |
197
+ | **`value`** | <code>string</code> | The text entered by the user. Empty string if cancelled. |
198
+ | **`cancelled`** | <code>boolean</code> | Whether the user cancelled the dialog. |
205
199
 
206
200
 
207
201
  #### SingleSelectOptions
208
202
 
209
- | Prop | Type | Description | Default |
210
- | ----------------------- | --------------------------- | ----------------------------------------- | --------------------- |
211
- | **`options`** | <code>SelectOption[]</code> | List of options to display for selection. | |
212
- | **`selectedValue`** | <code>string</code> | Value of the initially selected option. | |
213
- | **`okButtonTitle`** | <code>string</code> | Title for the confirmation button. | <code>"OK"</code> |
214
- | **`cancelButtonTitle`** | <code>string</code> | Title for the cancel button. | <code>"Cancel"</code> |
203
+ | Prop | Type | Description | Default |
204
+ | ----------------------- | ------------------- | ----------------------------------------- | --------------------- |
205
+ | **`options`** | <code>{}</code> | List of options to display for selection. | |
206
+ | **`selectedValue`** | <code>string</code> | Value of the initially selected option. | |
207
+ | **`okButtonTitle`** | <code>string</code> | Title for the confirmation button. | <code>"OK"</code> |
208
+ | **`cancelButtonTitle`** | <code>string</code> | Title for the cancel button. | <code>"Cancel"</code> |
215
209
 
216
210
 
217
211
  #### SelectOption
@@ -222,54 +216,55 @@ Show a message sheet dialog with an optional header logo, title, and message.
222
216
  | **`value`** | <code>string</code> | Value returned when the option is selected. |
223
217
 
224
218
 
225
- #### MultiSelectResult
219
+ #### SingleSelectResult
226
220
 
227
- | Prop | Type | Description |
228
- | --------------- | --------------------- | ------------------------------------------------------------- |
229
- | **`values`** | <code>string[]</code> | The values of the selected options. Empty array if cancelled. |
230
- | **`cancelled`** | <code>boolean</code> | Whether the user cancelled the dialog. |
221
+ | Prop | Type | Description |
222
+ | --------------- | --------------------------- | --------------------------------------------------------- |
223
+ | **`value`** | <code>string \| null</code> | The value of the selected option, or `null` if cancelled. |
224
+ | **`cancelled`** | <code>boolean</code> | Whether the user cancelled the dialog. |
231
225
 
232
226
 
233
227
  #### MultiSelectOptions
234
228
 
235
- | Prop | Type | Description | Default |
236
- | ----------------------- | --------------------------- | ----------------------------------------- | --------------------- |
237
- | **`options`** | <code>SelectOption[]</code> | List of options to display for selection. | |
238
- | **`selectedValues`** | <code>string[]</code> | Values of the initially selected options. | |
239
- | **`okButtonTitle`** | <code>string</code> | Title for the confirmation button. | <code>"OK"</code> |
240
- | **`cancelButtonTitle`** | <code>string</code> | Title for the cancel button. | <code>"Cancel"</code> |
229
+ | Prop | Type | Description | Default |
230
+ | ----------------------- | ------------------- | ----------------------------------------- | --------------------- |
231
+ | **`options`** | <code>{}</code> | List of options to display for selection. | |
232
+ | **`selectedValues`** | <code>{}</code> | Values of the initially selected options. | |
233
+ | **`okButtonTitle`** | <code>string</code> | Title for the confirmation button. | <code>"OK"</code> |
234
+ | **`cancelButtonTitle`** | <code>string</code> | Title for the cancel button. | <code>"Cancel"</code> |
241
235
 
242
236
 
243
- #### SheetResult
237
+ #### MultiSelectResult
244
238
 
245
- | Prop | Type | Description |
246
- | --------------- | -------------------- | ------------------------------------- |
247
- | **`confirmed`** | <code>boolean</code> | True if confirmed, false if cancelled |
239
+ | Prop | Type | Description |
240
+ | --------------- | -------------------- | ------------------------------------------------------------- |
241
+ | **`values`** | <code>{}</code> | The values of the selected options. Empty array if cancelled. |
242
+ | **`cancelled`** | <code>boolean</code> | Whether the user cancelled the dialog. |
248
243
 
249
244
 
250
245
  #### SheetOptions
251
246
 
252
- | Prop | Type | Description |
253
- | ------------------------ | ------------------------------------------------- | -------------------------------------------------------- |
254
- | **`headerLogo`** | <code>string</code> | Header logo - supports base64 data URL or HTTP/HTTPS URL |
255
- | **`title`** | <code>string</code> | Sheet title |
256
- | **`subtitle`** | <code>string</code> | Optional subtitle displayed between title and rows |
257
- | **`rows`** | <code>SheetRow[]</code> | Description rows |
258
- | **`confirmButtonTitle`** | <code>string</code> | Confirm button title |
259
- | **`cancelButtonTitle`** | <code>string</code> | Cancel button title |
260
- | **`mode`** | <code><a href="#dialogmode">DialogMode</a></code> | Dialog mode |
247
+ | Prop | Type | Description |
248
+ | ------------------------ | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
249
+ | **`headerLogo`** | <code>string</code> | Header logo. Supports: - PNG/JPEG/WebP/GIF as base64 data URL or HTTP/HTTPS URL - SVG as `data:image/svg+xml;base64,...`, `data:image/svg+xml,&lt;svg ...&gt;...` (URL-encoded or plain), or any HTTP/HTTPS URL ending in `.svg` |
250
+ | **`title`** | <code>string</code> | Sheet title |
251
+ | **`subtitle`** | <code>string</code> | Optional subtitle displayed between title and rows |
252
+ | **`rows`** | <code>{}</code> | Description rows |
253
+ | **`confirmButtonTitle`** | <code>string</code> | Confirm button title |
254
+ | **`cancelButtonTitle`** | <code>string</code> | Cancel button title |
255
+ | **`mode`** | <code><a href="#dialogmode">DialogMode</a></code> | Dialog mode |
261
256
 
262
257
 
263
258
  #### SheetRow
264
259
 
265
- | Prop | Type | Description |
266
- | ----------- | ------------------- | --------------------------------------------------------------- |
267
- | **`title`** | <code>string</code> | Row title (required) |
268
- | **`logo`** | <code>string</code> | Optional logo/icon - supports base64 data URL or HTTP/HTTPS URL |
269
- | **`value`** | <code>string</code> | Optional value displayed on the right |
260
+ | Prop | Type | Description |
261
+ | ----------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
262
+ | **`title`** | <code>string</code> | Row title (required) |
263
+ | **`logo`** | <code>string</code> | Optional logo/icon. Supports: - PNG/JPEG/WebP/GIF as base64 data URL or HTTP/HTTPS URL - SVG as `data:image/svg+xml;base64,...`, `data:image/svg+xml,&lt;svg ...&gt;...` (URL-encoded or plain), or any HTTP/HTTPS URL ending in `.svg` |
264
+ | **`value`** | <code>string</code> | Optional value displayed on the right |
270
265
 
271
266
 
272
- #### MessageSheetResult
267
+ #### SheetResult
273
268
 
274
269
  | Prop | Type | Description |
275
270
  | --------------- | -------------------- | ------------------------------------- |
@@ -278,15 +273,22 @@ Show a message sheet dialog with an optional header logo, title, and message.
278
273
 
279
274
  #### MessageSheetOptions
280
275
 
281
- | Prop | Type | Description |
282
- | ------------------------ | ------------------------------------------------- | -------------------------------------------------------- |
283
- | **`headerLogo`** | <code>string</code> | Header logo - supports base64 data URL or HTTP/HTTPS URL |
284
- | **`title`** | <code>string</code> | Sheet title |
285
- | **`subtitle`** | <code>string</code> | Optional subtitle displayed between title and message |
286
- | **`message`** | <code>string</code> | Message body displayed in the sheet |
287
- | **`confirmButtonTitle`** | <code>string</code> | Confirm button title |
288
- | **`cancelButtonTitle`** | <code>string</code> | Cancel button title |
289
- | **`mode`** | <code><a href="#dialogmode">DialogMode</a></code> | Dialog mode |
276
+ | Prop | Type | Description |
277
+ | ------------------------ | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
278
+ | **`headerLogo`** | <code>string</code> | Header logo. Supports: - PNG/JPEG/WebP/GIF as base64 data URL or HTTP/HTTPS URL - SVG as `data:image/svg+xml;base64,...`, `data:image/svg+xml,&lt;svg ...&gt;...` (URL-encoded or plain), or any HTTP/HTTPS URL ending in `.svg` |
279
+ | **`title`** | <code>string</code> | Sheet title |
280
+ | **`subtitle`** | <code>string</code> | Optional subtitle displayed between title and message |
281
+ | **`message`** | <code>string</code> | Message body displayed in the sheet |
282
+ | **`confirmButtonTitle`** | <code>string</code> | Confirm button title |
283
+ | **`cancelButtonTitle`** | <code>string</code> | Cancel button title |
284
+ | **`mode`** | <code><a href="#dialogmode">DialogMode</a></code> | Dialog mode |
285
+
286
+
287
+ #### MessageSheetResult
288
+
289
+ | Prop | Type | Description |
290
+ | --------------- | -------------------- | ------------------------------------- |
291
+ | **`confirmed`** | <code>boolean</code> | True if confirmed, false if cancelled |
290
292
 
291
293
 
292
294
  ### Type Aliases
@@ -53,6 +53,7 @@ dependencies {
53
53
  implementation project(':capacitor-android')
54
54
  implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
55
55
  implementation 'com.google.android.material:material:1.12.0'
56
+ implementation 'com.caverock:androidsvg-aar:1.4'
56
57
  testImplementation "junit:junit:$junitVersion"
57
58
  androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
58
59
  androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
@@ -7,7 +7,6 @@ import android.content.res.ColorStateList;
7
7
  import android.graphics.Bitmap;
8
8
  import android.graphics.BitmapFactory;
9
9
  import android.os.Bundle;
10
- import android.util.Base64;
11
10
  import android.util.TypedValue;
12
11
  import android.view.ContextThemeWrapper;
13
12
  import android.view.Gravity;
@@ -52,6 +51,8 @@ public class SheetBottomDialogFragment extends BottomSheetDialogFragment {
52
51
  private static final String ARG_SUBTITLE = "subtitle";
53
52
  private static final String ARG_IS_MESSAGE_SHEET = "isMessageSheet";
54
53
 
54
+ private static final Executor IMAGE_EXECUTOR = Executors.newCachedThreadPool();
55
+
55
56
  private ExtendedDialog.SheetCallback sheetCallback;
56
57
  private boolean dismissed = false;
57
58
  private Context themedContext;
@@ -706,28 +707,32 @@ public class SheetBottomDialogFragment extends BottomSheetDialogFragment {
706
707
  }
707
708
 
708
709
  private void loadImageAsync(ImageView imageView, String imageUrl) {
709
- Executor executor = Executors.newSingleThreadExecutor();
710
- executor.execute(() -> {
710
+ if (imageUrl == null || imageUrl.isEmpty()) return;
711
+ IMAGE_EXECUTOR.execute(() -> {
711
712
  try {
712
- Bitmap bitmap;
713
- if (imageUrl.startsWith("data:")) {
714
- String base64Data = imageUrl.substring(imageUrl.indexOf(",") + 1);
715
- byte[] decodedBytes = Base64.decode(base64Data, Base64.DEFAULT);
716
- bitmap = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length);
717
- } else {
718
- InputStream inputStream = new URL(imageUrl).openStream();
719
- bitmap = BitmapFactory.decodeStream(inputStream);
720
- inputStream.close();
721
- }
713
+ Bitmap bitmap = decodeImage(imageUrl, imageView);
722
714
  if (bitmap != null) {
723
715
  imageView.post(() -> imageView.setImageBitmap(bitmap));
724
716
  }
725
717
  } catch (Exception e) {
726
- // Silently fail - image won't be displayed
718
+ android.util.Log.w("ExtendedDialog", "Failed to load image: " + e.getMessage());
727
719
  }
728
720
  });
729
721
  }
730
722
 
723
+ private static Bitmap decodeImage(String imageUrl, ImageView imageView) throws Exception {
724
+ if (SvgImageLoader.isSvgSource(imageUrl)) {
725
+ return SvgImageLoader.render(imageUrl, imageView);
726
+ }
727
+ if (SvgImageLoader.isDataUrl(imageUrl)) {
728
+ byte[] decodedBytes = SvgImageLoader.decodeDataUrl(imageUrl);
729
+ return BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length);
730
+ }
731
+ try (InputStream inputStream = new URL(imageUrl).openStream()) {
732
+ return BitmapFactory.decodeStream(inputStream);
733
+ }
734
+ }
735
+
731
736
  private void handleConfirm() {
732
737
  if (dismissed) return;
733
738
  dismissed = true;
@@ -0,0 +1,101 @@
1
+ package com.albermonte.extendeddialog;
2
+
3
+ import android.graphics.Bitmap;
4
+ import android.graphics.Canvas;
5
+ import android.net.Uri;
6
+ import android.util.Base64;
7
+ import android.view.ViewGroup;
8
+ import android.widget.ImageView;
9
+ import com.caverock.androidsvg.SVG;
10
+ import com.caverock.androidsvg.SVGParseException;
11
+ import java.io.ByteArrayInputStream;
12
+ import java.io.IOException;
13
+ import java.io.InputStream;
14
+ import java.net.URL;
15
+ import java.net.URLDecoder;
16
+ import java.nio.charset.StandardCharsets;
17
+ import java.util.Locale;
18
+
19
+ final class SvgImageLoader {
20
+
21
+ private static final String SVG_DATA_URL_PREFIX = "data:image/svg+xml";
22
+ private static final String DATA_URL_PREFIX = "data:";
23
+ private static final String SVG_SUFFIX = ".svg";
24
+
25
+ private SvgImageLoader() {}
26
+
27
+ static boolean isDataUrl(String source) {
28
+ return source != null && source.regionMatches(true, 0, DATA_URL_PREFIX, 0, DATA_URL_PREFIX.length());
29
+ }
30
+
31
+ static boolean isSvgSource(String source) {
32
+ if (source == null) return false;
33
+ if (source.regionMatches(true, 0, SVG_DATA_URL_PREFIX, 0, SVG_DATA_URL_PREFIX.length())) return true;
34
+ Uri uri = Uri.parse(source);
35
+ String scheme = uri.getScheme();
36
+ if (scheme == null || (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https"))) {
37
+ return false;
38
+ }
39
+ String path = uri.getPath();
40
+ return path != null && path.toLowerCase(Locale.ROOT).endsWith(SVG_SUFFIX);
41
+ }
42
+
43
+ /** Supports `;base64` and percent-encoded payloads. */
44
+ static byte[] decodeDataUrl(String source) throws IOException {
45
+ int comma = source.indexOf(',');
46
+ if (comma < 0) throw new IOException("Malformed data URL");
47
+ String header = source.substring(0, comma);
48
+ String payload = source.substring(comma + 1);
49
+ if (header.toLowerCase(Locale.ROOT).contains(";base64")) {
50
+ return Base64.decode(payload, Base64.DEFAULT);
51
+ }
52
+ return URLDecoder.decode(payload, StandardCharsets.UTF_8.name()).getBytes(StandardCharsets.UTF_8);
53
+ }
54
+
55
+ /** Sizes Bitmap to {@code targetView}'s layout params (or 64dp default). */
56
+ static Bitmap render(String source, ImageView targetView) throws SVGParseException, IOException {
57
+ SVG svg = parse(source);
58
+ ViewGroup.LayoutParams params = targetView.getLayoutParams();
59
+ int width = params != null ? params.width : 0;
60
+ int height = params != null ? params.height : 0;
61
+ if (width <= 0 || height <= 0) {
62
+ float density = targetView.getResources().getDisplayMetrics().density;
63
+ int fallback = (int) (64 * density);
64
+ if (width <= 0) width = fallback;
65
+ if (height <= 0) height = fallback;
66
+ }
67
+
68
+ // AndroidSVG only scales through the viewBox. Without one, resizing the
69
+ // document clips the drawing to its top-left corner, so derive it from
70
+ // the declared width/height.
71
+ if (svg.getDocumentViewBox() == null) {
72
+ float docWidth = svg.getDocumentWidth();
73
+ float docHeight = svg.getDocumentHeight();
74
+ if (docWidth > 0 && docHeight > 0) {
75
+ svg.setDocumentViewBox(0, 0, docWidth, docHeight);
76
+ }
77
+ }
78
+
79
+ // Rescale via viewBox + preserveAspectRatio so small-viewport SVGs
80
+ // (e.g. width="20") aren't rendered tiny in the top-left corner.
81
+ svg.setDocumentWidth(width);
82
+ svg.setDocumentHeight(height);
83
+
84
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
85
+ Canvas canvas = new Canvas(bitmap);
86
+ svg.renderToCanvas(canvas);
87
+ return bitmap;
88
+ }
89
+
90
+ private static SVG parse(String source) throws SVGParseException, IOException {
91
+ if (isDataUrl(source)) {
92
+ byte[] bytes = decodeDataUrl(source);
93
+ try (InputStream in = new ByteArrayInputStream(bytes)) {
94
+ return SVG.getFromInputStream(in);
95
+ }
96
+ }
97
+ try (InputStream in = new URL(source).openStream()) {
98
+ return SVG.getFromInputStream(in);
99
+ }
100
+ }
101
+ }
package/dist/docs.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "methods": [
8
8
  {
9
9
  "name": "alert",
10
- "signature": "(options: AlertOptions) => Promise<void>",
10
+ "signature": "(options: AlertOptions) => any",
11
11
  "parameters": [
12
12
  {
13
13
  "name": "options",
@@ -15,7 +15,7 @@
15
15
  "type": "AlertOptions"
16
16
  }
17
17
  ],
18
- "returns": "Promise<void>",
18
+ "returns": "any",
19
19
  "tags": [
20
20
  {
21
21
  "name": "param",
@@ -30,7 +30,7 @@
30
30
  },
31
31
  {
32
32
  "name": "confirm",
33
- "signature": "(options: ConfirmOptions) => Promise<ConfirmResult>",
33
+ "signature": "(options: ConfirmOptions) => any",
34
34
  "parameters": [
35
35
  {
36
36
  "name": "options",
@@ -38,7 +38,7 @@
38
38
  "type": "ConfirmOptions"
39
39
  }
40
40
  ],
41
- "returns": "Promise<ConfirmResult>",
41
+ "returns": "any",
42
42
  "tags": [
43
43
  {
44
44
  "name": "param",
@@ -51,14 +51,14 @@
51
51
  ],
52
52
  "docs": "Show a confirmation dialog with OK and Cancel buttons.",
53
53
  "complexTypes": [
54
- "ConfirmResult",
55
- "ConfirmOptions"
54
+ "ConfirmOptions",
55
+ "ConfirmResult"
56
56
  ],
57
57
  "slug": "confirm"
58
58
  },
59
59
  {
60
60
  "name": "prompt",
61
- "signature": "(options: PromptOptions) => Promise<PromptResult>",
61
+ "signature": "(options: PromptOptions) => any",
62
62
  "parameters": [
63
63
  {
64
64
  "name": "options",
@@ -66,7 +66,7 @@
66
66
  "type": "PromptOptions"
67
67
  }
68
68
  ],
69
- "returns": "Promise<PromptResult>",
69
+ "returns": "any",
70
70
  "tags": [
71
71
  {
72
72
  "name": "param",
@@ -79,14 +79,14 @@
79
79
  ],
80
80
  "docs": "Show a prompt dialog with a text input field.",
81
81
  "complexTypes": [
82
- "PromptResult",
83
- "PromptOptions"
82
+ "PromptOptions",
83
+ "PromptResult"
84
84
  ],
85
85
  "slug": "prompt"
86
86
  },
87
87
  {
88
88
  "name": "singleSelect",
89
- "signature": "(options: SingleSelectOptions) => Promise<SingleSelectResult>",
89
+ "signature": "(options: SingleSelectOptions) => any",
90
90
  "parameters": [
91
91
  {
92
92
  "name": "options",
@@ -94,7 +94,7 @@
94
94
  "type": "SingleSelectOptions"
95
95
  }
96
96
  ],
97
- "returns": "Promise<SingleSelectResult>",
97
+ "returns": "any",
98
98
  "tags": [
99
99
  {
100
100
  "name": "param",
@@ -107,14 +107,14 @@
107
107
  ],
108
108
  "docs": "Show a single-select dialog where the user picks one option.",
109
109
  "complexTypes": [
110
- "SingleSelectResult",
111
- "SingleSelectOptions"
110
+ "SingleSelectOptions",
111
+ "SingleSelectResult"
112
112
  ],
113
113
  "slug": "singleselect"
114
114
  },
115
115
  {
116
116
  "name": "multiSelect",
117
- "signature": "(options: MultiSelectOptions) => Promise<MultiSelectResult>",
117
+ "signature": "(options: MultiSelectOptions) => any",
118
118
  "parameters": [
119
119
  {
120
120
  "name": "options",
@@ -122,7 +122,7 @@
122
122
  "type": "MultiSelectOptions"
123
123
  }
124
124
  ],
125
- "returns": "Promise<MultiSelectResult>",
125
+ "returns": "any",
126
126
  "tags": [
127
127
  {
128
128
  "name": "param",
@@ -135,14 +135,14 @@
135
135
  ],
136
136
  "docs": "Show a multi-select dialog where the user picks one or more options.",
137
137
  "complexTypes": [
138
- "MultiSelectResult",
139
- "MultiSelectOptions"
138
+ "MultiSelectOptions",
139
+ "MultiSelectResult"
140
140
  ],
141
141
  "slug": "multiselect"
142
142
  },
143
143
  {
144
144
  "name": "sheet",
145
- "signature": "(options: SheetOptions) => Promise<SheetResult>",
145
+ "signature": "(options: SheetOptions) => any",
146
146
  "parameters": [
147
147
  {
148
148
  "name": "options",
@@ -150,7 +150,7 @@
150
150
  "type": "SheetOptions"
151
151
  }
152
152
  ],
153
- "returns": "Promise<SheetResult>",
153
+ "returns": "any",
154
154
  "tags": [
155
155
  {
156
156
  "name": "param",
@@ -163,14 +163,14 @@
163
163
  ],
164
164
  "docs": "Show a sheet dialog with a header, title, and structured data rows.",
165
165
  "complexTypes": [
166
- "SheetResult",
167
- "SheetOptions"
166
+ "SheetOptions",
167
+ "SheetResult"
168
168
  ],
169
169
  "slug": "sheet"
170
170
  },
171
171
  {
172
172
  "name": "messageSheet",
173
- "signature": "(options: MessageSheetOptions) => Promise<MessageSheetResult>",
173
+ "signature": "(options: MessageSheetOptions) => any",
174
174
  "parameters": [
175
175
  {
176
176
  "name": "options",
@@ -178,7 +178,7 @@
178
178
  "type": "MessageSheetOptions"
179
179
  }
180
180
  ],
181
- "returns": "Promise<MessageSheetResult>",
181
+ "returns": "any",
182
182
  "tags": [
183
183
  {
184
184
  "name": "param",
@@ -191,8 +191,8 @@
191
191
  ],
192
192
  "docs": "Show a message sheet dialog with an optional header logo, title, and message.",
193
193
  "complexTypes": [
194
- "MessageSheetResult",
195
- "MessageSheetOptions"
194
+ "MessageSheetOptions",
195
+ "MessageSheetResult"
196
196
  ],
197
197
  "slug": "messagesheet"
198
198
  }
@@ -221,22 +221,6 @@
221
221
  }
222
222
  ]
223
223
  },
224
- {
225
- "name": "ConfirmResult",
226
- "slug": "confirmresult",
227
- "docs": "",
228
- "tags": [],
229
- "methods": [],
230
- "properties": [
231
- {
232
- "name": "value",
233
- "tags": [],
234
- "docs": "Whether the user confirmed (`true`) or cancelled (`false`).",
235
- "complexTypes": [],
236
- "type": "boolean"
237
- }
238
- ]
239
- },
240
224
  {
241
225
  "name": "ConfirmOptions",
242
226
  "slug": "confirmoptions",
@@ -271,8 +255,8 @@
271
255
  ]
272
256
  },
273
257
  {
274
- "name": "PromptResult",
275
- "slug": "promptresult",
258
+ "name": "ConfirmResult",
259
+ "slug": "confirmresult",
276
260
  "docs": "",
277
261
  "tags": [],
278
262
  "methods": [],
@@ -280,14 +264,7 @@
280
264
  {
281
265
  "name": "value",
282
266
  "tags": [],
283
- "docs": "The text entered by the user. Empty string if cancelled.",
284
- "complexTypes": [],
285
- "type": "string"
286
- },
287
- {
288
- "name": "cancelled",
289
- "tags": [],
290
- "docs": "Whether the user cancelled the dialog.",
267
+ "docs": "Whether the user confirmed (`true`) or cancelled (`false`).",
291
268
  "complexTypes": [],
292
269
  "type": "boolean"
293
270
  }
@@ -353,8 +330,8 @@
353
330
  ]
354
331
  },
355
332
  {
356
- "name": "SingleSelectResult",
357
- "slug": "singleselectresult",
333
+ "name": "PromptResult",
334
+ "slug": "promptresult",
358
335
  "docs": "",
359
336
  "tags": [],
360
337
  "methods": [],
@@ -362,9 +339,9 @@
362
339
  {
363
340
  "name": "value",
364
341
  "tags": [],
365
- "docs": "The value of the selected option, or `null` if cancelled.",
342
+ "docs": "The text entered by the user. Empty string if cancelled.",
366
343
  "complexTypes": [],
367
- "type": "string | null"
344
+ "type": "string"
368
345
  },
369
346
  {
370
347
  "name": "cancelled",
@@ -389,7 +366,7 @@
389
366
  "complexTypes": [
390
367
  "SelectOption"
391
368
  ],
392
- "type": "SelectOption[]"
369
+ "type": "{}"
393
370
  },
394
371
  {
395
372
  "name": "selectedValue",
@@ -448,18 +425,18 @@
448
425
  ]
449
426
  },
450
427
  {
451
- "name": "MultiSelectResult",
452
- "slug": "multiselectresult",
428
+ "name": "SingleSelectResult",
429
+ "slug": "singleselectresult",
453
430
  "docs": "",
454
431
  "tags": [],
455
432
  "methods": [],
456
433
  "properties": [
457
434
  {
458
- "name": "values",
435
+ "name": "value",
459
436
  "tags": [],
460
- "docs": "The values of the selected options. Empty array if cancelled.",
437
+ "docs": "The value of the selected option, or `null` if cancelled.",
461
438
  "complexTypes": [],
462
- "type": "string[]"
439
+ "type": "string | null"
463
440
  },
464
441
  {
465
442
  "name": "cancelled",
@@ -484,14 +461,14 @@
484
461
  "complexTypes": [
485
462
  "SelectOption"
486
463
  ],
487
- "type": "SelectOption[]"
464
+ "type": "{}"
488
465
  },
489
466
  {
490
467
  "name": "selectedValues",
491
468
  "tags": [],
492
469
  "docs": "Values of the initially selected options.",
493
470
  "complexTypes": [],
494
- "type": "string[] | undefined"
471
+ "type": "{} | undefined"
495
472
  },
496
473
  {
497
474
  "name": "okButtonTitle",
@@ -520,16 +497,23 @@
520
497
  ]
521
498
  },
522
499
  {
523
- "name": "SheetResult",
524
- "slug": "sheetresult",
500
+ "name": "MultiSelectResult",
501
+ "slug": "multiselectresult",
525
502
  "docs": "",
526
503
  "tags": [],
527
504
  "methods": [],
528
505
  "properties": [
529
506
  {
530
- "name": "confirmed",
507
+ "name": "values",
531
508
  "tags": [],
532
- "docs": "True if confirmed, false if cancelled",
509
+ "docs": "The values of the selected options. Empty array if cancelled.",
510
+ "complexTypes": [],
511
+ "type": "{}"
512
+ },
513
+ {
514
+ "name": "cancelled",
515
+ "tags": [],
516
+ "docs": "Whether the user cancelled the dialog.",
533
517
  "complexTypes": [],
534
518
  "type": "boolean"
535
519
  }
@@ -545,7 +529,7 @@
545
529
  {
546
530
  "name": "headerLogo",
547
531
  "tags": [],
548
- "docs": "Header logo - supports base64 data URL or HTTP/HTTPS URL",
532
+ "docs": "Header logo. Supports:\n- PNG/JPEG/WebP/GIF as base64 data URL or HTTP/HTTPS URL\n- SVG as `data:image/svg+xml;base64,...`, `data:image/svg+xml,<svg ...>...` (URL-encoded or plain), or any HTTP/HTTPS URL ending in `.svg`",
549
533
  "complexTypes": [],
550
534
  "type": "string | undefined"
551
535
  },
@@ -570,7 +554,7 @@
570
554
  "complexTypes": [
571
555
  "SheetRow"
572
556
  ],
573
- "type": "SheetRow[]"
557
+ "type": "{}"
574
558
  },
575
559
  {
576
560
  "name": "confirmButtonTitle",
@@ -614,7 +598,7 @@
614
598
  {
615
599
  "name": "logo",
616
600
  "tags": [],
617
- "docs": "Optional logo/icon - supports base64 data URL or HTTP/HTTPS URL",
601
+ "docs": "Optional logo/icon. Supports:\n- PNG/JPEG/WebP/GIF as base64 data URL or HTTP/HTTPS URL\n- SVG as `data:image/svg+xml;base64,...`, `data:image/svg+xml,<svg ...>...` (URL-encoded or plain), or any HTTP/HTTPS URL ending in `.svg`",
618
602
  "complexTypes": [],
619
603
  "type": "string | undefined"
620
604
  },
@@ -628,8 +612,8 @@
628
612
  ]
629
613
  },
630
614
  {
631
- "name": "MessageSheetResult",
632
- "slug": "messagesheetresult",
615
+ "name": "SheetResult",
616
+ "slug": "sheetresult",
633
617
  "docs": "",
634
618
  "tags": [],
635
619
  "methods": [],
@@ -653,7 +637,7 @@
653
637
  {
654
638
  "name": "headerLogo",
655
639
  "tags": [],
656
- "docs": "Header logo - supports base64 data URL or HTTP/HTTPS URL",
640
+ "docs": "Header logo. Supports:\n- PNG/JPEG/WebP/GIF as base64 data URL or HTTP/HTTPS URL\n- SVG as `data:image/svg+xml;base64,...`, `data:image/svg+xml,<svg ...>...` (URL-encoded or plain), or any HTTP/HTTPS URL ending in `.svg`",
657
641
  "complexTypes": [],
658
642
  "type": "string | undefined"
659
643
  },
@@ -702,6 +686,22 @@
702
686
  "type": "DialogMode"
703
687
  }
704
688
  ]
689
+ },
690
+ {
691
+ "name": "MessageSheetResult",
692
+ "slug": "messagesheetresult",
693
+ "docs": "",
694
+ "tags": [],
695
+ "methods": [],
696
+ "properties": [
697
+ {
698
+ "name": "confirmed",
699
+ "tags": [],
700
+ "docs": "True if confirmed, false if cancelled",
701
+ "complexTypes": [],
702
+ "type": "boolean"
703
+ }
704
+ ]
705
705
  }
706
706
  ],
707
707
  "enums": [],
@@ -260,13 +260,21 @@ export interface MultiSelectResult {
260
260
  export interface SheetRow {
261
261
  /** Row title (required) */
262
262
  title: string;
263
- /** Optional logo/icon - supports base64 data URL or HTTP/HTTPS URL */
263
+ /**
264
+ * Optional logo/icon. Supports:
265
+ * - PNG/JPEG/WebP/GIF as base64 data URL or HTTP/HTTPS URL
266
+ * - SVG as `data:image/svg+xml;base64,...`, `data:image/svg+xml,<svg ...>...` (URL-encoded or plain), or any HTTP/HTTPS URL ending in `.svg`
267
+ */
264
268
  logo?: string;
265
269
  /** Optional value displayed on the right */
266
270
  value?: string;
267
271
  }
268
272
  export interface SheetOptions extends DialogStyleOptions {
269
- /** Header logo - supports base64 data URL or HTTP/HTTPS URL */
273
+ /**
274
+ * Header logo. Supports:
275
+ * - PNG/JPEG/WebP/GIF as base64 data URL or HTTP/HTTPS URL
276
+ * - SVG as `data:image/svg+xml;base64,...`, `data:image/svg+xml,<svg ...>...` (URL-encoded or plain), or any HTTP/HTTPS URL ending in `.svg`
277
+ */
270
278
  headerLogo?: string;
271
279
  /** Sheet title */
272
280
  title: string;
@@ -282,7 +290,11 @@ export interface SheetOptions extends DialogStyleOptions {
282
290
  mode?: DialogMode;
283
291
  }
284
292
  export interface MessageSheetOptions extends DialogStyleOptions {
285
- /** Header logo - supports base64 data URL or HTTP/HTTPS URL */
293
+ /**
294
+ * Header logo. Supports:
295
+ * - PNG/JPEG/WebP/GIF as base64 data URL or HTTP/HTTPS URL
296
+ * - SVG as `data:image/svg+xml;base64,...`, `data:image/svg+xml,<svg ...>...` (URL-encoded or plain), or any HTTP/HTTPS URL ending in `.svg`
297
+ */
286
298
  headerLogo?: string;
287
299
  /** Sheet title */
288
300
  title: string;
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Dialog presentation mode.\n *\n * - `'basic'` — Standard modal dialog (default)\n * - `'fullscreen'` — Full-screen dialog presentation\n */\nexport type DialogMode = 'basic' | 'fullscreen';\n\nexport interface DialogStyleOptions {\n /**\n * Color for the OK/primary button (hex format, e.g., \"#FF5722\")\n */\n buttonColor?: string;\n /**\n * Color for the cancel button (hex format, e.g., \"#757575\")\n */\n cancelButtonColor?: string;\n /**\n * Color for the title text (hex format, e.g., \"#000000\")\n */\n titleColor?: string;\n /**\n * Color for the message text (hex format, e.g., \"#666666\")\n */\n messageColor?: string;\n /**\n * Background color of the dialog (hex format, e.g., \"#FFFFFF\")\n */\n backgroundColor?: string;\n /**\n * Font size for the title text in sp/pt (e.g., 20)\n */\n titleFontSize?: number;\n /**\n * Font size for the message text in sp/pt (e.g., 16)\n */\n messageFontSize?: number;\n /**\n * Font size for the button text in sp/pt (e.g., 14)\n */\n buttonFontSize?: number;\n /**\n * Vertical spacing between dialog content and the confirm/cancel buttons in dp/pt/px (e.g., 16).\n * Only applies to fullscreen and sheet modes.\n *\n * @example\n * // Fullscreen confirm with extra spacing between content and buttons\n * await ExtendedDialog.confirm({\n * title: 'Delete Account',\n * message: 'This action cannot be undone.',\n * mode: 'fullscreen',\n * contentButtonSpacing: 32,\n * });\n *\n * @example\n * // Sheet with tight spacing\n * await ExtendedDialog.sheet({\n * title: 'Order Summary',\n * rows: [{ title: 'Total', value: '$9.99' }],\n * contentButtonSpacing: 8,\n * });\n */\n contentButtonSpacing?: number;\n /**\n * Corner radius for the header logo in sheet dialogs (in dp/pt/px).\n * - Positive number: specific radius (e.g., 12)\n * - 0: square corners (no rounding)\n * - -1: full circle (radius = half of logo size)\n *\n * @default 8\n *\n * @example\n * // Custom rounded corners (12px)\n * await ExtendedDialog.sheet({\n * title: 'Settings',\n * headerLogo: 'https://example.com/logo.png',\n * headerLogoCornerRadius: 12,\n * rows: [],\n * });\n *\n * @example\n * // Full circle logo\n * await ExtendedDialog.sheet({\n * title: 'Profile',\n * headerLogo: 'https://example.com/avatar.png',\n * headerLogoCornerRadius: -1,\n * rows: [],\n * });\n *\n * @example\n * // Square logo (no rounding)\n * await ExtendedDialog.sheet({\n * title: 'Document',\n * headerLogo: 'https://example.com/icon.png',\n * headerLogoCornerRadius: 0,\n * rows: [],\n * });\n */\n headerLogoCornerRadius?: number;\n}\n\nexport interface BaseDialogOptions extends DialogStyleOptions {\n /**\n * Title displayed at the top of the dialog.\n */\n title?: string;\n /**\n * Message body displayed in the dialog.\n */\n message: string;\n /**\n * Presentation mode for the dialog.\n *\n * @default 'basic'\n */\n mode?: DialogMode;\n}\n\nexport interface AlertOptions extends BaseDialogOptions {\n /**\n * Title for the dismiss button.\n *\n * @default \"OK\"\n */\n buttonTitle?: string;\n}\n\nexport interface ConfirmOptions extends BaseDialogOptions {\n /**\n * Title for the confirmation button.\n *\n * @default \"OK\"\n */\n okButtonTitle?: string;\n /**\n * Title for the cancel button.\n *\n * @default \"Cancel\"\n */\n cancelButtonTitle?: string;\n}\n\nexport interface PromptOptions extends BaseDialogOptions {\n /**\n * Title for the confirmation button.\n *\n * @default \"OK\"\n */\n okButtonTitle?: string;\n /**\n * Title for the cancel button.\n *\n * @default \"Cancel\"\n */\n cancelButtonTitle?: string;\n /**\n * Placeholder text shown in the input field when empty.\n */\n inputPlaceholder?: string;\n /**\n * Pre-filled value for the input field.\n */\n inputText?: string;\n /**\n * Whether to automatically focus the input field and open the keyboard when the dialog appears.\n *\n * Note: On iOS, basic (non-fullscreen) dialogs always auto-focus the input field due to\n * UIAlertController's built-in behavior. This option only takes effect on iOS when using\n * fullscreen mode (`mode: 'fullscreen'`). On Android and web, this works for all modes.\n *\n * @default false\n */\n focusInput?: boolean;\n}\n\nexport interface SelectOption {\n /**\n * Display text for the option.\n */\n label: string;\n /**\n * Value returned when the option is selected.\n */\n value: string;\n}\n\nexport interface SingleSelectOptions extends BaseDialogOptions {\n /**\n * List of options to display for selection.\n */\n options: SelectOption[];\n /**\n * Value of the initially selected option.\n */\n selectedValue?: string;\n /**\n * Title for the confirmation button.\n *\n * @default \"OK\"\n */\n okButtonTitle?: string;\n /**\n * Title for the cancel button.\n *\n * @default \"Cancel\"\n */\n cancelButtonTitle?: string;\n}\n\nexport interface MultiSelectOptions extends BaseDialogOptions {\n /**\n * List of options to display for selection.\n */\n options: SelectOption[];\n /**\n * Values of the initially selected options.\n */\n selectedValues?: string[];\n /**\n * Title for the confirmation button.\n *\n * @default \"OK\"\n */\n okButtonTitle?: string;\n /**\n * Title for the cancel button.\n *\n * @default \"Cancel\"\n */\n cancelButtonTitle?: string;\n}\n\nexport interface ConfirmResult {\n /**\n * Whether the user confirmed (`true`) or cancelled (`false`).\n */\n value: boolean;\n}\n\nexport interface PromptResult {\n /**\n * The text entered by the user. Empty string if cancelled.\n */\n value: string;\n /**\n * Whether the user cancelled the dialog.\n */\n cancelled: boolean;\n}\n\nexport interface SingleSelectResult {\n /**\n * The value of the selected option, or `null` if cancelled.\n */\n value: string | null;\n /**\n * Whether the user cancelled the dialog.\n */\n cancelled: boolean;\n}\n\nexport interface MultiSelectResult {\n /**\n * The values of the selected options. Empty array if cancelled.\n */\n values: string[];\n /**\n * Whether the user cancelled the dialog.\n */\n cancelled: boolean;\n}\n\nexport interface SheetRow {\n /** Row title (required) */\n title: string;\n /** Optional logo/icon - supports base64 data URL or HTTP/HTTPS URL */\n logo?: string;\n /** Optional value displayed on the right */\n value?: string;\n}\n\nexport interface SheetOptions extends DialogStyleOptions {\n /** Header logo - supports base64 data URL or HTTP/HTTPS URL */\n headerLogo?: string;\n /** Sheet title */\n title: string;\n /** Optional subtitle displayed between title and rows */\n subtitle?: string;\n /** Description rows */\n rows: SheetRow[];\n /** Confirm button title */\n confirmButtonTitle?: string; // default: \"Confirm\"\n /** Cancel button title */\n cancelButtonTitle?: string; // default: \"Cancel\"\n /** Dialog mode */\n mode?: DialogMode;\n}\n\nexport interface MessageSheetOptions extends DialogStyleOptions {\n /** Header logo - supports base64 data URL or HTTP/HTTPS URL */\n headerLogo?: string;\n /** Sheet title */\n title: string;\n /** Optional subtitle displayed between title and message */\n subtitle?: string;\n /** Message body displayed in the sheet */\n message: string;\n /** Confirm button title */\n confirmButtonTitle?: string; // default: \"Confirm\"\n /** Cancel button title */\n cancelButtonTitle?: string; // default: \"Cancel\"\n /** Dialog mode */\n mode?: DialogMode;\n}\n\nexport interface SheetResult {\n /** True if confirmed, false if cancelled */\n confirmed: boolean;\n}\n\nexport interface MessageSheetResult {\n /** True if confirmed, false if cancelled */\n confirmed: boolean;\n}\n\nexport interface ExtendedDialogPlugin {\n /**\n * Show an alert dialog with a single dismiss button.\n *\n * @param options - Alert configuration options.\n */\n alert(options: AlertOptions): Promise<void>;\n /**\n * Show a confirmation dialog with OK and Cancel buttons.\n *\n * @param options - Confirm configuration options.\n * @returns The user's confirmation choice.\n */\n confirm(options: ConfirmOptions): Promise<ConfirmResult>;\n /**\n * Show a prompt dialog with a text input field.\n *\n * @param options - Prompt configuration options.\n * @returns The entered text and whether the dialog was cancelled.\n */\n prompt(options: PromptOptions): Promise<PromptResult>;\n /**\n * Show a single-select dialog where the user picks one option.\n *\n * @param options - Single select configuration options.\n * @returns The selected value and whether the dialog was cancelled.\n */\n singleSelect(options: SingleSelectOptions): Promise<SingleSelectResult>;\n /**\n * Show a multi-select dialog where the user picks one or more options.\n *\n * @param options - Multi select configuration options.\n * @returns The selected values and whether the dialog was cancelled.\n */\n multiSelect(options: MultiSelectOptions): Promise<MultiSelectResult>;\n /**\n * Show a sheet dialog with a header, title, and structured data rows.\n *\n * @param options - Sheet configuration options.\n * @returns Whether the user confirmed or cancelled the sheet.\n */\n sheet(options: SheetOptions): Promise<SheetResult>;\n\n /**\n * Show a message sheet dialog with an optional header logo, title, and message.\n *\n * @param options - Message sheet configuration options.\n * @returns Whether the user confirmed or cancelled the sheet.\n */\n messageSheet(options: MessageSheetOptions): Promise<MessageSheetResult>;\n}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Dialog presentation mode.\n *\n * - `'basic'` — Standard modal dialog (default)\n * - `'fullscreen'` — Full-screen dialog presentation\n */\nexport type DialogMode = 'basic' | 'fullscreen';\n\nexport interface DialogStyleOptions {\n /**\n * Color for the OK/primary button (hex format, e.g., \"#FF5722\")\n */\n buttonColor?: string;\n /**\n * Color for the cancel button (hex format, e.g., \"#757575\")\n */\n cancelButtonColor?: string;\n /**\n * Color for the title text (hex format, e.g., \"#000000\")\n */\n titleColor?: string;\n /**\n * Color for the message text (hex format, e.g., \"#666666\")\n */\n messageColor?: string;\n /**\n * Background color of the dialog (hex format, e.g., \"#FFFFFF\")\n */\n backgroundColor?: string;\n /**\n * Font size for the title text in sp/pt (e.g., 20)\n */\n titleFontSize?: number;\n /**\n * Font size for the message text in sp/pt (e.g., 16)\n */\n messageFontSize?: number;\n /**\n * Font size for the button text in sp/pt (e.g., 14)\n */\n buttonFontSize?: number;\n /**\n * Vertical spacing between dialog content and the confirm/cancel buttons in dp/pt/px (e.g., 16).\n * Only applies to fullscreen and sheet modes.\n *\n * @example\n * // Fullscreen confirm with extra spacing between content and buttons\n * await ExtendedDialog.confirm({\n * title: 'Delete Account',\n * message: 'This action cannot be undone.',\n * mode: 'fullscreen',\n * contentButtonSpacing: 32,\n * });\n *\n * @example\n * // Sheet with tight spacing\n * await ExtendedDialog.sheet({\n * title: 'Order Summary',\n * rows: [{ title: 'Total', value: '$9.99' }],\n * contentButtonSpacing: 8,\n * });\n */\n contentButtonSpacing?: number;\n /**\n * Corner radius for the header logo in sheet dialogs (in dp/pt/px).\n * - Positive number: specific radius (e.g., 12)\n * - 0: square corners (no rounding)\n * - -1: full circle (radius = half of logo size)\n *\n * @default 8\n *\n * @example\n * // Custom rounded corners (12px)\n * await ExtendedDialog.sheet({\n * title: 'Settings',\n * headerLogo: 'https://example.com/logo.png',\n * headerLogoCornerRadius: 12,\n * rows: [],\n * });\n *\n * @example\n * // Full circle logo\n * await ExtendedDialog.sheet({\n * title: 'Profile',\n * headerLogo: 'https://example.com/avatar.png',\n * headerLogoCornerRadius: -1,\n * rows: [],\n * });\n *\n * @example\n * // Square logo (no rounding)\n * await ExtendedDialog.sheet({\n * title: 'Document',\n * headerLogo: 'https://example.com/icon.png',\n * headerLogoCornerRadius: 0,\n * rows: [],\n * });\n */\n headerLogoCornerRadius?: number;\n}\n\nexport interface BaseDialogOptions extends DialogStyleOptions {\n /**\n * Title displayed at the top of the dialog.\n */\n title?: string;\n /**\n * Message body displayed in the dialog.\n */\n message: string;\n /**\n * Presentation mode for the dialog.\n *\n * @default 'basic'\n */\n mode?: DialogMode;\n}\n\nexport interface AlertOptions extends BaseDialogOptions {\n /**\n * Title for the dismiss button.\n *\n * @default \"OK\"\n */\n buttonTitle?: string;\n}\n\nexport interface ConfirmOptions extends BaseDialogOptions {\n /**\n * Title for the confirmation button.\n *\n * @default \"OK\"\n */\n okButtonTitle?: string;\n /**\n * Title for the cancel button.\n *\n * @default \"Cancel\"\n */\n cancelButtonTitle?: string;\n}\n\nexport interface PromptOptions extends BaseDialogOptions {\n /**\n * Title for the confirmation button.\n *\n * @default \"OK\"\n */\n okButtonTitle?: string;\n /**\n * Title for the cancel button.\n *\n * @default \"Cancel\"\n */\n cancelButtonTitle?: string;\n /**\n * Placeholder text shown in the input field when empty.\n */\n inputPlaceholder?: string;\n /**\n * Pre-filled value for the input field.\n */\n inputText?: string;\n /**\n * Whether to automatically focus the input field and open the keyboard when the dialog appears.\n *\n * Note: On iOS, basic (non-fullscreen) dialogs always auto-focus the input field due to\n * UIAlertController's built-in behavior. This option only takes effect on iOS when using\n * fullscreen mode (`mode: 'fullscreen'`). On Android and web, this works for all modes.\n *\n * @default false\n */\n focusInput?: boolean;\n}\n\nexport interface SelectOption {\n /**\n * Display text for the option.\n */\n label: string;\n /**\n * Value returned when the option is selected.\n */\n value: string;\n}\n\nexport interface SingleSelectOptions extends BaseDialogOptions {\n /**\n * List of options to display for selection.\n */\n options: SelectOption[];\n /**\n * Value of the initially selected option.\n */\n selectedValue?: string;\n /**\n * Title for the confirmation button.\n *\n * @default \"OK\"\n */\n okButtonTitle?: string;\n /**\n * Title for the cancel button.\n *\n * @default \"Cancel\"\n */\n cancelButtonTitle?: string;\n}\n\nexport interface MultiSelectOptions extends BaseDialogOptions {\n /**\n * List of options to display for selection.\n */\n options: SelectOption[];\n /**\n * Values of the initially selected options.\n */\n selectedValues?: string[];\n /**\n * Title for the confirmation button.\n *\n * @default \"OK\"\n */\n okButtonTitle?: string;\n /**\n * Title for the cancel button.\n *\n * @default \"Cancel\"\n */\n cancelButtonTitle?: string;\n}\n\nexport interface ConfirmResult {\n /**\n * Whether the user confirmed (`true`) or cancelled (`false`).\n */\n value: boolean;\n}\n\nexport interface PromptResult {\n /**\n * The text entered by the user. Empty string if cancelled.\n */\n value: string;\n /**\n * Whether the user cancelled the dialog.\n */\n cancelled: boolean;\n}\n\nexport interface SingleSelectResult {\n /**\n * The value of the selected option, or `null` if cancelled.\n */\n value: string | null;\n /**\n * Whether the user cancelled the dialog.\n */\n cancelled: boolean;\n}\n\nexport interface MultiSelectResult {\n /**\n * The values of the selected options. Empty array if cancelled.\n */\n values: string[];\n /**\n * Whether the user cancelled the dialog.\n */\n cancelled: boolean;\n}\n\nexport interface SheetRow {\n /** Row title (required) */\n title: string;\n /**\n * Optional logo/icon. Supports:\n * - PNG/JPEG/WebP/GIF as base64 data URL or HTTP/HTTPS URL\n * - SVG as `data:image/svg+xml;base64,...`, `data:image/svg+xml,<svg ...>...` (URL-encoded or plain), or any HTTP/HTTPS URL ending in `.svg`\n */\n logo?: string;\n /** Optional value displayed on the right */\n value?: string;\n}\n\nexport interface SheetOptions extends DialogStyleOptions {\n /**\n * Header logo. Supports:\n * - PNG/JPEG/WebP/GIF as base64 data URL or HTTP/HTTPS URL\n * - SVG as `data:image/svg+xml;base64,...`, `data:image/svg+xml,<svg ...>...` (URL-encoded or plain), or any HTTP/HTTPS URL ending in `.svg`\n */\n headerLogo?: string;\n /** Sheet title */\n title: string;\n /** Optional subtitle displayed between title and rows */\n subtitle?: string;\n /** Description rows */\n rows: SheetRow[];\n /** Confirm button title */\n confirmButtonTitle?: string; // default: \"Confirm\"\n /** Cancel button title */\n cancelButtonTitle?: string; // default: \"Cancel\"\n /** Dialog mode */\n mode?: DialogMode;\n}\n\nexport interface MessageSheetOptions extends DialogStyleOptions {\n /**\n * Header logo. Supports:\n * - PNG/JPEG/WebP/GIF as base64 data URL or HTTP/HTTPS URL\n * - SVG as `data:image/svg+xml;base64,...`, `data:image/svg+xml,<svg ...>...` (URL-encoded or plain), or any HTTP/HTTPS URL ending in `.svg`\n */\n headerLogo?: string;\n /** Sheet title */\n title: string;\n /** Optional subtitle displayed between title and message */\n subtitle?: string;\n /** Message body displayed in the sheet */\n message: string;\n /** Confirm button title */\n confirmButtonTitle?: string; // default: \"Confirm\"\n /** Cancel button title */\n cancelButtonTitle?: string; // default: \"Cancel\"\n /** Dialog mode */\n mode?: DialogMode;\n}\n\nexport interface SheetResult {\n /** True if confirmed, false if cancelled */\n confirmed: boolean;\n}\n\nexport interface MessageSheetResult {\n /** True if confirmed, false if cancelled */\n confirmed: boolean;\n}\n\nexport interface ExtendedDialogPlugin {\n /**\n * Show an alert dialog with a single dismiss button.\n *\n * @param options - Alert configuration options.\n */\n alert(options: AlertOptions): Promise<void>;\n /**\n * Show a confirmation dialog with OK and Cancel buttons.\n *\n * @param options - Confirm configuration options.\n * @returns The user's confirmation choice.\n */\n confirm(options: ConfirmOptions): Promise<ConfirmResult>;\n /**\n * Show a prompt dialog with a text input field.\n *\n * @param options - Prompt configuration options.\n * @returns The entered text and whether the dialog was cancelled.\n */\n prompt(options: PromptOptions): Promise<PromptResult>;\n /**\n * Show a single-select dialog where the user picks one option.\n *\n * @param options - Single select configuration options.\n * @returns The selected value and whether the dialog was cancelled.\n */\n singleSelect(options: SingleSelectOptions): Promise<SingleSelectResult>;\n /**\n * Show a multi-select dialog where the user picks one or more options.\n *\n * @param options - Multi select configuration options.\n * @returns The selected values and whether the dialog was cancelled.\n */\n multiSelect(options: MultiSelectOptions): Promise<MultiSelectResult>;\n /**\n * Show a sheet dialog with a header, title, and structured data rows.\n *\n * @param options - Sheet configuration options.\n * @returns Whether the user confirmed or cancelled the sheet.\n */\n sheet(options: SheetOptions): Promise<SheetResult>;\n\n /**\n * Show a message sheet dialog with an optional header logo, title, and message.\n *\n * @param options - Message sheet configuration options.\n * @returns Whether the user confirmed or cancelled the sheet.\n */\n messageSheet(options: MessageSheetOptions): Promise<MessageSheetResult>;\n}\n"]}
@@ -946,25 +946,30 @@ public class FullScreenDialogViewController: UIViewController {
946
946
  }
947
947
 
948
948
  private func loadImage(from urlString: String, into imageView: UIImageView) {
949
- // Check if it's a base64 data URL
950
- if urlString.hasPrefix("data:") {
951
- if let commaIndex = urlString.firstIndex(of: ",") {
952
- let base64String = String(urlString[urlString.index(after: commaIndex)...])
953
- if let imageData = Data(base64Encoded: base64String),
954
- let image = UIImage(data: imageData) {
955
- imageView.image = image
956
- }
949
+ if SvgImageLoader.isSvgSource(urlString) {
950
+ let targetSize = imageView.bounds.size == .zero
951
+ ? CGSize(width: 64, height: 64)
952
+ : imageView.bounds.size
953
+ SvgImageLoader.render(source: urlString, targetSize: targetSize) { [weak imageView] image in
954
+ imageView?.image = image
957
955
  }
958
- } else if let url = URL(string: urlString) {
959
- // Load from HTTP/HTTPS URL asynchronously
960
- URLSession.shared.dataTask(with: url) { data, _, _ in
961
- if let data = data, let image = UIImage(data: data) {
962
- DispatchQueue.main.async {
963
- imageView.image = image
964
- }
965
- }
966
- }.resume()
956
+ return
957
+ }
958
+ if SvgImageLoader.isDataUrl(urlString) {
959
+ if let imageData = SvgImageLoader.decodeDataUrl(urlString),
960
+ let image = UIImage(data: imageData) {
961
+ imageView.image = image
962
+ }
963
+ return
967
964
  }
965
+ guard let url = URL(string: urlString) else { return }
966
+ URLSession.shared.dataTask(with: url) { [weak imageView] data, _, _ in
967
+ if let data = data, let image = UIImage(data: data) {
968
+ DispatchQueue.main.async {
969
+ imageView?.image = image
970
+ }
971
+ }
972
+ }.resume()
968
973
  }
969
974
 
970
975
  private func applyLiquidGlassStyle() {
@@ -0,0 +1,70 @@
1
+ import UIKit
2
+ import SVGKit
3
+
4
+ enum SvgImageLoader {
5
+
6
+ private static let svgDataUrlPrefix = "data:image/svg+xml"
7
+ private static let dataUrlPrefix = "data:"
8
+
9
+ static func isDataUrl(_ source: String) -> Bool {
10
+ source.range(of: dataUrlPrefix, options: [.anchored, .caseInsensitive]) != nil
11
+ }
12
+
13
+ static func isSvgSource(_ source: String) -> Bool {
14
+ if source.range(of: svgDataUrlPrefix, options: [.anchored, .caseInsensitive]) != nil {
15
+ return true
16
+ }
17
+ if let url = URL(string: source),
18
+ let scheme = url.scheme?.lowercased(),
19
+ scheme == "http" || scheme == "https" {
20
+ return url.pathExtension.caseInsensitiveCompare("svg") == .orderedSame
21
+ }
22
+ return false
23
+ }
24
+
25
+ /// Supports `;base64` and percent-encoded payloads.
26
+ static func decodeDataUrl(_ source: String) -> Data? {
27
+ guard let commaIndex = source.firstIndex(of: ",") else { return nil }
28
+ let header = source[..<commaIndex]
29
+ let payload = String(source[source.index(after: commaIndex)...])
30
+ if header.range(of: ";base64", options: .caseInsensitive) != nil {
31
+ return Data(base64Encoded: payload)
32
+ }
33
+ return (payload.removingPercentEncoding ?? payload).data(using: .utf8)
34
+ }
35
+
36
+ /// Uses image's intrinsic size when `targetSize` is `.zero`.
37
+ static func render(source: String, targetSize: CGSize, completion: @escaping (UIImage?) -> Void) {
38
+ if isDataUrl(source) {
39
+ guard let data = decodeDataUrl(source) else {
40
+ completion(nil); return
41
+ }
42
+ DispatchQueue.global(qos: .userInitiated).async {
43
+ let image = render(data: data, targetSize: targetSize)
44
+ DispatchQueue.main.async { completion(image) }
45
+ }
46
+ return
47
+ }
48
+ guard let url = URL(string: source) else {
49
+ completion(nil); return
50
+ }
51
+ URLSession.shared.dataTask(with: url) { data, _, _ in
52
+ guard let data = data else {
53
+ DispatchQueue.main.async { completion(nil) }
54
+ return
55
+ }
56
+ DispatchQueue.global(qos: .userInitiated).async {
57
+ let image = render(data: data, targetSize: targetSize)
58
+ DispatchQueue.main.async { completion(image) }
59
+ }
60
+ }.resume()
61
+ }
62
+
63
+ private static func render(data: Data, targetSize: CGSize) -> UIImage? {
64
+ guard let svgImage = SVGKImage(data: data) else { return nil }
65
+ if targetSize.width > 0 && targetSize.height > 0 && svgImage.size != targetSize {
66
+ svgImage.size = targetSize
67
+ }
68
+ return svgImage.uiImage
69
+ }
70
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-extended-dialog",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "description": "Show native dialogs and sheets in Capacitor with Material 3 and Liquid Glass UI",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",