Merge branch 'main' into jz-5-22-25-am
* main: Handle single items better Finishing touches Relative positioning for trays WIP
This commit is contained in:
@@ -41,7 +41,6 @@
|
||||
--z-terminal: 20;
|
||||
--z-tray: 21;
|
||||
|
||||
|
||||
/* OKLCH colors: Fixed */
|
||||
--lch-black: 0% 0 0;
|
||||
--lch-white: 100% 0 0;
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card {
|
||||
@media (prefers-color-scheme: dark) {
|
||||
box-shadow: 0 0 0 1px var(--color-ink-lighter);
|
||||
}
|
||||
}
|
||||
|
||||
&:has(.card--notification) {
|
||||
.notifications-list__empty-message {
|
||||
display: none;
|
||||
|
||||
@@ -67,6 +67,10 @@
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Remove all animations and transitions for people that prefer not to see them */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
|
||||
@@ -83,4 +87,14 @@
|
||||
scroll-behavior: initial;
|
||||
}
|
||||
}
|
||||
|
||||
dialog {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
|
||||
&:where(:focus-visible):focus,
|
||||
&:where(:focus-visible):active {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,102 +3,95 @@
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.tray {
|
||||
--tray-item-duration: 350ms;
|
||||
--tray-item-height: 76px; /* FIXME: Magic number */
|
||||
--tray-item-offset: var(--block-space);
|
||||
--tray-bottom-padding: var(--block-space-half);
|
||||
/* Container
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
background-color: transparent;
|
||||
block-size: var(--footer-height);
|
||||
border: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.tray {
|
||||
--tray-duration: 350ms;
|
||||
--tray-margin: var(--inline-space);
|
||||
--tray-radius: 0.25rem;
|
||||
--tray-timing-function: cubic-bezier(0.25, 1.25, 0.5, 1);
|
||||
|
||||
align-items: end;
|
||||
block-size: calc(var(--footer-height) - var(--tray-margin) * 2);
|
||||
display: grid;
|
||||
inset-block: auto var(--tray-margin);
|
||||
inline-size: var(--tray-size);
|
||||
inset-block: auto 0;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
position: fixed;
|
||||
z-index: var(--z-tray);
|
||||
|
||||
&:where(:focus-visible):focus,
|
||||
&:where(:focus-visible):active {
|
||||
outline: 0;
|
||||
/* Make the dialog, expander, and actions inhabit the same space */
|
||||
> * {
|
||||
grid-column-start: 1;
|
||||
grid-row-start: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dialog
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
.tray__dialog {
|
||||
background-color: transparent;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
inline-size: auto;
|
||||
inset: auto 0 0 0;
|
||||
position: absolute;
|
||||
transition: var(--tray-duration) var(--tray-timing-function);
|
||||
transition-property: background-color, box-shadow, inset-block-end;
|
||||
|
||||
&:not([open]) {
|
||||
block-size: var(--footer-height);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
:where(.tray__item) {
|
||||
--tray-item-delay: calc((var(--tray-item-index) - 1) * 10ms);
|
||||
--tray-item-y: calc((var(--tray-item-index) - 1) * (var(--tray-item-offset) * -1) - var(--tray-bottom-padding));
|
||||
--tray-item-index: 1;
|
||||
--tray-item-scale: calc(1 - var(--tray-item-index) / 30);
|
||||
--tray-item-z: calc(6 - var(--tray-item-index));
|
||||
&[open] {
|
||||
border-radius: var(--tray-radius);
|
||||
box-shadow:
|
||||
0 0 0 1px var(--color-ink-lighter),
|
||||
0 0 16px oklch(var(--lch-black) / 33%);
|
||||
inset-block-end: calc(var(--footer-height) - var(--tray-margin));
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
display: flex;
|
||||
font-size: 10px;
|
||||
inline-size: var(--tray-size);
|
||||
inset: auto 0 0;
|
||||
outline: 0;
|
||||
pointer-events: none;
|
||||
position: absolute; /* tmp fix; ideally should remove the position-relative class from tray__item */
|
||||
scale: var(--tray-item-scale);
|
||||
transform: translateY(var(--tray-item-y));
|
||||
transform-origin: center;
|
||||
transition:
|
||||
border-radius var(--tray-item-duration),
|
||||
box-shadow var(--tray-item-duration),
|
||||
scale var(--tray-item-duration),
|
||||
transform var(--tray-item-duration);
|
||||
transition-delay: var(--tray-item-delay);
|
||||
transition-timing-function: cubic-bezier(0.25, 1.25, 0.5, 1);
|
||||
z-index: var(--tray-item-z);
|
||||
container-type: inline-size;
|
||||
/* Expander & Actions
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
&:nth-child(1) { --tray-item-index: 1; }
|
||||
&:nth-child(2) { --tray-item-index: 2; }
|
||||
&:nth-child(3) { --tray-item-index: 3; }
|
||||
&:nth-child(4) { --tray-item-index: 4; }
|
||||
&:nth-child(5) { --tray-item-index: 5; }
|
||||
&:nth-child(6) { --tray-item-index: 6; }
|
||||
.tray__expander {
|
||||
--hover-size: 0;
|
||||
--outline-size: 0;
|
||||
|
||||
.tray[open] & {
|
||||
--tray-item-y: calc(-100% * var(--tray-item-index) + var(--tray-item-height) - var(--footer-height));
|
||||
--tray-item-scale: 1;
|
||||
align-self: stretch;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
margin-block: calc(-1 * var(--tray-margin));
|
||||
z-index: calc(var(--z-tray) + 1);
|
||||
|
||||
pointer-events: unset;
|
||||
}
|
||||
|
||||
.tray:has(.tray__item:only-child) & {
|
||||
pointer-events: unset;
|
||||
}
|
||||
.tray__dialog[open] ~ &,
|
||||
.tray__dialog:not(:has(.tray__item:not(.tray__item--overflow))) ~ & {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.tray__actions {
|
||||
--hover-size: 0;
|
||||
|
||||
align-self: center;
|
||||
align-items: stretch;
|
||||
border: 1px dashed color-mix(in srgb, var(--color-white) 40%, var(--color-ink));
|
||||
border: 1px dashed oklch(var(--lch-white) / 50%);
|
||||
border-radius: 0.5em;
|
||||
color: var(--color-ink-inverted);
|
||||
display: flex;
|
||||
gap: var(--inline-space-half);
|
||||
justify-content: space-between;
|
||||
opacity: 0;
|
||||
scale: calc(1 - 1 / 30);
|
||||
transform: translateY(100%);
|
||||
transition: opacity 0.2s ease-out, scale 0.2s ease-out, transform 0.2s ease-out;
|
||||
transition: translate var(--tray-duration) var(--tray-timing-function);
|
||||
translate: 0 var(--footer-height);
|
||||
|
||||
.tray[open] & {
|
||||
.tray__dialog[open] ~ &,
|
||||
.tray__dialog:not(:has(.tray__item:not(.tray__item--overflow))) ~ & {
|
||||
opacity: 1;
|
||||
scale: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.tray:not(:has(.tray__item:not(.tray__item--overflow))) & {
|
||||
opacity: 1;
|
||||
scale: 1;
|
||||
transform: none;
|
||||
transition: none;
|
||||
translate: 0 0;
|
||||
}
|
||||
|
||||
/* Get the circle buttons to be full-height without magic numbers */
|
||||
@@ -127,27 +120,53 @@
|
||||
}
|
||||
}
|
||||
|
||||
.tray__expander {
|
||||
--hover-size: 0;
|
||||
--outline-size: 0;
|
||||
/* Item
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
background-color: transparent;
|
||||
block-size: var(--tray-item-height);
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
inset: auto 0 0 0;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
.tray__item {
|
||||
--tray-item-delay: calc((var(--tray-item-index) - 1) * 20ms);
|
||||
--tray-item-height: 76px; /* FIXME: Magic number */
|
||||
--tray-item-index: 1;
|
||||
--tray-item-margin: calc(-1 * var(--tray-item-height) + var(--tray-item-offset));
|
||||
--tray-item-offset: var(--block-space-half); /* The amount they overlap */
|
||||
--tray-item-scale: calc(1 - (var(--tray-item-index) - 1) / 30);
|
||||
--tray-item-z: calc(6 - var(--tray-item-index));
|
||||
|
||||
.tray[open] &,
|
||||
.tray:not(:has(.tray__item)) &,
|
||||
.tray:has(.tray__item:only-child) & {
|
||||
display: none;
|
||||
position: relative;
|
||||
|
||||
.tray__dialog & {
|
||||
font-size: 10px;
|
||||
transition: var(--tray-duration) var(--tray-timing-function);
|
||||
transition-delay: var(--tray-item-delay);
|
||||
transition-property: margin, scale;
|
||||
z-index: var(--tray-item-z);
|
||||
|
||||
&:not(:first-child) {
|
||||
scale: var(--tray-item-scale);
|
||||
margin-block-end: var(--tray-item-margin);
|
||||
}
|
||||
|
||||
&:nth-child(1) { --tray-item-index: 1; }
|
||||
&:nth-child(2) { --tray-item-index: 2; }
|
||||
&:nth-child(3) { --tray-item-index: 3; }
|
||||
&:nth-child(4) { --tray-item-index: 4; }
|
||||
&:nth-child(5) { --tray-item-index: 5; }
|
||||
&:nth-child(6) { --tray-item-index: 6; }
|
||||
&:nth-child(7) { --tray-item-index: 7; }
|
||||
&:nth-child(8) { --tray-item-index: 8; }
|
||||
&:nth-child(9) { --tray-item-index: 9; }
|
||||
&:nth-child(10) { --tray-item-index: 10; }
|
||||
}
|
||||
|
||||
.tray__dialog[open] & {
|
||||
--tray-item-margin: 0;
|
||||
--tray-item-scale: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.tray__item--overflow {
|
||||
--tray-item-index: 7 !important;
|
||||
--tray-item-z: 1;
|
||||
|
||||
display: none;
|
||||
opacity: 0;
|
||||
@@ -160,16 +179,14 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tray:has(.tray__item:nth-child(7)) & {
|
||||
.tray__dialog:has(.tray__item:nth-child(7)) & {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.tray[open] & {
|
||||
--tray-item-y: calc(-100% * var(--tray-item-index) + var(--tray-bottom-padding));
|
||||
.tray__dialog[open] & {
|
||||
--tray-item-scale: 1;
|
||||
|
||||
opacity: 1;
|
||||
pointer-events: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,10 +209,7 @@
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
&,
|
||||
[open] & {
|
||||
box-shadow: 0 0 0 1px var(--color-ink-light);
|
||||
}
|
||||
box-shadow: 0 0 0 1px var(--color-ink-lighter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,15 +234,20 @@
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
.tray--pins {
|
||||
inset-inline: var(--inline-space) auto;
|
||||
inset-inline: var(--tray-margin) auto;
|
||||
|
||||
/* Don't expand if there's only one pin */
|
||||
.tray__dialog:has(.tray__item:only-child) {
|
||||
pointer-events: unset;
|
||||
|
||||
~ .tray__expander {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.tray__item {
|
||||
--tray-item-z: calc(10 - var(--tray-item-index));
|
||||
|
||||
&:nth-child(7) { --tray-item-index: 7; }
|
||||
&:nth-child(8) { --tray-item-index: 8; }
|
||||
&:nth-child(9) { --tray-item-index: 9; }
|
||||
&:nth-child(10) { --tray-item-index: 10; }
|
||||
&:nth-child(1n + 11) { display: none; }
|
||||
}
|
||||
|
||||
@@ -287,7 +306,7 @@
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
.tray--notifications {
|
||||
inset-inline: auto var(--inline-space);
|
||||
inset-inline: auto var(--tray-margin);
|
||||
|
||||
.tray__item:nth-child(1n + 7) {
|
||||
display: none;
|
||||
|
||||
@@ -6,12 +6,18 @@ export default class extends Controller {
|
||||
modal: { type: Boolean, default: false }
|
||||
}
|
||||
|
||||
connect() {
|
||||
console.log("connect")
|
||||
}
|
||||
|
||||
open() {
|
||||
const modal = this.modalValue
|
||||
|
||||
if (modal) {
|
||||
console.log(modal)
|
||||
this.dialogTarget.showModal()
|
||||
} else {
|
||||
console.log("NOT FOUND")
|
||||
this.dialogTarget.show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<div class="card__background">
|
||||
<%= image_tag card.image.presence || "", size: 120, data: { upload_preview_target: "image" } %>
|
||||
</div>
|
||||
<% if card.image.present? %>
|
||||
<div class="card__background">
|
||||
<%= image_tag card.image.presence || "", size: 120, data: { upload_preview_target: "image" } %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if card.closed? %>
|
||||
<div class="card__closed">
|
||||
|
||||
@@ -48,9 +48,9 @@
|
||||
|
||||
<% if Current.user && !@hide_footer_frames %>
|
||||
<div id="footer_frames" data-turbo-permanent="true" >
|
||||
<%= render "notifications/tray" %>
|
||||
<%= render "my/pins/tray" %>
|
||||
<%= render "commands/terminal" %>
|
||||
<%= render "notifications/tray" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</footer>
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
<%= turbo_stream_from Current.user, :pins_tray %>
|
||||
|
||||
<%= tag.dialog id: "pin-tray", class: "tray tray--pins",
|
||||
data: { controller: "dialog", turbo_permanent: true, dialog_modal_value: false, dialog_target: "dialog", action: "keydown.esc->dialog#close:stop click@document->dialog#closeOnClickOutside" } do %>
|
||||
<%= turbo_frame_tag "pins", src: my_pins_path %>
|
||||
<section class="tray tray--pins" data-controller="dialog">
|
||||
<%= tag.dialog id: "pin-tray", class: "tray__dialog",
|
||||
data: { turbo_permanent: true, dialog_modal_value: false, dialog_target: "dialog", action: "keydown.esc->dialog#close:stop click@document->dialog#closeOnClickOutside" } do %>
|
||||
<%= turbo_frame_tag "pins", src: my_pins_path %>
|
||||
<% end %>
|
||||
|
||||
<div class="tray__actions">
|
||||
<button class="txt-uppercase btn btn--reversed borderless fill-transparent txt-x-small translucent center full-width" data-action="click->dialog#close">Pinned</button>
|
||||
<button class="txt-uppercase btn btn--reversed borderless fill-transparent txt-x-small translucent center full-width" data-action="click->dialog#close">
|
||||
Pinned
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button class="tray__expander" data-action="dialog#toggle" aria-label="Expand pins stack">
|
||||
</button>
|
||||
<% end %>
|
||||
<button class="tray__expander" data-action="dialog#toggle" aria-label="Expand pins stack"></button>
|
||||
</section>
|
||||
|
||||
@@ -1,35 +1,37 @@
|
||||
<%= turbo_stream_from Current.user, :notifications %>
|
||||
|
||||
<dialog class="tray tray--notifications"
|
||||
data-controller="dialog" data-dialog-modal-value="false" data-dialog-target="dialog"
|
||||
data-action="keydown.esc->dialog#close:stop click@document->dialog#closeOnClickOutside">
|
||||
<%= turbo_frame_tag "notifications", src: tray_notifications_path, refresh: "morph" %>
|
||||
<section class="tray tray--notifications" data-controller="dialog">
|
||||
<dialog class="tray__dialog"
|
||||
data-dialog-modal-value="false" data-dialog-target="dialog"
|
||||
data-action="keydown.esc->dialog#close:stop click@document->dialog#closeOnClickOutside">
|
||||
<%= turbo_frame_tag "notifications", src: tray_notifications_path, refresh: "morph" %>
|
||||
|
||||
<div class="tray__item tray__item--overflow">
|
||||
<%= link_to notifications_path, class: "card", data: { action: "click->dialog#close" } do %>
|
||||
+ More…
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<button class="tray__expander" data-action="dialog#toggle" aria-label="Expand notifications stack"></button>
|
||||
<div class="tray__item tray__item--overflow">
|
||||
<%= link_to notifications_path, class: "card", data: { action: "click->dialog#close" } do %>
|
||||
+ More…
|
||||
<% end %>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<div class="tray__actions">
|
||||
<%= button_to read_all_notifications_path,
|
||||
class: "tray__notification-read-action btn btn--reversed fill-transparent borderless flex-item-justify-start shadow txt-xx-small",
|
||||
class: "tray__notification-read-action btn btn--reversed fill-transparent borderless flex-item-justify-start txt-xx-small",
|
||||
title: "Mark all as read",
|
||||
data: { action: "click->dialog#close", turbo_frame: "notifications" } do %>
|
||||
<%= icon_tag "check" %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to tag.span("Notifications"), notifications_path,
|
||||
class: "btn btn--reversed borderless fill-transparent shadow center txt-uppercase txt-x-small full-width",
|
||||
class: "btn btn--reversed borderless fill-transparent center txt-uppercase txt-x-small full-width",
|
||||
data: { action: "click->dialog#close" } %>
|
||||
|
||||
<%= link_to settings_notifications_path,
|
||||
class: "tray__notification-settings-action btn btn--reversed fill-transparent borderless flex-item-justify-start shadow txt-xx-small",
|
||||
class: "tray__notification-settings-action btn btn--reversed fill-transparent borderless flex-item-justify-start txt-xx-small",
|
||||
title: "Notification settings",
|
||||
data: { action: "click->dialog#close" } do %>
|
||||
<%= icon_tag "settings" %>
|
||||
<% end %>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<button class="tray__expander" data-action="dialog#toggle" aria-label="Expand notifications stack"></button>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user