UNPKG

dcp-client

Version:

Core libraries for accessing DCP network

398 lines (370 loc) 12.3 kB
/** * @file dcp-modal.css * Basic CSS rules for laying out the modals implemented in src/dcp-client/dom-tk/modals.js. * This stylesheet should be used in conjunction with an overall appearance stylesheet from * the base application to set key details like the text colours, fonts, form element * appearance, etc. * @author Wes Garland, wes@distributive.network * @date Nov 2024 * @description * A generic modal has the following layout (see also dialogTemplateHtml in the implementation): * +----------------------------------------------+ * | CLOSE X | * | TITLE | * | | * | ICON BODY | * | | * | BUTTONS PRIMARY_BUTTON | * +----------------------------------------------+ * * The overall border of the box is grey with a slight drop shadow. The DIALOG is positioned on the page * via auto margins on all four sides; we pull it up to 10% for a more comfortable reading experience, * and to permit dialogs with a lot of information before scrolling. */ DIALOG#dcp-modal { position: fixed; margin-top: 10%; padding: 0; max-width: 65%; border-radius: 4px; border: 1px solid rgb(138, 138, 138); box-shadow: rgba(0, 0, 0, 0.4) 4px 4px 4px; text-align: center; /* Animate the dialog whenever it is rendered so that users can tell when they dismiss a dialog that * gets immediately replaced by another identical dialog */ & { transition: scale 0.3s, opacity 0.3s, margin-top 0.2s ease-out; opacity: 1; scale: 1; @starting-style { opacity: 0; scale: 0; margin-top: 50%; } } } /* Let the user resize alert boxes when the stack trace is visible */ DIALOG#dcp-modal.dcp-modal-alert { &:has(#dcp-modal-stack[show-toggle="on"]) { resize: both; overflow-x: clip; overflow-y: auto; } /* Undo the side-effects of the resize attrib when the stack trace gets hidden */ &:has(#dcp-modal-stack[show-toggle="off"]) { width: revert !important; height: revert !important; } } /** * Trick Chrome into ignoring max-width when the user uses the resize corner. */ DIALOG#dcp-modal[style*=width] { max-width: unset; } DIALOG#dcp-modal:focus { outline: none; } DIALOG#dcp-modal > * { text-align: left; } /** * The title section of the modal is optional; when it is not hidden, its background colour is * primary-hue-main; we assume this is a colour that is dark enough for white text. The unset width and * left/right padding ensure that titles wider than the body allow the overall box to grow without * crowding the edges. */ DIALOG#dcp-modal H3#dcp-modal-title { text-align: center; overflow: hidden; margin: 0; padding: 1em; background-color: var(--primary-hue-main); color: white; overflow: hidden; } /** * The close X is a DIV with the X in the background image that is always at the top right corner. When * the mouse hovers over it, we make the background darker. When the is not hidden, we invert the * colour of the X, and re-invert the backdrop on hover to maintain the hover behaviour. The filters * assume that * - the X is black with a transparent background * - white looks good on the title * - black looks on the default background */ DIALOG#dcp-modal #dcp-modal-close-x { background-image: url('/dcp-client/assets/x.svg'); background-size: contain; height: 0.6em; width: 0.6em; padding: 3px; margin: 2px; border: none; position: absolute; top: 0; right: 0; } DIALOG#dcp-modal #dcp-modal-close-x:hover { backdrop-filter: brightness(0.8); } DIALOG#dcp-modal #dcp-modal-title:not(.dcp-modal-hidden) + #dcp-modal-close-x { filter: invert(100%); } DIALOG#dcp-modal #dcp-modal-title:not(.dcp-modal-hidden) + #dcp-modal-close-x:hover { backdrop-filter: brightness(0.8) invert(100%); } /** * close-x is not used for modals immitating system modals */ DIALOG#dcp-modal.dcp-modal-alert > #dcp-modal-close-x, DIALOG#dcp-modal.dcp-modal-confirm > #dcp-modal-close-x, DIALOG#dcp-modal.dcp-modal-prompt > #dcp-modal-close-x { display: none; } /** * The content area exists so that the icon and the body can be vertically centered against each other. */ DIALOG#dcp-modal #dcp-modal-content-area { display: flex; align-items: center; padding: 1em; } /** * The modal icon, when present, displays to the left of all of the text. */ DIALOG#dcp-modal #dcp-modal-icon { background-position: center; background-size: contain; background-repeat: no-repeat; background-image: url('/dcp-client/assets/dcp-logo.png'); flex-shrink: 0; height: 2em; width: 2em; padding: 0px; margin: 0 0.75em 0 0; border: none; } DIALOG#dcp-modal.dcp-modal-alert #dcp-modal-icon { background-image: url('/dcp-client/assets/lucide/triangle-alert.svg'); } DIALOG#dcp-modal.dcp-modal-alert:has(#dcp-modal-stack) #dcp-modal-icon { background-image: url('/dcp-client/assets/lucide/bug.svg'); cursor: pointer; } DIALOG#dcp-modal.dcp-modal-confirm #dcp-modal-icon { background-image: url('/dcp-client/assets/lucide/circle-alert.svg'); } DIALOG#dcp-modal.dcp-modal-upload #dcp-modal-icon { background-image: url('/dcp-client/assets/lucide/hard-drive-upload.svg'); } /** * Stack is hidden by default; clicking on the bug icon toggles the show-toggle attribute */ DIALOG#dcp-modal.dcp-modal-alert #dcp-modal-stack { font-size: 0.7em; display: none; } DIALOG#dcp-modal.dcp-modal-alert #dcp-modal-stack[show-toggle="on"] { display: block; } DIALOG#dcp-modal #dcp-modal-body[mode="text"] { white-space: pre-line; } /** * Overall dialog is overflow: hidden, but we set things up here so that really large content will add * scrollbars on the inside of the dialog, instead of becoming unmanageably large. */ DIALOG#dcp-modal #dcp-modal-body { width: 100%; overflow: auto; max-height: 50vh; } DIALOG#dcp-modal.dcp-modal-prompt INPUT[type="number"], DIALOG#dcp-modal.dcp-modal-prompt INPUT[type="password"], DIALOG#dcp-modal.dcp-modal-prompt INPUT[type="text"] { width: 100%; } /** * The error section takes up no space unless it has content. It appears below the body. */ DIALOG#dcp-modal #dcp-modal-error { text-align: center; font-size: 0.75em; padding: 0 0 1em 1em; margin-top: -1em; } /** * The button area holds the elements passed as buttons to cm.showModalDialog. They are floated to the * right so that they appear in inverse order of declaration, putting the default primary button all the * way to the right. A centered-buttons class has been provided to center the buttons in the modal. This * class selector can be removed to make that the default behaviour. The centering happens by shrinking * button-area to only the necessary size so that the top-level text-align of #dcp-modal can center it. */ DIALOG#dcp-modal #dcp-modal-button-area { margin-left: 1em; } DIALOG#dcp-modal #dcp-modal-button-area > * { margin-bottom: 1em; margin-right: 1em; } DIALOG#dcp-modal #dcp-modal-button-area > * { float: right; position: relative; } DIALOG#dcp-modal.centered-buttons #dcp-modal-button-area { display: inline-block; } /** * Class used to hide elements, eg title, in a way that we can use for sibling combinators. */ DIALOG#dcp-modal .dcp-modal-hidden { display: none; } /** * Create the dialog opening behaviour. We animate a quick fade in/our of the backdrop to make things * look smooth. Does not currently (Nov 2024) work in Firefox. */ DIALOG#dcp-modal[open]::backdrop { animation: backdrop-fade 150ms ease forwards; } DIALOG#dcp-modal:not([open])::backdrop { animation: backdrop-fade 50ms ease backwards; animation-direction: reverse; } @keyframes backdrop-fade { from { backdrop-filter: brightness(1.0); } to { backdrop-filter: brightness(0.7); } } /** * The upload modal has some unique positioning requirements. The icon is lifted from its normal * position and centered within the modal. This allows it to render on top of the drop zone. The * drop-zone uses a negative vertical position transformation to position the drop zone label. This * means that the padding at the top of the body content needs to be become a margin to avoid cutting * the top of the text off. */ DIALOG#dcp-modal.dcp-modal-upload #dcp-modal-icon { position: absolute; left: 50%; transform: translate(-50%, 0.5em); z-index: 1; } DIALOG#dcp-modal.dcp-modal-upload #dcp-modal-body { text-align: center; margin: 0; padding: 0; white-space: initial; width: 100%; z-index: 2; } DIALOG#dcp-modal.dcp-modal-upload:has(#dcp-modal-drop-target) #dcp-modal-content-area { padding-top: 0; } DIALOG#dcp-modal.dcp-modal-upload #dcp-modal-drop-target { margin-top: 1em; display: inline-block; border: 2px dashed #bbb; height: 6em; width: calc(6em * 1.618); } DIALOG#dcp-modal.dcp-modal-upload #dcp-modal-drop-label { display: inline-block; position: relative; transform: translateY(-0.9em); color: grey; background: white; padding: 2px 4px 2px 4px; font-size: 0.75em; } /** Control the behaviour of the upload modal when drag/dropping to give user feedback */ DIALOG#dcp-modal.dcp-modal-upload #dcp-modal-icon { transition: all 100ms linear; } DIALOG#dcp-modal.dcp-modal-upload[drag-state="over"] #dcp-modal-drop-target { border-color: #777; } DIALOG#dcp-modal.dcp-modal-upload[drag-state="over"] #dcp-modal-icon { font-size: 1.5em; } DIALOG#dcp-modal.dcp-modal-upload[drag-state="invalid"] { cursor: not-allowed; } DIALOG#dcp-modal.dcp-modal-upload[drag-state="over"] { cursor: copy; } DIALOG#dcp-modal.dcp-modal-upload[drag-state="invalid"] #dcp-modal-drop-target { opacity: 0.35; } DIALOG#dcp-modal.dcp-modal-upload[drag-state="invalid"] #dcp-modal-icon { font-size: 0; opacity: 0; } /** * Password dialogs can have one or two label+input divs, depending on need (enter vs create). */ DIALOG#dcp-modal:has(#dcp-modal-password-prompt) { max-width: 45%; INPUT[type] { max-width: 50em; } LABEL { line-height: 37px; /* remove when fixing dcp-style.css */ padding-right: 0.5em; width: 25ch; } LABEL::after { content: ":"; } #dcp-modal-password-prompt, #dcp-modal-password-confirm-prompt { display: flex; flex-direction: row; flex-wrap: nowrap; justify-content: flex-start; & > DIV { display: flex; flex-grow: 1; } } #dcp-modal-password-preamble { &:not(:empty) { padding-bottom: 0.5em; } } } /** * Passphrase prompts with the showEye option are rendered as input[type="text"][obscured] instead of * input type="password". A click handler is added to toggle when the obscured attribute on the sibling * span element. */ DIALOG#dcp-modal DIV#dcp-modal-password-prompt, DIALOG#dcp-modal DIV#dcp-modal-password-confirm-prompt { /** * INPUT[type="text"].with-eye is a password-like element with an eye icon that toggles password * visibility. Visibility toggling happens by adding/removing the 'obscured' attribute. */ INPUT[type="text"].dcp-show-eye[obscured] { -webkit-text-security: disc; } INPUT[type="text"].dcp-show-eye[obscured] + SPAN { background-image: url("/dcp-client/assets/lucide/eye-off.svg"); } INPUT[type="text"].dcp-show-eye + SPAN { background-image: url("/dcp-client/assets/lucide/eye.svg"); } /** Appearance of the eye itself */ INPUT.dcp-show-eye + SPAN { display: inline-block; position: relative; background-repeat: no-repeat; background-size: cover; height: 16px; width: 16px; margin-left: -16px; right: 8px; transform: translateY(9px); opacity: 50%; cursor: pointer; } }