dcp-client 5.5.5 → 5.6.1

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.
@@ -11,6 +11,5 @@
11
11
  * @date Nov 2024
12
12
  */
13
13
 
14
- @layer dcp-style dcp-client;
15
- @import https://cdn.distributed.computerl
16
-
14
+ @layer dcp-style, dcp-client, dcp-modal, dcp-auth;
15
+ @import url("./dcp-modal.css") layer(dcp-modal);
@@ -0,0 +1,397 @@
1
+ /**
2
+ * @file dcp-modal.css
3
+ * Basic CSS rules for laying out the modals implemented in src/dcp-client/dom-tk/modals.js.
4
+ * This stylesheet should be used in conjunction with an overall appearance stylesheet from
5
+ * the base application to set key details like the text colours, fonts, form element
6
+ * appearance, etc.
7
+ * @author Wes Garland, wes@distributive.network
8
+ * @date Nov 2024
9
+ * @description
10
+ * A generic modal has the following layout (see also dialogTemplateHtml in the implementation):
11
+ * +----------------------------------------------+
12
+ * | CLOSE X |
13
+ * | TITLE |
14
+ * | |
15
+ * | ICON BODY |
16
+ * | |
17
+ * | BUTTONS PRIMARY_BUTTON |
18
+ * +----------------------------------------------+
19
+ *
20
+ * The overall border of the box is grey with a slight drop shadow. The DIALOG is positioned on the page
21
+ * via auto margins on all four sides; we pull it up to 10% for a more comfortable reading experience,
22
+ * and to permit dialogs with a lot of information before scrolling.
23
+ */
24
+ DIALOG#dcp-modal {
25
+ position: fixed;
26
+ margin-top: 10%;
27
+ padding: 0;
28
+ max-width: 65%;
29
+ border-radius: 4px;
30
+ border: 1px solid rgb(138, 138, 138);
31
+ box-shadow: rgba(0, 0, 0, 0.4) 4px 4px 4px;
32
+ text-align: center;
33
+
34
+ /* Animate the dialog whenever it is rendered so that users can tell when they dismiss a dialog that
35
+ * gets immediately replaced by another identical dialog
36
+ */
37
+ & {
38
+ transition: scale 0.3s, opacity 0.3s, margin-top 0.2s ease-out;
39
+ opacity: 1;
40
+ scale: 1;
41
+ @starting-style {
42
+ opacity: 0;
43
+ scale: 0;
44
+ margin-top: 50%;
45
+ }
46
+ }
47
+ }
48
+
49
+ /* Let the user resize alert boxes when the stack trace is visible */
50
+ DIALOG#dcp-modal.dcp-modal-alert {
51
+ &:has(#dcp-modal-stack[show-toggle="on"]) {
52
+ resize: both;
53
+ overflow-x: clip;
54
+ overflow-y: auto;
55
+ }
56
+ /* Undo the side-effects of the resize attrib when the stack trace gets hidden */
57
+ &:has(#dcp-modal-stack[show-toggle="off"]) {
58
+ width: revert !important;
59
+ height: revert !important;
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Trick Chrome into ignoring max-width when the user uses the resize corner.
65
+ */
66
+ DIALOG#dcp-modal[style*=width] {
67
+ max-width: unset;
68
+ }
69
+
70
+ DIALOG#dcp-modal:focus {
71
+ outline: none;
72
+ }
73
+
74
+ DIALOG#dcp-modal > * {
75
+ text-align: left;
76
+ }
77
+
78
+ /**
79
+ * The title section of the modal is optional; when it is not hidden, its background colour is
80
+ * primary-hue-main; we assume this is a colour that is dark enough for white text. The unset width and
81
+ * left/right padding ensure that titles wider than the body allow the overall box to grow without
82
+ * crowding the edges.
83
+ */
84
+ DIALOG#dcp-modal H3#dcp-modal-title {
85
+ text-align: center;
86
+ overflow: hidden;
87
+ margin: 0;
88
+ padding: 1em;
89
+ background-color: var(--primary-hue-main);
90
+ color: white;
91
+ overflow: hidden;
92
+ }
93
+
94
+ /**
95
+ * The close X is a DIV with the X in the background image that is always at the top right corner. When
96
+ * the mouse hovers over it, we make the background darker. When the is not hidden, we invert the
97
+ * colour of the X, and re-invert the backdrop on hover to maintain the hover behaviour. The filters
98
+ * assume that
99
+ * - the X is black with a transparent background
100
+ * - white looks good on the title
101
+ * - black looks on the default background
102
+ */
103
+ DIALOG#dcp-modal #dcp-modal-close-x {
104
+ background-image: url('/dcp-client/assets/x.svg');
105
+ background-size: contain;
106
+ height: 0.6em;
107
+ width: 0.6em;
108
+ padding: 3px;
109
+ margin: 2px;
110
+ border: none;
111
+ position: absolute;
112
+ top: 0;
113
+ right: 0;
114
+ }
115
+ DIALOG#dcp-modal #dcp-modal-close-x:hover {
116
+ backdrop-filter: brightness(0.8);
117
+ }
118
+ DIALOG#dcp-modal #dcp-modal-title:not(.dcp-modal-hidden) + #dcp-modal-close-x {
119
+ filter: invert(100%);
120
+ }
121
+ DIALOG#dcp-modal #dcp-modal-title:not(.dcp-modal-hidden) + #dcp-modal-close-x:hover {
122
+ backdrop-filter: brightness(0.8) invert(100%);
123
+ }
124
+
125
+ /**
126
+ * close-x is not used for modals immitating system modals
127
+ */
128
+ DIALOG#dcp-modal.dcp-modal-alert > #dcp-modal-close-x,
129
+ DIALOG#dcp-modal.dcp-modal-confirm > #dcp-modal-close-x,
130
+ DIALOG#dcp-modal.dcp-modal-prompt > #dcp-modal-close-x {
131
+ display: none;
132
+ }
133
+
134
+ /**
135
+ * The content area exists so that the icon and the body can be vertically centered against each other.
136
+ */
137
+ DIALOG#dcp-modal #dcp-modal-content-area {
138
+ display: flex;
139
+ align-items: center;
140
+ padding: 1em;
141
+ }
142
+
143
+ /**
144
+ * The modal icon, when present, displays to the left of all of the text.
145
+ */
146
+ DIALOG#dcp-modal #dcp-modal-icon {
147
+ background-position: center;
148
+ background-size: contain;
149
+ background-repeat: no-repeat;
150
+ background-image: url('/dcp-client/assets/dcp-logo.png');
151
+ flex-shrink: 0;
152
+ height: 2em;
153
+ width: 2em;
154
+ padding: 0px;
155
+ margin: 0 0.75em 0 0;
156
+ border: none;
157
+ }
158
+ DIALOG#dcp-modal.dcp-modal-alert #dcp-modal-icon {
159
+ background-image: url('/dcp-client/assets/lucide/triangle-alert.svg');
160
+ }
161
+ DIALOG#dcp-modal.dcp-modal-alert:has(#dcp-modal-stack) #dcp-modal-icon {
162
+ background-image: url('/dcp-client/assets/lucide/bug.svg');
163
+ cursor: pointer;
164
+ }
165
+ DIALOG#dcp-modal.dcp-modal-confirm #dcp-modal-icon {
166
+ background-image: url('/dcp-client/assets/lucide/circle-alert.svg');
167
+ }
168
+ DIALOG#dcp-modal.dcp-modal-upload #dcp-modal-icon {
169
+ background-image: url('/dcp-client/assets/lucide/hard-drive-upload.svg');
170
+ }
171
+
172
+ /**
173
+ * Stack is hidden by default; clicking on the bug icon toggles the show-toggle attribute
174
+ */
175
+ DIALOG#dcp-modal.dcp-modal-alert #dcp-modal-stack {
176
+ font-size: 0.7em;
177
+ display: none;
178
+ }
179
+ DIALOG#dcp-modal.dcp-modal-alert #dcp-modal-stack[show-toggle="on"] {
180
+ display: block;
181
+ }
182
+
183
+ DIALOG#dcp-modal #dcp-modal-body[mode="text"] {
184
+ white-space: pre-line;
185
+ }
186
+
187
+ /**
188
+ * Overall dialog is overflow: hidden, but we set things up here so that really large content will add
189
+ * scrollbars on the inside of the dialog, instead of becoming unmanageably large.
190
+ */
191
+ DIALOG#dcp-modal #dcp-modal-body {
192
+ width: 100%;
193
+ overflow: auto;
194
+ max-height: 50vh;
195
+ }
196
+
197
+ DIALOG#dcp-modal.dcp-modal-prompt INPUT[type="number"],
198
+ DIALOG#dcp-modal.dcp-modal-prompt INPUT[type="password"],
199
+ DIALOG#dcp-modal.dcp-modal-prompt INPUT[type="text"] {
200
+ width: 100%;
201
+ }
202
+
203
+ /**
204
+ * The error section takes up no space unless it has content. It appears below the body.
205
+ */
206
+ DIALOG#dcp-modal #dcp-modal-error {
207
+ text-align: center;
208
+ font-size: 0.75em;
209
+ padding: 0 0 1em 1em;
210
+ margin-top: -1em;
211
+ }
212
+ /**
213
+ * The button area holds the elements passed as buttons to cm.showModalDialog. They are floated to the
214
+ * right so that they appear in inverse order of declaration, putting the default primary button all the
215
+ * way to the right. A centered-buttons class has been provided to center the buttons in the modal. This
216
+ * class selector can be removed to make that the default behaviour. The centering happens by shrinking
217
+ * button-area to only the necessary size so that the top-level text-align of #dcp-modal can center it.
218
+ */
219
+ DIALOG#dcp-modal #dcp-modal-button-area {
220
+ margin-left: 1em;
221
+ }
222
+ DIALOG#dcp-modal #dcp-modal-button-area > * {
223
+ margin-bottom: 1em;
224
+ margin-right: 1em;
225
+ }
226
+ DIALOG#dcp-modal #dcp-modal-button-area > * {
227
+ float: right;
228
+ position: relative;
229
+ }
230
+ DIALOG#dcp-modal.centered-buttons #dcp-modal-button-area {
231
+ display: inline-block;
232
+ }
233
+
234
+ /**
235
+ * Class used to hide elements, eg title, in a way that we can use for sibling combinators.
236
+ */
237
+ DIALOG#dcp-modal .dcp-modal-hidden {
238
+ display: none;
239
+ }
240
+
241
+ /**
242
+ * Create the dialog opening behaviour. We animate a quick fade in/our of the backdrop to make things
243
+ * look smooth. Does not currently (Nov 2024) work in Firefox.
244
+ */
245
+ DIALOG#dcp-modal[open]::backdrop {
246
+ animation: backdrop-fade 150ms ease forwards;
247
+ }
248
+ DIALOG#dcp-modal:not([open])::backdrop {
249
+ animation: backdrop-fade 50ms ease backwards;
250
+ animation-direction: reverse;
251
+ }
252
+ @keyframes backdrop-fade {
253
+ from { backdrop-filter: brightness(1.0); }
254
+ to { backdrop-filter: brightness(0.7); }
255
+ }
256
+
257
+ /**
258
+ * The upload modal has some unique positioning requirements. The icon is lifted from its normal
259
+ * position and centered within the modal. This allows it to render on top of the drop zone. The
260
+ * drop-zone uses a negative vertical position transformation to position the drop zone label. This
261
+ * means that the padding at the top of the body content needs to be become a margin to avoid cutting
262
+ * the top of the text off.
263
+ */
264
+ DIALOG#dcp-modal.dcp-modal-upload #dcp-modal-icon {
265
+ position: absolute;
266
+ left: 50%;
267
+ transform: translate(-50%, 0.5em);
268
+ z-index: 1;
269
+ }
270
+ DIALOG#dcp-modal.dcp-modal-upload #dcp-modal-body {
271
+ text-align: center;
272
+ margin: 0;
273
+ padding: 0;
274
+ white-space: initial;
275
+ width: 100%;
276
+ z-index: 2;
277
+ }
278
+ DIALOG#dcp-modal.dcp-modal-upload:has(#dcp-modal-drop-target) #dcp-modal-content-area
279
+ {
280
+ padding-top: 0;
281
+ }
282
+ DIALOG#dcp-modal.dcp-modal-upload #dcp-modal-drop-target {
283
+ margin-top: 1em;
284
+ display: inline-block;
285
+ border: 2px dashed #bbb;
286
+ height: 6em;
287
+ width: calc(6em * 1.618);
288
+ }
289
+ DIALOG#dcp-modal.dcp-modal-upload #dcp-modal-drop-label {
290
+ display: inline-block;
291
+ position: relative;
292
+ transform: translateY(-0.9em);
293
+ color: grey;
294
+ background: white;
295
+ padding: 2px 4px 2px 4px;
296
+ font-size: 0.75em;
297
+ }
298
+
299
+ /** Control the behaviour of the upload modal when drag/dropping to give user feedback */
300
+ DIALOG#dcp-modal.dcp-modal-upload #dcp-modal-icon {
301
+ transition: all 100ms linear;
302
+ }
303
+ DIALOG#dcp-modal.dcp-modal-upload[drag-state="over"] #dcp-modal-drop-target {
304
+ border-color: #777;
305
+ }
306
+ DIALOG#dcp-modal.dcp-modal-upload[drag-state="over"] #dcp-modal-icon {
307
+ font-size: 1.5em;
308
+ }
309
+ DIALOG#dcp-modal.dcp-modal-upload[drag-state="invalid"] {
310
+ cursor: not-allowed;
311
+ }
312
+ DIALOG#dcp-modal.dcp-modal-upload[drag-state="over"] {
313
+ cursor: copy;
314
+ }
315
+ DIALOG#dcp-modal.dcp-modal-upload[drag-state="invalid"] #dcp-modal-drop-target {
316
+ opacity: 0.35;
317
+ }
318
+ DIALOG#dcp-modal.dcp-modal-upload[drag-state="invalid"] #dcp-modal-icon {
319
+ font-size: 0;
320
+ opacity: 0;
321
+ }
322
+
323
+ /**
324
+ * Password dialogs can have one or two label+input divs, depending on need (enter vs create).
325
+ */
326
+ DIALOG#dcp-modal:has(#dcp-modal-password-prompt) {
327
+ max-width: 45%;
328
+ INPUT[type] {
329
+ max-width: 50em;
330
+ }
331
+
332
+ LABEL {
333
+ line-height: 37px; /* remove when fixing dcp-style.css */
334
+ padding-right: 0.5em;
335
+ width: 25ch;
336
+ }
337
+ LABEL::after {
338
+ content: ":";
339
+ }
340
+
341
+ #dcp-modal-password-prompt,
342
+ #dcp-modal-password-confirm-prompt {
343
+ display: flex;
344
+ flex-direction: row;
345
+ flex-wrap: nowrap;
346
+ justify-content: flex-start;
347
+
348
+ & > DIV {
349
+ display: flex;
350
+ flex-grow: 1;
351
+ }
352
+ }
353
+
354
+ #dcp-modal-password-preamble {
355
+ &:not(:empty) {
356
+ padding-bottom: 0.5em;
357
+ }
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Passphrase prompts with the showEye option are rendered as input[type="text"][obscured] instead of
363
+ * input type="password". A click handler is added to toggle when the obscured attribute on the sibling
364
+ * span element.
365
+ */
366
+ DIALOG#dcp-modal DIV#dcp-modal-password-prompt,
367
+ DIALOG#dcp-modal DIV#dcp-modal-password-confirm-prompt {
368
+ /**
369
+ * INPUT[type="text"].with-eye is a password-like element with an eye icon that toggles password
370
+ * visibility. Visibility toggling happens by adding/removing the 'obscured' attribute.
371
+ */
372
+ INPUT[type="text"].dcp-show-eye[obscured] {
373
+ -webkit-text-security: disc;
374
+ }
375
+ INPUT[type="text"].dcp-show-eye[obscured] + SPAN {
376
+ background-image: url("/dcp-client/assets/lucide/eye-off.svg");
377
+ }
378
+ INPUT[type="text"].dcp-show-eye + SPAN {
379
+ background-image: url("/dcp-client/assets/lucide/eye.svg");
380
+ }
381
+
382
+ /** Appearance of the eye itself */
383
+ INPUT.dcp-show-eye + SPAN {
384
+ display: inline-block;
385
+ position: relative;
386
+
387
+ background-repeat: no-repeat;
388
+ background-size: cover;
389
+ height: 16px;
390
+ width: 16px;
391
+ margin-left: -16px;
392
+ right: 8px;
393
+ transform: translateY(9px);
394
+ opacity: 50%;
395
+ cursor: pointer;
396
+ }
397
+ }
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-eye-off-icon lucide-eye-off"><path d="M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49"/><path d="M14.084 14.158a3 3 0 0 1-4.242-4.242"/><path d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143"/><path d="m2 2 20 20"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-eye-icon lucide-eye"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"/><circle cx="12" cy="12" r="3"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-loader-circle-icon lucide-loader-circle"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>
@@ -0,0 +1,54 @@
1
+ <!doctype html>
2
+ <!--
3
+ - @file dcp-test.html - test page to help validate the dcp-client modals
4
+ - - version: __GIT_VERSION__
5
+ - - branch: __GIT_BRANCH__
6
+ - - build: __DCP_BUILD
7
+ -
8
+ - @author Wes Garland, wes@distributive.network
9
+ - @date July 2024
10
+ -->
11
+ <html>
12
+ <head>
13
+ <meta charset="utf-8">
14
+ <title>DCP Modals Test Page</title>
15
+ <script src="./load-bt-common.js"></script>
16
+ <link rel="stylesheet" cdn-href="/css/dcp-style-branded.css">
17
+ </head>
18
+ <body>
19
+ <h1>DCP Modals Test Page.</h1>
20
+ <p>
21
+ <textarea id="some-text" rows="10" cols="80">
22
+ Lorem ipsum odor amet, consectetuer adipiscing elit. Consectetur senectus facilisi primis dignissim tellus mauris. Fringilla turpis justo proin bibendum, malesuada praesent. Sagittis sit lacinia condimentum id nam venenatis bibendum finibus cubilia. Rhoncus euismod platea posuere elementum pretium dui iaculis. Molestie rhoncus praesent quam penatibus cubilia mollis rhoncus vestibulum? Magnis magnis ultrices torquent molestie iaculis vulputate blandit interdum vivamus. Platea dis dolor ipsum cubilia mi, erat per.
23
+
24
+ Accumsan mollis auctor pulvinar in suscipit elementum id. Rutrum praesent consectetur dapibus; nullam primis tincidunt lacus tempus. Nulla rhoncus potenti leo mi venenatis potenti accumsan. Lacinia facilisis ultricies vulputate, nostra erat aptent semper. Duis facilisis euismod amet integer accumsan rhoncus. Ultricies luctus luctus, malesuada parturient suspendisse dignissim. Mus purus proin amet imperdiet montes volutpat vitae risus amet.
25
+ </textarea>
26
+ </p>
27
+ <button primary=true
28
+ onclick="dcp['dom-tk'].modals.passwordPrompt('wes@distributive.network', 'Enter your very secret password').then(xd => $('#response5').value = xd)"
29
+ >passwordPrompt</button>Password: <input id='response5' type="text"><br>
30
+ <button primary=true
31
+ onclick="dcp['dom-tk'].modals.alert(Date.now() + ' ' + $('#some-text').value, {title: $('#alert-title').value})"
32
+ >alert
33
+ </button> title: <input type="text" id='alert-title'>
34
+
35
+ <button onclick="dcp['dom-tk'].modals.alert(new Error('this is an error message'), {title: $('#alert-title').value})">
36
+ alert stack
37
+ </button><br>
38
+
39
+ <button primary=true
40
+ onclick="dcp['dom-tk'].modals.confirm($('#some-text').value).then(xa => $('#response1').value = xa || '')"
41
+ >Confirm
42
+ </button>Response: <input id="response1" type="text"><br>
43
+ <button primary=true
44
+ onclick="dcp['dom-tk'].modals.prompt('When you eat your smarties, do you eat the red ones last?', 'sure do').then(xb => $('#response2').value = String(xb||''))"
45
+ >Prompt
46
+ </button>Response: <input id='response2' type="text"><br>
47
+ <button primary=true
48
+ onclick="dcp['dom-tk'].modals.upload('Upload your PDF', '*.pdf,application/pdf').then(xc => $('#response3').value = `${xc?.contents.byteLength} bytes uploaded`)"
49
+ >Upload PDF</button>Result: <input id='response3' type="text"><br>
50
+ <button primary=true
51
+ onclick="dcp['dom-tk']..modals.uploadKeystore('Show Me The Money').then(ks => $('#response4').value = ks?.address);"
52
+ >Upload Keystore</button>Address: <input id='response4' type="text"><br>
53
+ </body>
54
+ </html>