Merge branch 'main' into mobile/prepare-webviews-2
* main: (100 commits) Fix notification broadcast test for turbo-rails 2.0.21 Update `turbo-rails` to get latest Turbo version Remove reference to removed controller Fix bad formatting on bridge page title attr Use private functions for bridge components Setup focus handling on touch target's connect callback Setup proper focus handling for mobile on the card perma's board picker Move dialog focus handling into the dialog controller Create popup initial alignment classes Fix class typo Hide the board selector button if card is closed Align board and tag picker dialogs without using math Add dialog manager to card perma Fix "M" hotkey using stale user from fragment cache Solve problem when Action Text raises on missing attachables Solve problem when Action Text raises on missing attachables Completing a step removes stalled status from UI Bump Bootsnap to v1.21.1 Add Enable all/Disable all buttons to webhook event selection Use correct class selectors to target cards with background images ...
This commit is contained in:
+4
-4
@@ -1,8 +1,8 @@
|
||||
GIT
|
||||
remote: https://github.com/basecamp/lexxy
|
||||
revision: 7c197c0afc7095c89df9cb6e24484df9e7212ac8
|
||||
revision: 4f0fc4d5773bc6892de70f175440c259974c12a7
|
||||
specs:
|
||||
lexxy (0.1.24.beta)
|
||||
lexxy (0.7.0.beta)
|
||||
rails (>= 8.0.2)
|
||||
|
||||
GIT
|
||||
@@ -147,7 +147,7 @@ GEM
|
||||
benchmark (0.5.0)
|
||||
bigdecimal (4.0.1)
|
||||
bindex (0.8.1)
|
||||
bootsnap (1.20.1)
|
||||
bootsnap (1.21.1)
|
||||
msgpack (~> 1.2)
|
||||
brakeman (7.1.2)
|
||||
racc
|
||||
@@ -449,7 +449,7 @@ GEM
|
||||
timeout (0.6.0)
|
||||
trilogy (2.9.0)
|
||||
tsort (0.2.0)
|
||||
turbo-rails (2.0.20)
|
||||
turbo-rails (2.0.21)
|
||||
actionpack (>= 7.1.0)
|
||||
railties (>= 7.1.0)
|
||||
tzinfo (2.0.6)
|
||||
|
||||
+4
-4
@@ -21,9 +21,9 @@ GIT
|
||||
|
||||
GIT
|
||||
remote: https://github.com/basecamp/lexxy
|
||||
revision: 7c197c0afc7095c89df9cb6e24484df9e7212ac8
|
||||
revision: 4f0fc4d5773bc6892de70f175440c259974c12a7
|
||||
specs:
|
||||
lexxy (0.1.24.beta)
|
||||
lexxy (0.7.0.beta)
|
||||
rails (>= 8.0.2)
|
||||
|
||||
GIT
|
||||
@@ -225,7 +225,7 @@ GEM
|
||||
benchmark (0.5.0)
|
||||
bigdecimal (4.0.1)
|
||||
bindex (0.8.1)
|
||||
bootsnap (1.20.1)
|
||||
bootsnap (1.21.1)
|
||||
msgpack (~> 1.2)
|
||||
brakeman (7.1.2)
|
||||
racc
|
||||
@@ -568,7 +568,7 @@ GEM
|
||||
timeout (0.6.0)
|
||||
trilogy (2.9.0)
|
||||
tsort (0.2.0)
|
||||
turbo-rails (2.0.20)
|
||||
turbo-rails (2.0.21)
|
||||
actionpack (>= 7.1.0)
|
||||
railties (>= 7.1.0)
|
||||
tzinfo (2.0.6)
|
||||
|
||||
@@ -378,6 +378,7 @@
|
||||
}
|
||||
|
||||
.cards__menu {
|
||||
position: relative;
|
||||
z-index: var(--z-popup);
|
||||
}
|
||||
|
||||
@@ -585,17 +586,28 @@
|
||||
}
|
||||
}
|
||||
|
||||
.card:has(.card__background img:not([src=""])) {
|
||||
&:has(.card__background img:not([src=""])) {
|
||||
.card__content,
|
||||
.card__meta,
|
||||
.card__comments,
|
||||
.card__column-name:not(.card__column-name--current) {
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
@media (any-hover: hover) {
|
||||
.card:hover & {
|
||||
@media (any-hover: hover) {
|
||||
&:hover {
|
||||
.card__content,
|
||||
.card__footer,
|
||||
.card__comments,
|
||||
.card__column-name:not(.card__column-name--current) {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.card__background img {
|
||||
filter: blur(3px) brightness(1.2);
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -604,13 +616,6 @@
|
||||
inset-inline-start: 100%;
|
||||
translate: -90% -40%;
|
||||
}
|
||||
|
||||
@media (any-hover: hover) {
|
||||
.card:has(.card__background img:not([src=""])):hover .card__background img:not([src=""]) {
|
||||
filter: blur(3px) brightness(1.2);
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Considering
|
||||
|
||||
@@ -119,17 +119,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.board-picker__button {
|
||||
--btn-border-size: 0;
|
||||
|
||||
font-size: 0.5em;
|
||||
margin-inline-start: 1ch;
|
||||
|
||||
+ .panel {
|
||||
--panel-size: 18ch;
|
||||
}
|
||||
}
|
||||
|
||||
.bubble {
|
||||
--bubble-number-max: 42px;
|
||||
--bubble-size: 6rem;
|
||||
@@ -292,10 +281,7 @@
|
||||
|
||||
/* Overlap the card BG by half the button height */
|
||||
&:has(.btn) {
|
||||
&,
|
||||
~ .card-perma__closure-message {
|
||||
translate: 0 calc(-1 * var(--half-btn-height));
|
||||
}
|
||||
translate: 0 calc(-1 * var(--half-btn-height));
|
||||
}
|
||||
|
||||
form {
|
||||
@@ -308,12 +294,6 @@
|
||||
--btn-color: var(--color-ink-inverted);
|
||||
}
|
||||
|
||||
.btn--plain {
|
||||
--btn-color: var(--card-color);
|
||||
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.btn--reversed {
|
||||
--btn-background: var(--color-canvas);
|
||||
--btn-color: var(--card-color);
|
||||
@@ -341,11 +321,25 @@
|
||||
.card-perma__closure-message {
|
||||
color: var(--card-color);
|
||||
grid-area: closure-message;
|
||||
margin-block-start: 0.5ch;
|
||||
margin-block: var(--block-space) var(--block-space-double);
|
||||
padding-inline: 1ch;
|
||||
|
||||
form {
|
||||
display: inline;
|
||||
.btn--plain {
|
||||
--btn-color: var(--card-color);
|
||||
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media (max-width: 799px) {
|
||||
margin-block: var(--block-space-half);
|
||||
translate: 0 calc(-0.5 * var(--half-btn-height));
|
||||
}
|
||||
|
||||
@media (min-width: 800px) {
|
||||
.card-perma__notch--bottom:has(.btn) ~ & {
|
||||
margin-block: var(--block-space-half) var(--block-space);
|
||||
translate: 0 calc(-0.5 * var(--half-btn-height));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,21 +71,19 @@
|
||||
min-inline-size: 0;
|
||||
padding-block: 0.25lh;
|
||||
padding-inline: var(--card-padding-inline) 1ch;
|
||||
position: relative;
|
||||
transition: background-color 100ms ease-out;
|
||||
|
||||
&:has(.board-picker__button) {
|
||||
cursor: pointer;
|
||||
transition: background-color 100ms ease-out;
|
||||
|
||||
&:has(.btn) {
|
||||
@media (any-hover: hover) {
|
||||
&:hover {
|
||||
background-color: color-mix(in srgb, var(--card-color) 90%, var(--color-ink));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:has(.btn:focus-visible) {
|
||||
outline: var(--focus-ring-size) solid var(--focus-ring-color);
|
||||
outline-offset: var(--focus-ring-offset);
|
||||
}
|
||||
dialog {
|
||||
inset-block-start: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,9 +97,11 @@
|
||||
}
|
||||
|
||||
.card__board-name {
|
||||
align-items: center;
|
||||
border-inline-start: 1px solid color-mix(in hsl, transparent 75%, currentColor);
|
||||
color: currentColor;
|
||||
display: flex;
|
||||
gap: 0.25ch;
|
||||
margin-inline-start: var(--card-header-space);
|
||||
max-inline-size: 100%;
|
||||
min-inline-size: 0;
|
||||
@@ -109,14 +109,59 @@
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.card__board-picker-button {
|
||||
inset: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.card__tags {
|
||||
--btn-color: var(--card-color);
|
||||
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
color: var(--card-text-color);
|
||||
display: flex;
|
||||
gap: var(--card-header-space);
|
||||
gap: 0.5ch;
|
||||
min-inline-size: 0;
|
||||
|
||||
[data-controller="dialog"] {
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.popup {
|
||||
--panel-size: 18ch;
|
||||
|
||||
inset-block-start: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.card__tag-picker {
|
||||
--panel-border-radius: 2em;
|
||||
--panel-padding: 0.5em 0.7em;
|
||||
--panel-size: max-content;
|
||||
|
||||
inline-size: auto !important;
|
||||
inset: 0 auto auto 0;
|
||||
max-inline-size: var(--panel-size) !important;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
|
||||
&[open] {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.input {
|
||||
--input-padding: 0.2em 0.5em;
|
||||
|
||||
inline-size: 18ch;
|
||||
}
|
||||
}
|
||||
|
||||
.card__tag-picker-button {
|
||||
font-size: 0.6em;
|
||||
}
|
||||
|
||||
.card__tag {
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
|
||||
figure.node--selected {
|
||||
figure.node--selected,
|
||||
div.node--selected {
|
||||
&:has(img) {
|
||||
img {
|
||||
outline: var(--focus-ring-size) solid var(--focus-ring-color);
|
||||
@@ -22,6 +23,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
.lexxy-content__table-wrapper {
|
||||
margin: 0;
|
||||
margin-block: 1ch;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
th, td {
|
||||
&.table-cell--selected {
|
||||
background-color: var(--color-selected-light);
|
||||
}
|
||||
|
||||
&.lexxy-content__table-cell--selected {
|
||||
background-color: var(--color-selected);
|
||||
border-color: var(--color-selected-dark);
|
||||
}
|
||||
}
|
||||
|
||||
&.lexxy-content__table--selection {
|
||||
::selection {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Lexical uses the `lexxy-editor--empty` class even if you have a list
|
||||
* started but haven't added other characters. Here, we hide the placeholder
|
||||
* when you click the List button in the toolbar. */
|
||||
@@ -101,6 +127,11 @@
|
||||
/* Toolbar
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* TODO: Temporary - hide table button while we work on the styles */
|
||||
button[name="table"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
lexxy-toolbar {
|
||||
--lexxy-toolbar-icon-size: 1em;
|
||||
|
||||
@@ -115,7 +146,7 @@
|
||||
position: relative;
|
||||
position: sticky;
|
||||
inset-block-start: 0;
|
||||
z-index: 1;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.lexxy-editor__toolbar-button {
|
||||
@@ -166,7 +197,7 @@
|
||||
inset-inline-end: 0;
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.lexxy-editor__toolbar-spacer {
|
||||
@@ -188,7 +219,7 @@
|
||||
font-size: var(--text-small);
|
||||
padding: var(--lexxy-dropdown-padding);
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
z-index: 2;
|
||||
|
||||
button {
|
||||
block-size: var(--lexxy-dropdown-btn-size);
|
||||
@@ -288,7 +319,130 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Prompt ,enu (@mentions, etc.)
|
||||
.lexxy-table-handle-buttons {
|
||||
--button-size: 2.5em;
|
||||
|
||||
color: var(--color-ink-inverted);
|
||||
display: none;
|
||||
flex-direction: row;
|
||||
font-size: var(--text-x-small);
|
||||
gap: 0.25ch;
|
||||
line-height: 1;
|
||||
position: absolute;
|
||||
transform: translate(-50%, -120%);
|
||||
z-index: 1;
|
||||
|
||||
&:has([open]) {
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.lexxy-table-control {
|
||||
align-items: center;
|
||||
background-color: var(--color-ink);
|
||||
border-radius: 0.75ch;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 1ch;
|
||||
padding: 2px;
|
||||
white-space: nowrap;
|
||||
|
||||
button,
|
||||
summary {
|
||||
aspect-ratio: 1 / 1;
|
||||
align-items: center;
|
||||
background-color: transparent;
|
||||
border-radius: 0.5ch;
|
||||
border: 0;
|
||||
color: var(--color-ink-inverted);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
font-size: inherit;
|
||||
font-weight: 700;
|
||||
justify-content: center;
|
||||
line-height: 1;
|
||||
list-style: none;
|
||||
min-block-size: var(--button-size);
|
||||
min-inline-size: var(--button-size);
|
||||
padding: 0;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-ink-darker);
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:focus-visible {
|
||||
background-color: var(--color-ink-darker);
|
||||
outline: var(--focus-ring-size) solid var(--focus-ring-color);
|
||||
outline-offset: var(--focus-ring-offset);
|
||||
}
|
||||
|
||||
svg {
|
||||
block-size: 1em;
|
||||
inline-size: 1em;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lexxy-table-control__more-menu {
|
||||
gap: 0;
|
||||
padding: 2px;
|
||||
position: relative;
|
||||
|
||||
summary {
|
||||
&::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.lexxy-table-control__more-menu-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25ch;
|
||||
inset-block-start: 105%;
|
||||
inset-inline-end: 0;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
|
||||
.lexxy-table-control__more-menu-section {
|
||||
background: var(--color-ink);
|
||||
border-radius: 0.75ch;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
button {
|
||||
aspect-ratio: unset;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
font-weight: normal;
|
||||
gap: 1ch;
|
||||
justify-content: flex-start;
|
||||
padding: 0.5ch 2ch;
|
||||
padding-inline-start: 1ch;
|
||||
white-space: nowrap;
|
||||
|
||||
svg {
|
||||
block-size: 1.3em;
|
||||
inline-size: 1.3em;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Prompt menu (@mentions, etc.)
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
.lexxy-prompt-menu {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
@layer native {
|
||||
[data-platform~=native] {
|
||||
--footer-height: 0;
|
||||
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
|
||||
.hide-on-native {
|
||||
@@ -27,11 +29,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Card columns
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
.board-tools.card {
|
||||
padding-block-start: 0;
|
||||
}
|
||||
|
||||
/* Card perma
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
.card-perma {
|
||||
margin-block-start: 0;
|
||||
|
||||
&:not(:has(.card-perma__notch-new-card-buttons)) .card-perma__bg {
|
||||
padding-block: clamp(0.25rem, 2vw, var(--padding-block));
|
||||
}
|
||||
}
|
||||
|
||||
.card-perma__bg {
|
||||
@@ -39,6 +52,11 @@
|
||||
border-start-end-radius: calc(0.2em + clamp(0.25rem, 2vw, var(--padding-block)));
|
||||
}
|
||||
|
||||
.card-perma__closure-message {
|
||||
margin-block: var(--block-space);
|
||||
translate: unset;
|
||||
}
|
||||
|
||||
/* Search
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
@@ -47,3 +65,25 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-bridge-components~=form] {
|
||||
[data-controller~=bridge--form] {
|
||||
[data-bridge--form-target~=submit] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-bridge-components~=overflow-menu] {
|
||||
[data-controller~=bridge--overflow-menu] {
|
||||
[data-bridge--overflow-menu-target~=item] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-bridge-components~=buttons] {
|
||||
[data-bridge--buttons-target~=button] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,16 @@
|
||||
display: var(--popup-display);
|
||||
}
|
||||
|
||||
/* The .pop-up--align-<drection> classes are used for initial alignment.
|
||||
* The orient JS helper layers on the .orient-<direction> to avoid running
|
||||
* off the edge of the screen and will override .popup--align */
|
||||
&:where(.popup--align-left),
|
||||
&.orient-left {
|
||||
inset-inline: auto 0;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
&:where(.popup--align-right),
|
||||
&.orient-right {
|
||||
inset-inline: 0 auto;
|
||||
transform: translateX(0);
|
||||
@@ -48,6 +53,10 @@
|
||||
.popup__title {
|
||||
font-weight: 800;
|
||||
white-space: nowrap;
|
||||
|
||||
&[tabindex="-1"]:focus-visible {
|
||||
outline: unset;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide lists when all the items within are hidden */
|
||||
@@ -123,6 +132,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
&:has(.popup__btn[disabled]) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.checked {
|
||||
display: none;
|
||||
}
|
||||
@@ -155,7 +168,7 @@
|
||||
max-inline-size: 100%;
|
||||
padding: var(--inline-space-half) var(--popup-item-padding-inline);
|
||||
text-align: start;
|
||||
|
||||
|
||||
&:focus-visible {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@@ -164,8 +164,8 @@
|
||||
.comments__subscribers,
|
||||
.card__meta-avatars--assignees > div > div:last-child,
|
||||
.steps > .step:last-child,
|
||||
div:has(> .tag-picker__button),
|
||||
div:has(> .board-picker__button),
|
||||
.card__board-name .icon,
|
||||
div:has(> .card__tag-picker-button),
|
||||
.delete-card,
|
||||
.header--card .header__title {
|
||||
display: none;
|
||||
|
||||
@@ -209,6 +209,45 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
.lexxy-content__table-wrapper {
|
||||
margin: 0;
|
||||
margin-block: 1ch;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
inline-size: calc(100% - 0.5ch);
|
||||
margin: 0.25ch;
|
||||
|
||||
th,
|
||||
td {
|
||||
border: 1px solid var(--color-ink-light);
|
||||
padding: 0.5ch 1ch;
|
||||
text-align: start;
|
||||
word-break: normal;
|
||||
|
||||
*:last-child {
|
||||
margin-block-end: 0;
|
||||
}
|
||||
|
||||
*:not(pre, code) {
|
||||
word-break: normal;
|
||||
}
|
||||
}
|
||||
|
||||
th,
|
||||
.lexxy-content__table-cell--header {
|
||||
background: var(--color-ink-lightest);
|
||||
}
|
||||
|
||||
td {
|
||||
background: var(--color-canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Attachments
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
@layer components {
|
||||
.tag-picker {
|
||||
--panel-border-radius: 2em;
|
||||
--panel-padding: 0.5em 0.7em;
|
||||
--panel-size: max-content;
|
||||
|
||||
inline-size: auto !important;
|
||||
inset: 0 auto auto 0;
|
||||
max-inline-size: var(--panel-size) !important;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
|
||||
&[open] {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.input {
|
||||
--input-padding: 0.2em 0.5em;
|
||||
|
||||
inline-size: 18ch;
|
||||
}
|
||||
}
|
||||
|
||||
.tag-picker__button {
|
||||
font-size: 0.6em;
|
||||
margin-block-start: 0.1em;
|
||||
margin-inline-start: 0.2em;
|
||||
|
||||
+ .panel {
|
||||
--panel-size: 18ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
class Cards::SelfAssignmentsController < ApplicationController
|
||||
include CardScoped
|
||||
|
||||
def create
|
||||
if @card.toggle_assignment(Current.user)
|
||||
respond_to do |format|
|
||||
format.turbo_stream { render "cards/assignments/create" }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
else
|
||||
respond_to do |format|
|
||||
format.turbo_stream { render "cards/assignments/create" }
|
||||
format.json { head :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -8,7 +8,7 @@ class Users::AvatarsController < ApplicationController
|
||||
if @user.system?
|
||||
redirect_to view_context.image_path("system_user.png")
|
||||
elsif @user.avatar.attached?
|
||||
redirect_to rails_blob_url(@user.avatar_thumbnail, disposition: "inline")
|
||||
redirect_to rails_blob_path(@user.avatar_thumbnail, disposition: "inline")
|
||||
elsif stale? @user, cache_control: cache_control
|
||||
render_initials
|
||||
end
|
||||
|
||||
@@ -48,7 +48,8 @@ module AccessesHelper
|
||||
params: { show_watchers: show_watchers, involvement: next_involvement(access.involvement), icon_only: icon_only },
|
||||
aria: { labelledby: dom_id(board, :involvement_label) },
|
||||
title: (label_text if icon_only),
|
||||
class: class_names("btn", { "btn--reversed": access.watching? && icon_only })) do
|
||||
class: class_names("btn", { "btn--reversed": access.watching? && icon_only }),
|
||||
data: !icon_only && { bridge__overflow_menu_target: "item", bridge_title: label_text }) do
|
||||
icon_tag("notification-bell-#{icon_only ? 'reverse-' : nil}#{access.involvement.dasherize}") +
|
||||
tag.span(label_text, class: class_names("txt-nowrap txt-uppercase", "for-screen-reader": icon_only), id: dom_id(board, :involvement_label))
|
||||
end
|
||||
|
||||
@@ -42,6 +42,6 @@ module AvatarsHelper
|
||||
end
|
||||
|
||||
def avatar_image_tag(user, **options)
|
||||
image_tag user_avatar_url(user, script_name: user.account.slug), aria: { hidden: "true" }, size: 48, title: user.name, **options
|
||||
image_tag user_avatar_path(user, script_name: user.account.slug), aria: { hidden: "true" }, size: 48, title: user.name, **options
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,7 +4,8 @@ module BoardsHelper
|
||||
end
|
||||
|
||||
def link_to_edit_board(board)
|
||||
link_to edit_board_path(board), class: "btn btn--circle-mobile", data: { controller: "tooltip" } do
|
||||
link_to edit_board_path(board), class: "btn btn--circle-mobile",
|
||||
data: { controller: "tooltip", bridge__overflow_menu_target: "item", bridge_title: "Board settings" } do
|
||||
icon_tag("settings") + tag.span("Settings for #{board.name}", class: "for-screen-reader")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
module BridgeHelper
|
||||
def bridge_icon(name)
|
||||
asset_url("#{name}.svg")
|
||||
end
|
||||
end
|
||||
@@ -16,6 +16,7 @@ module EntropyHelper
|
||||
{
|
||||
stalledAfterDays: card.entropy.days_before_reminder,
|
||||
lastActivitySpikeAt: card.last_activity_spike_at.iso8601,
|
||||
updatedAt: card.updated_at.iso8601,
|
||||
action: "Stalled"
|
||||
}
|
||||
end
|
||||
|
||||
@@ -40,6 +40,10 @@ module FiltersHelper
|
||||
}, &block
|
||||
end
|
||||
|
||||
def filter_title(title)
|
||||
tag.strong title, class: "popup__title pad-inline-half", tabindex: "-1", data: { dialog_target: "focusTouch" }
|
||||
end
|
||||
|
||||
def collapsible_nav_section(title, **properties, &block)
|
||||
tag.details class: "nav__section popup__section", data: { action: "toggle->nav-section-expander#toggle", nav_section_expander_target: "section", nav_section_expander_key_value: title.parameterize }, open: true, **properties do
|
||||
concat(tag.summary(class: "popup__section-title") do
|
||||
|
||||
@@ -9,4 +9,23 @@ module FormsHelper
|
||||
form_with(**attributes, data: data) { }
|
||||
end
|
||||
end
|
||||
|
||||
def bridged_form_with(**attributes, &)
|
||||
data = attributes.delete(:data) || {}
|
||||
controllers = [ data[:controller], "bridge--form" ].compact.join(" ").strip
|
||||
actions = [
|
||||
data[:action],
|
||||
"turbo:submit-start->bridge--form#submitStart",
|
||||
"turbo:submit-end->bridge--form#submitEnd"
|
||||
].compact.join(" ").strip
|
||||
|
||||
data[:controller] = controllers
|
||||
data[:action] = actions
|
||||
|
||||
if block_given?
|
||||
form_with **attributes, data: data, &
|
||||
else
|
||||
form_with(**attributes, data: data) { }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -25,7 +25,7 @@ module WebhooksHelper
|
||||
def link_to_webhooks(board, &)
|
||||
link_to board_webhooks_path(board_id: board),
|
||||
class: [ "btn btn--circle-mobile", { "btn--reversed": board.webhooks.any? } ],
|
||||
data: { controller: "tooltip" } do
|
||||
data: { controller: "tooltip", bridge__overflow_menu_target: "item", bridge_title: "Webhooks" } do
|
||||
icon_tag("world") + tag.span("Webhooks", class: "for-screen-reader")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
|
||||
import "@hotwired/turbo-rails"
|
||||
import "@hotwired/hotwire-native-bridge"
|
||||
import "initializers"
|
||||
import "controllers"
|
||||
|
||||
import "lexxy"
|
||||
import "@rails/actiontext"
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
|
||||
import { BridgeElement } from "@hotwired/hotwire-native-bridge"
|
||||
|
||||
export default class extends BridgeComponent {
|
||||
static component = "buttons"
|
||||
static targets = [ "button" ]
|
||||
|
||||
buttonTargetConnected() {
|
||||
this.notifyBridgeOfConnect()
|
||||
}
|
||||
|
||||
buttonTargetDisconnected() {
|
||||
if (!this.#isControllerTearingDown()) {
|
||||
this.notifyBridgeOfConnect()
|
||||
}
|
||||
}
|
||||
|
||||
notifyBridgeOfConnect() {
|
||||
const buttons = this.#enabledButtonTargets
|
||||
.map((target, index) => {
|
||||
const element = new BridgeElement(target)
|
||||
return { ...element.getButton(), index }
|
||||
})
|
||||
|
||||
this.send("connect", { buttons }, message => {
|
||||
this.#clickButton(message)
|
||||
})
|
||||
}
|
||||
|
||||
#clickButton(message) {
|
||||
const selectedIndex = message.data.selectedIndex
|
||||
this.#enabledButtonTargets[selectedIndex].click()
|
||||
}
|
||||
|
||||
get #enabledButtonTargets() {
|
||||
return this.buttonTargets
|
||||
.filter(target => !target.closest("[data-bridge-disabled]"))
|
||||
}
|
||||
|
||||
#isControllerTearingDown() {
|
||||
return !document.body.contains(this.element)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
|
||||
import { BridgeElement } from "@hotwired/hotwire-native-bridge"
|
||||
|
||||
export default class extends BridgeComponent {
|
||||
static component = "form"
|
||||
static targets = [ "submit", "cancel" ]
|
||||
static values = { submitTitle: String }
|
||||
|
||||
submitTargetConnected() {
|
||||
this.notifyBridgeOfConnect()
|
||||
this.#observeSubmitTarget()
|
||||
}
|
||||
|
||||
submitTargetDisconnected() {
|
||||
this.notifyBridgeOfDisonnect()
|
||||
this.submitObserver?.disconnect()
|
||||
}
|
||||
|
||||
notifyBridgeOfConnect() {
|
||||
const submitElement = new BridgeElement(this.submitTarget)
|
||||
const cancelElement = this.hasCancelTarget ? new BridgeElement(this.cancelTarget) : null
|
||||
|
||||
const submitButton = { title: submitElement.title }
|
||||
const cancelButton = cancelElement ? { title: cancelElement.title } : null
|
||||
|
||||
this.send("connect", { submitButton, cancelButton }, message => this.receive(message))
|
||||
}
|
||||
|
||||
receive(message) {
|
||||
switch (message.event) {
|
||||
case "submit":
|
||||
this.submitTarget.click()
|
||||
break
|
||||
case "cancel":
|
||||
this.cancelTarget.click()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
notifyBridgeOfDisonnect() {
|
||||
this.send("disconnect")
|
||||
}
|
||||
|
||||
submitStart() {
|
||||
this.send("submitStart")
|
||||
}
|
||||
|
||||
submitEnd() {
|
||||
this.send("submitEnd")
|
||||
}
|
||||
|
||||
#observeSubmitTarget() {
|
||||
this.submitObserver = new MutationObserver(() => {
|
||||
this.send(this.submitTarget.disabled ? "submitDisabled" : "submitEnabled")
|
||||
})
|
||||
|
||||
this.submitObserver.observe(this.submitTarget, {
|
||||
attributes: true,
|
||||
attributeFilter: [ "disabled" ]
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
|
||||
|
||||
// Bridge component to control custom safe-area insets from native apps.
|
||||
// Sets CSS variables --injected-safe-inset-(top|right|bottom|left).
|
||||
export default class extends BridgeComponent {
|
||||
static component = "insets"
|
||||
|
||||
connect() {
|
||||
super.connect()
|
||||
this.notifyBridgeOfConnect()
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
super.disconnect()
|
||||
this.send("disconnect")
|
||||
}
|
||||
|
||||
notifyBridgeOfConnect() {
|
||||
this.send("connect", {}, message => {
|
||||
this.#setInsets(message.data)
|
||||
})
|
||||
}
|
||||
|
||||
#setInsets({ top, right, bottom, left }) {
|
||||
const root = document.documentElement.style
|
||||
root.setProperty("--injected-safe-inset-top", `${top}px`)
|
||||
root.setProperty("--injected-safe-inset-right", `${right}px`)
|
||||
root.setProperty("--injected-safe-inset-bottom", `${bottom}px`)
|
||||
root.setProperty("--injected-safe-inset-left", `${left}px`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
|
||||
import { BridgeElement } from "@hotwired/hotwire-native-bridge"
|
||||
|
||||
export default class extends BridgeComponent {
|
||||
static component = "overflow-menu"
|
||||
static targets = [ "item" ]
|
||||
|
||||
itemTargetConnected() {
|
||||
this.notifyBridgeOfConnect()
|
||||
}
|
||||
|
||||
itemTargetDisconnected() {
|
||||
if (!this.#isControllerTearingDown) {
|
||||
this.notifyBridgeOfConnect()
|
||||
}
|
||||
}
|
||||
|
||||
notifyBridgeOfConnect() {
|
||||
const items = this.#enabledItemTargets
|
||||
.map((target, index) => {
|
||||
const element = new BridgeElement(target)
|
||||
return { title: element.title, index }
|
||||
})
|
||||
|
||||
this.send("connect", { items }, message => {
|
||||
this.#clickItem(message)
|
||||
})
|
||||
}
|
||||
|
||||
#clickItem(message) {
|
||||
const selectedIndex = message.data.selectedIndex
|
||||
this.#enabledItemTargets[selectedIndex].click()
|
||||
}
|
||||
|
||||
get #enabledItemTargets() {
|
||||
return this.itemTargets
|
||||
.filter(target => !target.closest("[data-bridge-disabled]"))
|
||||
}
|
||||
|
||||
#isControllerTearingDown() {
|
||||
return !document.body.contains(this.element)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
|
||||
|
||||
export default class extends BridgeComponent {
|
||||
static component = "text-size"
|
||||
|
||||
connect() {
|
||||
super.connect()
|
||||
this.notifyBridgeOfConnect()
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
super.disconnect()
|
||||
this.send("disconnect")
|
||||
}
|
||||
|
||||
notifyBridgeOfConnect() {
|
||||
this.send("connect", {}, message => {
|
||||
this.#setTextSize(message.data)
|
||||
})
|
||||
}
|
||||
|
||||
#setTextSize(data) {
|
||||
document.documentElement.dataset.textSize = data.textSize
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
|
||||
import { viewport } from "helpers/bridge/viewport_helpers"
|
||||
import { nextFrame } from "helpers/timing_helpers"
|
||||
|
||||
export default class extends BridgeComponent {
|
||||
static component = "title"
|
||||
static targets = [ "header" ]
|
||||
static values = { title: String }
|
||||
|
||||
async connect() {
|
||||
super.connect()
|
||||
await nextFrame()
|
||||
this.#startObserver()
|
||||
window.addEventListener("resize", this.#windowResized)
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
super.disconnect()
|
||||
this.#stopObserver()
|
||||
window.removeEventListener("resize", this.#windowResized)
|
||||
}
|
||||
|
||||
notifyBridgeOfVisibilityChange(visible) {
|
||||
this.send("visibility", { title: this.#title, elementVisible: visible })
|
||||
}
|
||||
|
||||
// Intersection Observer
|
||||
|
||||
#startObserver() {
|
||||
if (!this.hasHeaderTarget) return
|
||||
|
||||
this.observer = new IntersectionObserver(([ entry ]) =>
|
||||
this.notifyBridgeOfVisibilityChange(entry.isIntersecting),
|
||||
{ rootMargin: `-${this.#topOffset}px 0px 0px 0px` }
|
||||
)
|
||||
|
||||
this.observer.observe(this.headerTarget)
|
||||
this.previousTopOffset = this.#topOffset
|
||||
}
|
||||
|
||||
#stopObserver() {
|
||||
this.observer?.disconnect()
|
||||
}
|
||||
|
||||
#updateObserverIfNeeded() {
|
||||
if (this.#topOffset === this.previousTopOffset) return
|
||||
|
||||
this.#stopObserver()
|
||||
this.#startObserver()
|
||||
}
|
||||
|
||||
#windowResized = () => {
|
||||
this.#updateObserverIfNeeded()
|
||||
}
|
||||
|
||||
get #title() {
|
||||
return this.titleValue ? this.titleValue : document.title
|
||||
}
|
||||
|
||||
get #topOffset() {
|
||||
return viewport.top
|
||||
}
|
||||
}
|
||||
@@ -52,8 +52,11 @@ export default class extends Controller {
|
||||
this.#show()
|
||||
}
|
||||
|
||||
// Keep in sync with Card::Stallable#stalled? in app/models/card/stallable.rb
|
||||
get #isStalled() {
|
||||
return this.stalledValue.lastActivitySpikeAt && signedDifferenceInDays(new Date(this.stalledValue.lastActivitySpikeAt), new Date()) > this.stalledValue.stalledAfterDays
|
||||
return this.stalledValue.lastActivitySpikeAt &&
|
||||
signedDifferenceInDays(new Date(this.stalledValue.lastActivitySpikeAt), new Date()) > this.stalledValue.stalledAfterDays &&
|
||||
signedDifferenceInDays(new Date(this.stalledValue.updatedAt), new Date()) > this.stalledValue.stalledAfterDays
|
||||
}
|
||||
|
||||
#showStalled() {
|
||||
|
||||
@@ -79,7 +79,7 @@ export default class extends Controller {
|
||||
const selection = this.#selectedCard
|
||||
if (!selection) return
|
||||
|
||||
const url = selection.card.dataset.cardAssignToMeUrl
|
||||
const url = selection.card.dataset.cardAssignToSelfUrl
|
||||
if (url) {
|
||||
event.preventDefault()
|
||||
await post(url, { responseKind: "turbo-stream" })
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
import { orient } from "helpers/orientation_helpers"
|
||||
import { isTouchDevice } from "helpers/platform_helpers"
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = [ "dialog" ]
|
||||
static targets = [ "dialog", "focusMouse", "focusTouch" ]
|
||||
static values = {
|
||||
modal: { type: Boolean, default: false },
|
||||
sizing: { type: Boolean, default: true },
|
||||
@@ -14,6 +15,10 @@ export default class extends Controller {
|
||||
if (this.autoOpenValue) this.open()
|
||||
}
|
||||
|
||||
focusTouchTargetConnected() {
|
||||
this.#setupFocus()
|
||||
}
|
||||
|
||||
open() {
|
||||
const modal = this.modalValue
|
||||
|
||||
@@ -63,4 +68,10 @@ export default class extends Controller {
|
||||
captureKey(event) {
|
||||
if (event.key !== "Escape") { event.stopPropagation() }
|
||||
}
|
||||
|
||||
#setupFocus() {
|
||||
const touch = isTouchDevice()
|
||||
if (this.hasFocusMouseTarget) this.focusMouseTarget.autofocus = !touch
|
||||
if (this.hasFocusTouchTarget) this.focusTouchTarget.autofocus = touch
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
let top = 0
|
||||
const viewportTarget = window.visualViewport || window
|
||||
|
||||
export const viewport = {
|
||||
get top() {
|
||||
return top
|
||||
},
|
||||
get height() {
|
||||
return viewportTarget.height || window.innerHeight
|
||||
}
|
||||
}
|
||||
|
||||
function update() {
|
||||
requestAnimationFrame(() => {
|
||||
const styles = getComputedStyle(document.documentElement)
|
||||
const customInset = styles.getPropertyValue("--custom-safe-inset-top")
|
||||
const fallbackInset = styles.getPropertyValue("--safe-area-inset-top")
|
||||
const insetValue = (customInset || fallbackInset).trim()
|
||||
top = parseInt(insetValue || "0", 10) || 0
|
||||
})
|
||||
}
|
||||
|
||||
viewportTarget.addEventListener("resize", update)
|
||||
update()
|
||||
@@ -0,0 +1,18 @@
|
||||
import { BridgeElement } from "@hotwired/hotwire-native-bridge"
|
||||
|
||||
BridgeElement.prototype.getButton = function() {
|
||||
return {
|
||||
title: this.title,
|
||||
icon: this.getIcon()
|
||||
}
|
||||
}
|
||||
|
||||
BridgeElement.prototype.getIcon = function() {
|
||||
const url = this.bridgeAttribute(`icon-url`)
|
||||
|
||||
if (url) {
|
||||
return { url }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
import "initializers/current"
|
||||
import "initializers/bridge/bridge_element"
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
class Card::CleanInaccessibleDataJob < ApplicationJob
|
||||
discard_on ActiveJob::DeserializationError
|
||||
|
||||
def perform(card)
|
||||
card.clean_inaccessible_data
|
||||
end
|
||||
end
|
||||
+2
-11
@@ -1,5 +1,5 @@
|
||||
class Card < ApplicationRecord
|
||||
include Assignable, Attachments, Broadcastable, Closeable, Colored, Entropic, Eventable,
|
||||
include Accessible, Assignable, Attachments, Broadcastable, Closeable, Colored, Entropic, Eventable,
|
||||
Exportable, Golden, Mentions, Multistep, Pinnable, Postponable, Promptable,
|
||||
Readable, Searchable, Stallable, Statuses, Storage::Tracked, Taggable, Triageable, Watchable
|
||||
|
||||
@@ -46,12 +46,6 @@ class Card < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
delegate :accessible_to?, to: :board
|
||||
|
||||
def publicly_accessible?
|
||||
published? && board.publicly_accessible?
|
||||
end
|
||||
|
||||
def card
|
||||
self
|
||||
end
|
||||
@@ -135,16 +129,13 @@ class Card < ApplicationRecord
|
||||
end
|
||||
|
||||
remove_inaccessible_notifications_later
|
||||
clean_inaccessible_data_later
|
||||
end
|
||||
|
||||
def track_board_change_event(old_board_name)
|
||||
track_event "board_changed", particulars: { old_board: old_board_name, new_board: board.name }
|
||||
end
|
||||
|
||||
def grant_access_to_assignees
|
||||
board.accesses.grant_to(assignees)
|
||||
end
|
||||
|
||||
def assign_number
|
||||
self.number ||= account.increment!(:cards_count).cards_count
|
||||
end
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
module Card::Accessible
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
delegate :accessible_to?, to: :board
|
||||
end
|
||||
|
||||
def publicly_accessible?
|
||||
published? && board.publicly_accessible?
|
||||
end
|
||||
|
||||
def clean_inaccessible_data
|
||||
accessible_user_ids = board.accesses.pluck(:user_id)
|
||||
pins.where.not(user_id: accessible_user_ids).in_batches.destroy_all
|
||||
watches.where.not(user_id: accessible_user_ids).in_batches.destroy_all
|
||||
end
|
||||
|
||||
private
|
||||
def grant_access_to_assignees
|
||||
board.accesses.grant_to(assignees)
|
||||
end
|
||||
|
||||
def clean_inaccessible_data_later
|
||||
Card::CleanInaccessibleDataJob.perform_later(self)
|
||||
end
|
||||
end
|
||||
@@ -13,6 +13,7 @@ module Card::Stallable
|
||||
after_update_commit :detect_activity_spikes_later, if: :should_detect_activity_spikes?
|
||||
end
|
||||
|
||||
# Keep in sync with #isStalled in app/javascript/controllers/bubble_controller.js
|
||||
def stalled?
|
||||
if activity_spike.present?
|
||||
open? && last_activity_spike_at < STALLED_AFTER_LAST_SPIKE_PERIOD.ago && updated_at < STALLED_AFTER_LAST_SPIKE_PERIOD.ago
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<% @page_title = "Account Settings" %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<h1 class="header__title">
|
||||
<h1 class="header__title" data-bridge--title-target="header">
|
||||
<%= @page_title %>
|
||||
<% unless Current.user.admin? %>
|
||||
<div class="txt-normal font-weight-normal">Only admins can change these settings</div>
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
height: height %>
|
||||
<% elsif blob.audio? %>
|
||||
<audio controls="true" width="100%" preload="metadata">
|
||||
<source src="<%= rails_blob_url(blob) %>" type="<%= blob.content_type %>">
|
||||
<source src="<%= rails_blob_path(blob) %>" type="<%= blob.content_type %>">
|
||||
</audio>
|
||||
<% elsif blob.variable? %>
|
||||
<%= link_to url_for(blob.variant(variant)), data: { lightbox_target: "image", lightbox_caption_value: blob.filename.to_s } do %>
|
||||
<%= image_tag url_for(blob.variant(variant)), width: width, height: height %>
|
||||
<%= link_to rails_representation_path(blob.variant(variant)), data: { lightbox_target: "image", lightbox_caption_value: blob.filename.to_s } do %>
|
||||
<%= image_tag rails_representation_path(blob.variant(variant)), width: width, height: height %>
|
||||
<% end %>
|
||||
<% elsif blob.previewable? %>
|
||||
<%= image_tag url_for(blob.preview(variant)), width: width, height: height %>
|
||||
<%= image_tag rails_representation_path(blob.preview(variant)), width: width, height: height %>
|
||||
<% else %>
|
||||
<span class="attachment__icon"><%= blob.filename.extension&.downcase.presence || "unknown" %></span>
|
||||
<% end %>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= link_back_to_board(@board) %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title divider divider--fade full-width">
|
||||
<h1 class="header__title divider divider--fade full-width" data-bridge--title-target="header">
|
||||
<span class="overflow-ellipsis"><%= @page_title %></span>
|
||||
</h1>
|
||||
<% end %>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= link_back_to_board(@board) %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title divider divider--fade full-width">
|
||||
<h1 class="header__title divider divider--fade full-width" data-bridge--title-target="header">
|
||||
<span class="overflow-ellipsis"><%= @page_title %></span>
|
||||
</h1>
|
||||
<% end %>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= link_back_to_board(@column.board) %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title divider divider--fade full-width">
|
||||
<h1 class="header__title divider divider--fade full-width" data-bridge--title-target="header">
|
||||
<span class="overflow-ellipsis"><%= @page_title %></span>
|
||||
</h1>
|
||||
<% end %>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= link_back_to_board(@board) %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title divider divider--fade full-width">
|
||||
<h1 class="header__title divider divider--fade full-width" data-bridge--title-target="header">
|
||||
<span class="overflow-ellipsis"><%= @page_title %></span>
|
||||
</h1>
|
||||
<% end %>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= link_back_to_board(@board) %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title">
|
||||
<h1 class="header__title" data-bridge--title-target="header">
|
||||
<div><%= @page_title %></div>
|
||||
<% unless Current.user.can_administer_board?(@board) %>
|
||||
<div class="txt-normal font-weight-normal">Only admins can change these settings</div>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<% @page_title = "Create a new board" %>
|
||||
|
||||
<div class="panel panel--centered">
|
||||
<%= form_with model: @board, class: "flex flex-column gap", data: { controller: "form", action: "submit->form#preventEmptySubmit" } do |form| %>
|
||||
<%= bridged_form_with model: @board, class: "flex flex-column gap", data: { controller: "form", action: "submit->form#preventEmptySubmit" } do |form| %>
|
||||
<h1 class="txt-x-large margin-none font-weight-black"><%= @page_title %></h1>
|
||||
<%= form.text_field :name, required: true, class: "input full-width", autofocus: true, autocomplete: "off", placeholder: "Name it…", data: { form_target: "input", action: "keydown.esc@document->form#cancel", validation_message: "Board names can‘t be blank" } %>
|
||||
|
||||
<button type="submit" class="btn btn--link center">
|
||||
<button type="submit" class="btn btn--link center" data-bridge--form-target="submit">
|
||||
<span>Create board</span>
|
||||
<%= icon_tag "arrow-right" %>
|
||||
</button>
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
<%= turbo_stream_from @board %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<div class="header__actions header__actions--start">
|
||||
<div class="header__actions header__actions--start hide-on-native">
|
||||
<%= link_to_webhooks(@board) if Current.user.admin? %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title divider divider--fade full-width">
|
||||
<h1 class="header__title divider divider--fade full-width" data-bridge--title-target="header">
|
||||
<span class="overflow-ellipsis"><%= @board.name %></span>
|
||||
</h1>
|
||||
|
||||
<div class="header__actions header__actions--end">
|
||||
<div class="header__actions header__actions--end hide-on-native">
|
||||
<%= link_to_edit_board @board %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
<%= render "boards/show/expander", title: "Maybe?", count: board.cards.awaiting_triage.count, column_id: "maybe" %>
|
||||
<%= render "boards/show/menu/maximize", column_path: board_columns_stream_path(board) %>
|
||||
</header>
|
||||
<%= render "columns/show/add_card_button", board: board %>
|
||||
<div data-bridge-disabled="true">
|
||||
<%= render "columns/show/add_card_button", board: board %>
|
||||
</div>
|
||||
<div class="cards__list hide-scrollbar" data-drag-drop-item-container>
|
||||
<% if page.used? %>
|
||||
<%= with_automatic_pagination "maybe", @page do %>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<%= icon_tag "menu-dots-horizontal", class: "translucent" %>
|
||||
<span class="for-screen-reader">Column options</span>
|
||||
</button>
|
||||
<dialog class="popup panel flex-column gap fill-white shadow txt-small margin-block-double" data-dialog-target="dialog">
|
||||
<dialog class="popup popup--align-right panel flex-column gap fill-white shadow txt-small margin-block-double" data-dialog-target="dialog">
|
||||
<ul class="popup__list">
|
||||
<li class="popup__item">
|
||||
<button class="popup__btn btn" data-action="click->clicker#click">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<section id="<%= dom_id(card, :card_container) %>" class="card-perma" style="--card-color: <%= card.color %>;">
|
||||
<section id="<%= dom_id(card, :card_container) %>" class="card-perma" style="--card-color: <%= card.color %>;" data-controller="dialog-manager bridge--form">
|
||||
<% cache card do %>
|
||||
<div class="card-perma__actions card-perma__actions--left">
|
||||
<%= render "cards/container/gild", card: card if card.published? && !card.closed? %>
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
dialog_target: "dialog",
|
||||
navigable_list_focus_on_selection_value: false,
|
||||
navigable_list_actionable_items_value: true } do %>
|
||||
<strong class="popup__title pad-inline-half">Move this card to…</strong>
|
||||
<%= filter_title "Move this card to…" %>
|
||||
|
||||
<%= text_field_tag :search, nil, placeholder: "Filter…", class: "input input--transparent txt-small margin-block-half font-weight-normal", autofocus: true,
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", dialog_target: "focusMouse", action: "input->filter#filter" } %>
|
||||
|
||||
<ul class="popup__list margin-block-start-half margin-none-block-end" data-filter-target="list">
|
||||
<% @boards.each do |board| %>
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
<div class="display-contents" id="<%= dom_id(card, :card_closure_toggle) %>">
|
||||
<% if card.closed? %>
|
||||
<div class="card-perma__notch card-perma__notch--bottom flex-column">
|
||||
<div class="flex gap-half margin-block-start-double margin-block-end">
|
||||
<span class="card-perma__closure-message">
|
||||
Completed by <%= card.closed_by.name %> on <%= local_datetime_tag(card.closed_at, style: :shortdate) %>.
|
||||
<%= button_to card_closure_path(card), method: :delete, class: "btn btn--plain borderless fill-transparent" do %>
|
||||
<span class="pad-inline-end">Undo</span>
|
||||
<% end %>
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-perma__closure-message flex gap-half justify-center">
|
||||
<span>Completed by <%= card.closed_by.name %> on <%= local_datetime_tag(card.closed_at, style: :shortdate) %>.</span>
|
||||
<%= button_to card_closure_path(card), method: :delete, class: "btn btn--plain borderless fill-transparent" do %>
|
||||
<span class="pad-inline-end">Undo</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="card-perma__notch card-perma__notch--bottom flex-column">
|
||||
<div class="card-perma__notch card-perma__notch--bottom">
|
||||
<%= render "cards/container/closure_buttons", card: card %>
|
||||
</div>
|
||||
<% if card.entropic? && card.open? && !card.postponed? %>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<div class="flex gap-half">
|
||||
<%= link_to edit_card_path(card), class: "btn btn--circle-mobile borderless", data: { controller: "hotkey", action: "keydown.e@document->hotkey#click", turbo_frame: dom_id(card, :edit) } do %>
|
||||
<%= link_to edit_card_path(card), class: "btn btn--circle-mobile borderless",
|
||||
data: { controller: "hotkey", action: "keydown.e@document->hotkey#click", bridge__buttons_target: "button", bridge_title: "Edit", bridge_icon_url: bridge_icon("pencil"), turbo_frame: dom_id(card, :edit) } do %>
|
||||
<%= icon_tag "pencil", class: "icon--mobile-only" %>
|
||||
<span>Edit</span>
|
||||
<kbd class="txt-x-small hide-on-touch">e</kbd>
|
||||
<% end %>
|
||||
|
||||
<%= button_to card_closure_path(card), class: "btn btn--circle-mobile borderless",
|
||||
data: { controller: "hotkey", form_target: "submit", action: "keydown.d@document->hotkey#click" },
|
||||
data: { controller: "hotkey", form_target: "submit", bridge__buttons_target: "button", bridge_title: "Mark as Done", bridge_icon_url: bridge_icon("check"), action: "keydown.d@document->hotkey#click" },
|
||||
form: { data: { controller: "form" } } do %>
|
||||
<%= icon_tag "check", class: "icon--mobile-only" %>
|
||||
<span class="overflow-ellipsis">Mark as Done</span>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<% if card.golden? %>
|
||||
<%= button_to card_goldness_path(card), method: :delete, class: "btn btn--reversed btn--circle-mobile", data: { controller: "tooltip hotkey", action: "keydown.shift+g@document->hotkey#click" } do %>
|
||||
<%= button_to card_goldness_path(card), method: :delete, class: "btn btn--reversed btn--circle-mobile",
|
||||
data: { controller: "tooltip hotkey", action: "keydown.shift+g@document->hotkey#click", bridge__overflow_menu_target: "item", bridge_title: "Demote to normal" } do %>
|
||||
<%= icon_tag "golden-ticket" %>
|
||||
<span class="for-screen-reader">Demote to normal (shift+g)</span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= button_to card_goldness_path(card), class: "btn btn--circle-mobile", data: { controller: "tooltip hotkey", action: "keydown.shift+g@document->hotkey#click" } do %>
|
||||
<%= button_to card_goldness_path(card), class: "btn btn--circle-mobile",
|
||||
data: { controller: "tooltip hotkey", action: "keydown.shift+g@document->hotkey#click", bridge__overflow_menu_target: "item", bridge_title: "Promote to Golden Ticket" } do %>
|
||||
<%= icon_tag "golden-ticket" %>
|
||||
<span class="for-screen-reader">Promote to Golden Ticket (shift+g)</span>
|
||||
<% end %>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<% if card.image.attached? %>
|
||||
<%= button_to card_image_path(card), method: :delete, class: "btn", data: { controller: "tooltip" } do %>
|
||||
<%= button_to card_image_path(card), method: :delete, class: "btn",
|
||||
data: { controller: "tooltip", bridge__overflow_menu_target: "item", bridge_title: "Remove background image" } do %>
|
||||
<%= icon_tag "picture-remove" %>
|
||||
<span class="for-screen-reader">Remove background image</span>
|
||||
<% end %>
|
||||
<% elsif !card.closed? %>
|
||||
<%= form_with model: card, data: { controller: "form" } do |form| %>
|
||||
<label class="card-perma__image-btn btn input--file btn--circle-mobile" data-controller="tooltip">
|
||||
<label class="card-perma__image-btn btn input--file btn--circle-mobile" data-controller="tooltip" data-bridge--overflow-menu-target="item" data-bridge-title="Add background image">
|
||||
<%= icon_tag "picture-add" %>
|
||||
<span class="for-screen-reader">Add background image</span>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="card-perma__notch card-perma__notch--bottom flex-column">
|
||||
<%= button_tag type: :submit, form: dom_id(card, :edit_form), class: "btn borderless",
|
||||
title: "Save changes (#{ hotkey_label(["ctrl", "enter"]) })",
|
||||
data: { controller: "hotkey", action: "keydown.ctrl+enter@document->hotkey#click keydown.meta+enter@document->hotkey#click" } do %>
|
||||
data: { controller: "hotkey", bridge__form_target: "submit", action: "keydown.ctrl+enter@document->hotkey#click keydown.meta+enter@document->hotkey#click" } do %>
|
||||
<span>Save changes</span>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<div class="card-perma__notch-new-card-buttons">
|
||||
<%= button_to card_publish_path(card), name: "creation_type", value: "add", class: "btn",
|
||||
title: "Create card (#{ hotkey_label(["ctrl", "enter"]) })",
|
||||
form: { data: { controller: "form" } },
|
||||
data: { form_target: "submit", controller: "clicker", action: "keydown.ctrl+enter@document->clicker#click keydown.meta+enter@document->clicker#click" } do %>
|
||||
form: { data: { controller: "form bridge--form" } },
|
||||
data: { form_target: "submit", bridge__form_target: "submit", controller: "clicker", action: "keydown.ctrl+enter@document->clicker#click keydown.meta+enter@document->clicker#click" } do %>
|
||||
<span>Create card</span>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -11,9 +11,7 @@
|
||||
<% card_data[:card_not_now_url] = card_not_now_path(card) %>
|
||||
<% card_data[:card_closure_url] = card_closure_path(card) %>
|
||||
<% card_data[:action] = "mouseenter->navigable-list#hoverSelect" %>
|
||||
<% if Current.user %>
|
||||
<% card_data[:card_assign_to_me_url] = card_assignments_path(card, params: { assignee_id: Current.user.id }) %>
|
||||
<% end %>
|
||||
<% card_data[:card_assign_to_self_url] = card_self_assignment_path(card) %>
|
||||
<% end %>
|
||||
|
||||
<%= card_article_tag card, class: "card", draggable: draggable, data: card_data, tabindex: 0 do %>
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
<label class="card__board">
|
||||
<span class="card__id">
|
||||
<span class="for-screen-reader">Card number</span>
|
||||
<%= card.number %>
|
||||
</span>
|
||||
<span class="card__board-name">
|
||||
<span class="overflow-ellipsis"><%= card.board.name %></span>
|
||||
</span>
|
||||
<div class="card__id">
|
||||
<span class="for-screen-reader">Card number</span>
|
||||
<%= card.number %>
|
||||
</div>
|
||||
<div class="card__board-name">
|
||||
<span class="overflow-ellipsis"><%= card.board.name %></span>
|
||||
<%= yield %>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
<%= render "cards/display/common/board", card: card do %>
|
||||
<div class="align-self-center position-relative txt-small"
|
||||
data-controller="dialog"
|
||||
data-action="keydown.esc->dialog#close:stop click@document->dialog#closeOnClickOutside mouseenter->dialog#loadLazyFrames" <%= "hidden" if card.closed? %>>
|
||||
<button class="board-picker__button btn btn--reversed card__hide-on-index fill-transparent" data-action="click->dialog#open:stop">
|
||||
<%= icon_tag "caret-down" %>
|
||||
<span class="for-screen-reader">Choose a board for this card</span>
|
||||
</button>
|
||||
|
||||
<dialog class="popup panel flex-column align-start gap-half fill-white shadow margin-block-double" data-dialog-target="dialog" data-action="turbo:before-morph-attribute->dialog#preventCloseOnMorphing turbo:submit-end->dialog#close">
|
||||
<%= turbo_frame_tag "board_picker", src: edit_card_board_path(card), target: "_top", loading: :lazy, refresh: "morph" %>
|
||||
</dialog>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="card__board" data-controller="dialog" data-action="keydown.esc->dialog#close:stop click@document->dialog#closeOnClickOutside mouseenter->dialog#loadLazyFrames">
|
||||
<%= render "cards/display/common/board", card: card do %>
|
||||
<%= icon_tag "caret-down", class: "txt-xx-small", hidden: card.closed? %>
|
||||
<% end %>
|
||||
<button class="card__board-picker-button btn btn--plain" data-action="click->dialog#open:stop" aria-label="Choose a board for this card" <%= "hidden" if card.closed? %>></button>
|
||||
<dialog class="popup popup--align-right panel flex-column align-start gap-half fill-white shadow" data-dialog-target="dialog" data-action="turbo:before-morph-attribute->dialog#preventCloseOnMorphing turbo:submit-end->dialog#close">
|
||||
<%= turbo_frame_tag "board_picker", src: edit_card_board_path(card), target: "_top", loading: :lazy, refresh: "morph" %>
|
||||
</dialog>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div id="<%= dom_id(card, :tags) %>" class="card__tags">
|
||||
<div class="position-relative" data-controller="dialog"
|
||||
<div data-controller="dialog"
|
||||
data-action="keydown.esc->dialog#close click@document->dialog#closeOnClickOutside mouseenter->dialog#loadLazyFrames" <%= "hidden" if card.closed? %>>
|
||||
<button class="tag-picker__button btn card__hide-on-index" style="--btn-background: var(--card-bg-color);"
|
||||
<button class="card__tag-picker-button btn card__hide-on-index" style="--btn-background: var(--card-bg-color);"
|
||||
data-controller="tooltip hotkey" data-action="click->dialog#open:stop keydown.t@document->hotkey#click">
|
||||
<%= icon_tag "tag" %>
|
||||
<span class="for-screen-reader">Add a tag</span>
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
<%= render "cards/display/common/board", card: card %>
|
||||
<div class="card__board">
|
||||
<%= render "cards/display/common/board", card: card %>
|
||||
</div>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<%= general_prompts(@card.board) %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to "Close editor and discard changes", @card, data: { form_target: "cancel" }, hidden: true %>
|
||||
<%= link_to "Close editor and discard changes", @card,
|
||||
data: { form_target: "cancel", bridge__form_target: "cancel", bridge_title: "Cancel" }, hidden: true %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<%= render "cards/broadcasts", filter: @filter %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<h1 class="header__title divider divider--fade full-width">
|
||||
<h1 class="header__title divider divider--fade full-width" data-bridge--title-target="header">
|
||||
<span class="overflow-ellipsis txt-capitalize-first-letter"><%= @user_filtering.selected_boards_label %></span>
|
||||
</h1>
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<div id="<%= dom_id(card, :pin_button) %>">
|
||||
<% if card.pinned_by? Current.user %>
|
||||
<%= button_to card_pin_path(card), method: :delete, class: "btn btn--reversed btn--circle-mobile", data: { controller: "tooltip hotkey", action: "keydown.shift+p@document->hotkey#click" } do %>
|
||||
<%= button_to card_pin_path(card), method: :delete, class: "btn btn--reversed btn--circle-mobile",
|
||||
data: { controller: "tooltip hotkey", action: "keydown.shift+p@document->hotkey#click", bridge__overflow_menu_target: "item", bridge_title: "Un-pin this card" } do %>
|
||||
<%= icon_tag "pinned" %> <span class="for-screen-reader">Un-pin this card (shift+p)</span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= button_to card_pin_path(card), class: "btn btn--circle-mobile", data: { controller: "tooltip hotkey", action: "keydown.shift+p@document->hotkey#click" } do %>
|
||||
<%= button_to card_pin_path(card), class: "btn btn--circle-mobile",
|
||||
data: { controller: "tooltip hotkey", action: "keydown.shift+p@document->hotkey#click", bridge__overflow_menu_target: "item", bridge_title: "Pin this card" } do %>
|
||||
<%= icon_tag "unpinned" %> <span class="for-screen-reader">Pin this card (shift+p)</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
<%= turbo_stream.replace @step do %>
|
||||
<%= render "cards/steps/step", step: @step %>
|
||||
<% end %>
|
||||
|
||||
<%= turbo_stream.replace dom_id(@card, "bubble") do %>
|
||||
<%= render "cards/display/preview/bubble", card: @card %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<div id="<%= dom_id(card, :watch_button) %>">
|
||||
<% if card.watched_by? Current.user %>
|
||||
<%= button_to card_watch_path(card), method: :delete, class: "btn btn--reversed btn--circle-mobile", data: { controller: "tooltip hotkey", action: "keydown.shift+n@document->hotkey#click" } do %>
|
||||
<%= button_to card_watch_path(card), method: :delete, class: "btn btn--reversed btn--circle-mobile",
|
||||
data: { controller: "tooltip hotkey", action: "keydown.shift+n@document->hotkey#click", bridge__overflow_menu_target: "item", bridge_title: "Stop watching" } do %>
|
||||
<%= icon_tag "bell" %>
|
||||
<span class="for-screen-reader">Stop watching (shift+n)</span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= button_to card_watch_path(card), class: "btn btn--circle-mobile", data: { controller: "tooltip hotkey", action: "keydown.shift+n@document->hotkey#click" } do %>
|
||||
<%= button_to card_watch_path(card), class: "btn btn--circle-mobile",
|
||||
data: { controller: "tooltip hotkey", action: "keydown.shift+n@document->hotkey#click", bridge__overflow_menu_target: "item", bridge_title: "Watch this" } do %>
|
||||
<%= icon_tag "bell-off" %>
|
||||
<span class="for-screen-reader">Watch this (shift+n)</span>
|
||||
<% end %>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<div class="board-tools card">
|
||||
<%= button_to board_cards_path(board), method: :post, class: "btn btn--link", form: { data: { turbo_frame: "_top" } }, data: { controller: "hotkey", action: "keydown.c@document->hotkey#click" } do %>
|
||||
<%= button_to board_cards_path(board), method: :post, class: "btn btn--link",
|
||||
form: { data: { turbo_frame: "_top" } },
|
||||
data: { controller: "hotkey", action: "keydown.c@document->hotkey#click", bridge__buttons_target: "button", bridge_title: "Add card", bridge_icon_url: bridge_icon("add") } do %>
|
||||
<%= icon_tag "add", class: "show-on-touch" %>
|
||||
<span>Add a card</span>
|
||||
<kbd class="hide-on-touch">C</kbd>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= back_link_to "Activity", root_path, "keydown.left@document->hotkey#click keydown.esc@document->hotkey#click" %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title">
|
||||
<h1 class="header__title" data-bridge--title-target="header">
|
||||
<%= @column.title %>
|
||||
</h1>
|
||||
<% end %>
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
<% height = attachment.metadata["height"] %>
|
||||
|
||||
<% if attachment.previewable? %>
|
||||
<%= image_tag url_for(attachment.preview(variant)), class: "attachment attachment--image", width: width, height: height %>
|
||||
<%= image_tag rails_representation_path(attachment.preview(variant)), class: "attachment attachment--image", width: width, height: height %>
|
||||
<% elsif attachment.variable? %>
|
||||
<%= image_tag url_for(attachment.variant(variant)), class: "attachment attachment--image", width: width, height: height %>
|
||||
<%= image_tag rails_representation_path(attachment.variant(variant)), class: "attachment attachment--image", width: width, height: height %>
|
||||
<% else %>
|
||||
<div class="attachment attachment--file attachment--<%= attachment.filename.extension -%>">
|
||||
<span class="attachment__icon"><%= attachment.filename.extension&.downcase.presence || "unknown" %></span>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<% content_for :header do %>
|
||||
<%= render "events/index/add_card_button", user_filtering: @user_filtering %>
|
||||
|
||||
<h1 class="header__title" data-controller="dialog-manager">
|
||||
<h1 class="header__title" data-controller="dialog-manager" data-bridge--title-target="header">
|
||||
<% if @user_filtering.boards.many? %>
|
||||
<span>Activity <%= @user_filtering.filter.boards.any? ? "in" : "across" %></span>
|
||||
<% else %>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<div class="header__actions header__actions--end">
|
||||
<div>
|
||||
<%= link_to new_board_path, class: "btn btn--link btn--circle-mobile", data: { controller: "hotkey", action: "keydown.b@document->hotkey#click" } do %>
|
||||
<%= link_to new_board_path, class: "btn btn--link btn--circle-mobile",
|
||||
data: { controller: "hotkey", action: "keydown.b@document->hotkey#click", bridge__buttons_target: "button", bridge_title: "Add a board", bridge_icon_url: bridge_icon("board") } do %>
|
||||
<%= icon_tag "board" %>
|
||||
<span class="overflow-ellipsis">Add a board</span>
|
||||
<kbd class="hide-on-touch">B</kbd>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<div class="header__actions header__actions--start">
|
||||
<% if board = user_filtering.single_board_or_first %>
|
||||
<%= button_to board_cards_path(board), method: :post, class: "btn btn--link btn--circle-mobile", data: { controller: "hotkey", action: "keydown.c@document->hotkey#click" } do %>
|
||||
<%= button_to board_cards_path(board), method: :post, class: "btn btn--link btn--circle-mobile",
|
||||
data: { controller: "hotkey", action: "keydown.c@document->hotkey#click", bridge__buttons_target: "button", bridge_title: "Add a card", bridge_icon_url: bridge_icon("add") } do %>
|
||||
<%= icon_tag "add" %>
|
||||
<span class="overflow-ellipsis">Add a card</span>
|
||||
<kbd class="hide-on-touch">C</kbd>
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
</template>
|
||||
|
||||
<%= filter_dialog "Board…" do %>
|
||||
<strong class="popup__title">Board…</strong>
|
||||
<%= filter_title "Board…" %>
|
||||
<%= text_field_tag nil, nil, id: nil, placeholder: "Filter…", class: "input input--transparent txt-small font-weight-normal", autofocus: true,
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", dialog_target: "focusMouse", action: "input->filter#filter" } %>
|
||||
|
||||
<ul class="popup__list" data-filter-target="list" role="listbox">
|
||||
<% user_filtering.boards.each do |board| %>
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
</template>
|
||||
|
||||
<%= filter_dialog "Person…" do %>
|
||||
<strong class="popup__title">Person…</strong>
|
||||
<%= filter_title "Person…" %>
|
||||
<%= text_field_tag nil, nil, id: nil, placeholder: "Filter…", class: "input input--transparent txt-small font-weight-normal", autofocus: true,
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", dialog_target: "focusMouse", action: "input->filter#filter" } %>
|
||||
|
||||
<ul class="popup__list" data-filter-target="list" role="listbox">
|
||||
<% user_filtering.users.each do |user| %>
|
||||
|
||||
@@ -16,11 +16,10 @@
|
||||
</template>
|
||||
|
||||
<%= filter_dialog "Assigned to…" do %>
|
||||
<strong class="popup__title">Assigned to…</strong>
|
||||
|
||||
<%= filter_title "Assigned to…" %>
|
||||
<% if Current.account.users.active.many? %>
|
||||
<%= text_field_tag nil, nil, id: nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", dialog_target: "focusMouse", action: "input->filter#filter" } %>
|
||||
<% end %>
|
||||
|
||||
<ul class="popup__list" data-filter-target="list" role="listbox">
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
</template>
|
||||
|
||||
<%= filter_dialog "Board…" do %>
|
||||
<strong class="popup__title">Board…</strong>
|
||||
|
||||
<%= filter_title "Board…" %>
|
||||
<% if user_filtering.boards.many? %>
|
||||
<%= text_field_tag nil, nil, id: nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", dialog_target: "focusMouse", action: "input->filter#filter" } %>
|
||||
<% end %>
|
||||
|
||||
<ul class="popup__list" data-filter-target="list" role="listbox">
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
</template>
|
||||
|
||||
<%= filter_dialog "Closed by…" do %>
|
||||
<strong class="popup__title">Closed by…</strong>
|
||||
|
||||
<%= filter_title "Closed by…" %>
|
||||
<% if user_filtering.users.many? %>
|
||||
<%= text_field_tag nil, nil, id: nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", dialog_target: "focusMouse", action: "input->filter#filter" } %>
|
||||
<% end %>
|
||||
|
||||
<ul class="popup__list" data-filter-target="list" role="listbox">
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
</template>
|
||||
|
||||
<%= filter_dialog "Added by…" do %>
|
||||
<strong class="popup__title">Added by…</strong>
|
||||
|
||||
<%= filter_title "Added by…" %>
|
||||
<% if user_filtering.users.many? %>
|
||||
<%= text_field_tag nil, nil, id: nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", dialog_target: "focusMouse", action: "input->filter#filter" } %>
|
||||
<% end %>
|
||||
|
||||
<ul class="popup__list" data-filter-target="list" role="listbox">
|
||||
|
||||
@@ -15,11 +15,10 @@
|
||||
</template>
|
||||
|
||||
<%= filter_dialog "Tagged…" do %>
|
||||
<strong class="popup__title">Tagged…</strong>
|
||||
|
||||
<%= filter_title "Tagged…" %>
|
||||
<% if user_filtering.tags.many? %>
|
||||
<%= text_field_tag nil, nil, id: nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
|
||||
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", dialog_target: "focusMouse", action: "input->filter#filter" } %>
|
||||
<% end %>
|
||||
|
||||
<ul class="popup__list" data-filter-target="list" role="listbox">
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
<html lang="en">
|
||||
<%= render "layouts/shared/head" %>
|
||||
|
||||
<body class="<%= @body_class %>" data-controller="local-time timezone-cookie turbo-navigation theme" data-action="turbo:morph@window->local-time#refreshAll turbo:before-visit@document->turbo-navigation#rememberLocation" data-platform="<%= platform.type %>">
|
||||
<div id="global-container">
|
||||
<body class="<%= @body_class %>"
|
||||
data-controller="local-time timezone-cookie turbo-navigation theme bridge--title bridge--text-size bridge--insets"
|
||||
data-action="turbo:morph@window->local-time#refreshAll turbo:before-visit@document->turbo-navigation#rememberLocation"
|
||||
data-platform="<%= platform.type %>" data-bridge--title-title-value="<%= @page_title %>">
|
||||
<div id="global-container" data-controller="bridge--buttons bridge--overflow-menu">
|
||||
<header class="header header--mobile-actions-stack <%= @header_class %>" id="header">
|
||||
<a href="#main" class="header__skip-navigation btn" data-turbo="false">Skip to main content</a>
|
||||
<%= render "my/menu" if Current.user %>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= back_link_to "My profile", user_path(Current.user), "keydown.left@document->hotkey#click keydown.esc@document->hotkey#click" %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title"><%= @page_title %></h1>
|
||||
<h1 class="header__title" data-bridge--title-target="header"><%= @page_title %></h1>
|
||||
<% end %>
|
||||
|
||||
<section class="panel panel--wide shadow center webhooks">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= back_link_to "tokens", my_access_tokens_path, "keydown.left@document->hotkey#click keydown.esc@document->hotkey#click" %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title"><%= @page_title %></h1>
|
||||
<h1 class="header__title" data-bridge--title-target="header"><%= @page_title %></h1>
|
||||
<% end %>
|
||||
|
||||
<article class="panel panel--wide shadow center txt-align-start" style="view-transition-name: <%= dom_id(@access_token) %>">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= back_link_to "Tokens", my_access_tokens_path, "keydown.left@document->hotkey#click keydown.esc@document->hotkey#click" %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title"><%= @page_title %></h1>
|
||||
<h1 class="header__title" data-bridge--title-target="header"><%= @page_title %></h1>
|
||||
<% end %>
|
||||
|
||||
<article class="panel panel--wide shadow center txt-align-start" style="view-transition-name: <%= dom_id(@access_token) %>">
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<%= collapsible_nav_section "Accounts" do %>
|
||||
<%# Bust cache 1 Dec 2025 %>
|
||||
<% accounts.each do |account| %>
|
||||
<%= filter_place_menu_item landing_url(script_name: account.slug), account.name, "marker", current: account == Current.account, turbo: false %>
|
||||
<%= filter_place_menu_item landing_path(script_name: account.slug), account.name, "marker", current: account == Current.account, turbo: false %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<%= tag.li class: "popup__item", data: { filter_target: "item", navigable_list_target: "item" } do %>
|
||||
<%= icon_tag "logout", class: "popup__icon" %>
|
||||
<%= button_to session_url(script_name: nil), method: :delete, class: "popup__btn btn", data: { turbo: false } do %>
|
||||
<%= button_to session_path(script_name: nil), method: :delete, class: "popup__btn btn", data: { turbo: false } do %>
|
||||
<span>Sign out</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= back_link_to "Home", root_path, "keydown.left@document->hotkey#click keydown.esc@document->hotkey#click" %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title"><%= @page_title %></h1>
|
||||
<h1 class="header__title" data-bridge--title-target="header"><%= @page_title %></h1>
|
||||
<% end %>
|
||||
|
||||
<div class="pins-list panel panel--wide flex center borderless unpad flex-column gap-half margin-block-start">
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
<%= back_link_to "Home", root_path, "keydown.left@document->hotkey#click keydown.esc@document->hotkey#click" %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title"><%= @page_title %></h1>
|
||||
<h1 class="header__title" data-bridge--title-target="header"><%= @page_title %></h1>
|
||||
|
||||
<div class="header__actions header__actions--end">
|
||||
<%= link_to notifications_settings_path, class: "btn btn--circle-mobile" do %>
|
||||
<%= link_to notifications_settings_path, class: "btn btn--circle-mobile", data: { bridge__overflow_menu_target: "item", bridge_title: "Notification settings" } do %>
|
||||
<%= icon_tag "settings" %> <span class="for-screen-reader">Notification settings</span>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<% @page_title = "Notification Settings" %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<h1 class="header__title"><%= @page_title %></h1>
|
||||
<h1 class="header__title" data-bridge--title-target="header"><%= @page_title %></h1>
|
||||
<% end %>
|
||||
|
||||
<section class="settings margin-block-start-half">
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
#<%= card.number %> <%= card.title %>
|
||||
</template>
|
||||
<template type="editor">
|
||||
<%= link_to "##{card.number} #{card.title}", card_url(card) %>
|
||||
<%= link_to "##{card.number} #{card.title}", card_path(card) %>
|
||||
</template>
|
||||
</lexxy-prompt-item>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= back_link_to @board.name, published_board_url(@board), "keydown.left@document->hotkey#click keydown.esc@document->hotkey#click" %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title divider divider--fade full-width">
|
||||
<h1 class="header__title divider divider--fade full-width" data-bridge--title-target="header">
|
||||
<span class="overflow-ellipsis"><%= @page_title %></span>
|
||||
</h1>
|
||||
<% end %>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= back_link_to @board.name, published_board_url(@board), "keydown.left@document->hotkey#click keydown.esc@document->hotkey#click" %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title divider divider--fade full-width">
|
||||
<h1 class="header__title divider divider--fade full-width" data-bridge--title-target="header">
|
||||
<span class="overflow-ellipsis"><%= @page_title %></span>
|
||||
</h1>
|
||||
<% end %>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= back_link_to @column.board.name, published_board_url(@column.board), "keydown.left@document->hotkey#click keydown.esc@document->hotkey#click" %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title divider divider--fade full-width">
|
||||
<h1 class="header__title divider divider--fade full-width" data-bridge--title-target="header">
|
||||
<span class="overflow-ellipsis"><%= @page_title %></span>
|
||||
</h1>
|
||||
<% end %>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= back_link_to @board.name, published_board_url(@board), "keydown.left@document->hotkey#click keydown.esc@document->hotkey#click" %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title divider divider--fade full-width">
|
||||
<h1 class="header__title divider divider--fade full-width" data-bridge--title-target="header">
|
||||
<span class="overflow-ellipsis"><%= @page_title %></span>
|
||||
</h1>
|
||||
<% end %>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<% end %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<h1 class="header__title divider divider--fade full-width">
|
||||
<h1 class="header__title divider divider--fade full-width" data-bridge--title-target="header">
|
||||
<span class="overflow-ellipsis"><%= @page_title %></span>
|
||||
</h1>
|
||||
<% end %>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= back_link_to "Home", root_path, "keydown.left@document->hotkey#click keydown.esc@document->hotkey#click" %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title"><%= @page_title %></h1>
|
||||
<h1 class="header__title" data-bridge--title-target="header"><%= @page_title %></h1>
|
||||
<% end %>
|
||||
|
||||
<div class="search-perma margin-block-start">
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<% @accounts.each do |account| %>
|
||||
<li class="popup__item txt-medium">
|
||||
<%= icon_tag "marker", class: "popup__icon" %>
|
||||
<%= link_to landing_url(script_name: account.slug), class: "btn overflow-ellipsis fill-transparent popup__btn" do %>
|
||||
<%= link_to landing_path(script_name: account.slug), class: "btn overflow-ellipsis fill-transparent popup__btn" do %>
|
||||
<strong><%= account.name %></strong>
|
||||
<% end %>
|
||||
</li>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<% if Current.user == @user %>
|
||||
<hr class="separator--horizontal full-width flex-item-grow margin-block-start-double" style="--border-color: var(--color-ink-light);" aria-hidden="true">
|
||||
|
||||
<%= button_to session_url(script_name: nil), method: :delete, class: "btn txt-x-small center", data: { turbo: false } do %>
|
||||
<%= button_to session_path(script_name: nil), method: :delete, class: "btn txt-x-small center", data: { turbo: false } do %>
|
||||
<span>Sign out of Fizzy on this device</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
<ul class="flex flex-column align-start gap-half margin-none unpad">
|
||||
<%= form.collection_check_boxes \
|
||||
:subscribed_actions,
|
||||
webhook_action_options,
|
||||
:first,
|
||||
:last do |item| %>
|
||||
<li class="list-style-none margin-none">
|
||||
<label class="toggler__visible-when-off flex align-center gap cursor-pointer">
|
||||
<span class="switch txt-x-small flex-item-no-shrink">
|
||||
<%= item.check_box(class: "switch__input") %>
|
||||
<span class="switch__btn round"></span>
|
||||
</span>
|
||||
<span class="min-width"><%= item.text %></span>
|
||||
</label>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<div data-controller="toggle-class">
|
||||
<div class="flex align-center gap-half margin-block-end-half">
|
||||
<%= button_tag "Enable all", type: "button", class: "btn btn--plain txt-x-small txt-link font-weight-normal", data: { action: "click->toggle-class#checkAll" } %>
|
||||
<span class="txt-subtle">·</span>
|
||||
<%= button_tag "Disable all", type: "button", class: "btn btn--plain txt-x-small txt-link font-weight-normal", data: { action: "click->toggle-class#checkNone" } %>
|
||||
</div>
|
||||
<ul class="flex flex-column align-start gap-half margin-none unpad">
|
||||
<%= form.collection_check_boxes \
|
||||
:subscribed_actions,
|
||||
webhook_action_options,
|
||||
:first,
|
||||
:last do |item| %>
|
||||
<li class="list-style-none margin-none">
|
||||
<label class="toggler__visible-when-off flex align-center gap cursor-pointer">
|
||||
<span class="switch txt-x-small flex-item-no-shrink">
|
||||
<%= item.check_box(class: "switch__input", data: { toggle_class_target: "checkbox" }) %>
|
||||
<span class="switch__btn round"></span>
|
||||
</span>
|
||||
<span class="min-width"><%= item.text %></span>
|
||||
</label>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= link_back_to_board(@board) %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title"><%= @page_title %></h1>
|
||||
<h1 class="header__title" data-bridge--title-target="header"><%= @page_title %></h1>
|
||||
<% end %>
|
||||
|
||||
<%= tag.section class: "panel panel--wide shadow center webhooks" do %>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user