Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 84 additions & 3 deletions src/anthias_server/app/static/sass/_styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -908,10 +908,23 @@ label { text-align: left; }
font-size: 0.9rem;
margin: 0 0 var(--space-3);
}
a {
// The call-to-action is an action (opens the Add modal), not
// navigation, so it's a <button> styled as a link.
a,
.empty-state__action {
font: inherit;
font-weight: 600;
padding: 0;
border: none;
background: none;
cursor: pointer;
color: var(--surface-anchor);
&:hover { color: var(--surface-anchor-hover); }
&:focus-visible {
outline: none;
border-radius: var(--radius-sm);
box-shadow: 0 0 0 var(--ring-width) var(--color-focus-ring);
}
}
}

Expand Down Expand Up @@ -939,7 +952,15 @@ label { text-align: left; }
width: 100%;
max-width: 720px;
margin: auto;
overflow: hidden;
// Cap the card to the viewport (minus the overlay's space-5 padding
// top + bottom) and scroll inside it, so a tall modal — the
// bulk-edit form with every group expanded, or the Edit modal with
// the failure alert + Advanced open — never pushes its footer below
// the fold. The header/footer pin via position: sticky so the title
// and the Save/Apply buttons stay reachable while the body scrolls.
// (overflow-y: auto still clips children to the rounded corners.)
max-height: calc(100vh - var(--space-5) * 2);
overflow-y: auto;
color: var(--color-text);
animation: modal-in 220ms var(--ease-out) both;
}
Expand All @@ -954,6 +975,13 @@ label { text-align: left; }
.modal-card__header {
padding: var(--space-5) var(--space-6);
border-bottom: 1px solid var(--color-border);
// Pin to the top of the scrolling card so the title + close button
// stay put while a tall body scrolls underneath. Opaque background
// so scrolled content doesn't bleed through.
position: sticky;
top: 0;
z-index: 2;
background: var(--color-surface);
display: flex;
// Top-aligned across every modal: only the preview modal can carry
// a long title (the asset name), but `flex-start` is also fine for
Expand Down Expand Up @@ -1030,6 +1058,11 @@ label { text-align: left; }
padding: var(--space-4) var(--space-6);
border-top: 1px solid var(--color-border);
background: var(--color-surface-soft);
// Pin to the bottom of the scrolling card so Save / Apply / Cancel
// stay reachable no matter how tall the body grows.
position: sticky;
bottom: 0;
z-index: 2;
display: flex;
align-items: center;
justify-content: flex-end;
Expand Down Expand Up @@ -2187,7 +2220,11 @@ body.auth-body {
background: var(--color-surface-tint);
transition: border-color var(--t-fast), background var(--t-fast);

&:hover {
// The dashed border + cloud icon read as "drop files here", so the
// zone now accepts drops (dropFiles() in home.ts). Mirror the hover
// treatment while a drag is over it for feedback.
&:hover,
&.is-dragover {
border-color: var(--color-link);
background: #ece4f5;
}
Expand Down Expand Up @@ -2444,15 +2481,27 @@ body.auth-body {
color: var(--color-text-on-dark);
background: transparent;
border-color: var(--color-border-on-dark);
// Set the utility "clear" apart from the destructive Delete it sits
// beside in the action row.
margin-left: var(--space-2);
}

@media (max-width: 640px) {
.bulk-bar__inner {
flex-direction: column;
align-items: stretch;
border-radius: var(--radius-lg);
// Anchor the clear (×) to the top-right corner so it doesn't wrap
// onto a second row right next to the red Delete (mis-tap risk).
position: relative;
}
.bulk-bar__actions { justify-content: center; }
.bulk-bar__clear {
position: absolute;
top: var(--space-3);
right: var(--space-3);
margin-left: 0;
}
}

// Bulk-edit modal: each schedule field group is a card whose body is
Expand All @@ -2471,3 +2520,35 @@ body.auth-body {
}
}
.bulk-field__toggle { cursor: pointer; }

// Reserve room at the bottom of the page while a selection is active so
// the fixed bulk bar never covers the last rows or their action
// buttons (it floats over them otherwise, blocking clicks on the very
// assets a bulk selection targets). Bound to `selectedIds.length` in
// home.html. The bar is taller when stacked on phones, hence the bump.
.app-container.has-bulk-selection {
padding-bottom: 7rem;
@media (max-width: 640px) { padding-bottom: 11rem; }
}

// Selection checkboxes on the dark Enabled surface. The default
// white-fill, faint-border checkbox and its purple checked/indeterminate
// fill nearly vanish against the purple gradient — lift the border and
// give the box a translucent-white ground so it reads as a control and
// its state is legible. Scoped to .asset-select so the per-row activity
// switch (also an .app-check-input) keeps its own track styling.
.surface--active .asset-select .app-check-input {
border-color: rgba(255, 255, 255, 0.5);
background-color: rgba(255, 255, 255, 0.1);

&:checked,
&:indeterminate { border-color: rgba(255, 255, 255, 0.75); }
}

// Stack the ffmpeg recipe's copy button under the (horizontally
// scrolling) command on narrow widths, instead of squeezing the code to
// a thin strip beside it.
@media (max-width: 480px) {
.modal-alert__recipe { flex-direction: column; }
.modal-alert__copy { align-self: stretch; }
}
23 changes: 23 additions & 0 deletions src/anthias_server/app/static/src/home.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
uploadFileName: string
uploadIndex: number
uploadTotal: number
dragActive: boolean
// Bulk selection / actions (#3046)
selectedIds: string[]
visibleIds: Record<SectionKey, string[]>
Expand All @@ -65,6 +66,7 @@
closePreview(): void
bindFlatpickr(): void
uploadFiles(input: HTMLInputElement): Promise<void>
dropFiles(event: DragEvent): void
uploadOne(url: string, csrf: string, file: File): Promise<UploadResult>
// Bulk selection helpers
isSelected(id: string): boolean
Expand Down Expand Up @@ -126,6 +128,7 @@
uploadFileName: '',
uploadIndex: 0,
uploadTotal: 0,
dragActive: false,
selectedIds: [],
Comment thread
vpetersson marked this conversation as resolved.
visibleIds: { active: [], inactive: [] },
bulkEditOpen: false,
Expand Down Expand Up @@ -239,6 +242,11 @@
// anyway.
this.mode = null
this.editAsset = null
// Clear any leftover drag highlight: dragging into the dropzone and
// then closing the modal (Esc/backdrop/Cancel) before dragleave
// fires would otherwise leave dragActive true, so the dropzone
// re-opens still highlighted.
this.dragActive = false
if (!this.uploadState) {
this.uploadProgress = 0
this.uploadFileName = ''
Expand Down Expand Up @@ -398,6 +406,21 @@
}
},

// Drag-and-drop entry point for the dropzone. Assigns the dropped
// FileList to the hidden <input> (so input.form / re-select still
// behave) and runs it through the same sequential uploadFiles()
// batch path the input's change event uses.
dropFiles(event: DragEvent) {
const dropped = event.dataTransfer?.files
if (!dropped || !dropped.length || this.uploadState) return

Check warning on line 415 in src/anthias_server/app/static/src/home.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=Screenly_screenly-ose&issues=AZ62IJthzxzPhDtxZclV&open=AZ62IJthzxzPhDtxZclV&pullRequest=3066
const input = document.getElementById(
'add-file',
) as HTMLInputElement | null
if (!input) return
input.files = dropped
void this.uploadFiles(input)
},

// POST a single file and resolve an UploadResult: 'ok' on a 2xx
// with no error toast, 'rejected' on a 2xx carrying an error toast
// (server refused the file), 'error' on a non-2xx or transport
Expand Down
10 changes: 9 additions & 1 deletion src/anthias_server/app/templates/_asset_modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,19 @@ <h2 x-text="mode === 'edit' ? 'Edit asset' : 'Add asset'"></h2>
>
{% csrf_token %}
<div class="modal-card__body" style="padding-top: 0">
{% comment %} The dashed dropzone reads as a drop target, so it
accepts drops as well as clicks. dropFiles() hands the
dropped FileList to the same uploadFiles() batch path the
<input>'s change event uses. {% endcomment %}
<label
for="add-file"
class="upload-dropzone"
:class="{ 'is-disabled': uploadState }"
:class="{ 'is-disabled': uploadState, 'is-dragover': dragActive }"
x-show="!uploadState"
@dragover.prevent
@dragenter.prevent="dragActive = true"
@dragleave.self.prevent="dragActive = false"
@drop.prevent="dragActive = false; dropFiles($event)"
Comment thread
Copilot marked this conversation as resolved.
>
<i class="ti ti-cloud-upload" style="font-size: 2.25rem; opacity: 0.55"></i>
<p class="mb-1 font-semibold mt-2">Click to choose files</p>
Expand Down
2 changes: 1 addition & 1 deletion src/anthias_server/app/templates/_bulk_edit_modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ <h2>Edit
</label>
<div class="mt-3" x-show="applyDuration" x-cloak>
<div class="app-floating">
<input type="number" class="app-input" id="bulk-duration" name="duration" min="0" step="1" placeholder="e.g. 10" required :disabled="!applyDuration">
<input type="number" class="app-input" id="bulk-duration" name="duration" min="0" step="1" required :disabled="!applyDuration">
<label for="bulk-duration">Duration (seconds)</label>
</div>
<p class="modal-section__hint mt-2 mb-0">
Expand Down
4 changes: 2 additions & 2 deletions src/anthias_server/app/templates/_empty_assets.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
{% else %}Assets you toggle off — or that haven't been enabled yet — appear here.
{% endif %}
</p>
<a href="#" @click.prevent="openAdd()">
<button type="button" class="empty-state__action" @click="openAdd()">
<i class="ti ti-plus mr-1"></i>Add an asset
</a>
</button>
</div>
2 changes: 1 addition & 1 deletion src/anthias_server/app/templates/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
{% endblock %}

{% block main %}
<div class="app-container" x-data="homeApp()" @asset-saved.window="closeModal()" @bulk-done.window="onBulkDone($event)">
<div class="app-container" :class="{ 'has-bulk-selection': selectedIds.length }" x-data="homeApp()" @asset-saved.window="closeModal()" @bulk-done.window="onBulkDone($event)">
<div class="page-header-bar">
<div class="page-header-bar__title">
<h1 class="page-header">Schedule Overview</h1>
Expand Down