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
1 change: 1 addition & 0 deletions docs/src/ts/component/page/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Navigation uses `U.Router.go(path, options)`. Authentication guards redirect una
| `history.tsx` | Version history with `history/left.tsx`, `history/right.tsx` |
| `archive.tsx` | Trash/deleted objects |
| `archiveListTree.tsx` | Archive tree list view |
| `archiveSuggested.tsx` | Bin "Suggestions" tab, from `ObjectCleanupSuggestions`; orphans grouped by source object; `Delete permanently` + bulk `ObjectCleanupSuggestionIgnore`. Reports its count to `archive.tsx` for the tab badge |
| `invite.tsx` | Space invitation handler |
| `import.tsx` | Import handler |
| `membership.tsx` | Membership page |
Expand Down
3 changes: 2 additions & 1 deletion docs/src/ts/component/popup/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# popup/ - Modal Dialogs

Modal popup system with **33 TSX files** (plus 10 Storybook story files). Managed by the `PopupStore` in `src/ts/store/popup.ts`.
Modal popup system with **34 TSX files** (plus 10 Storybook story files). Managed by the `PopupStore` in `src/ts/store/popup.ts`.

## Architecture

Expand All @@ -27,6 +27,7 @@ Open a popup: `S.Popup.open('confirm', { data: { ... } })`
### Pages & Objects
- `page.tsx` - Full-page object viewer/editor in modal
- `objectManager.tsx` - Object management interface
- `cleanup.tsx` - Cascade-deletion confirmation: checkbox tree of orphan candidates (objects and files, nested by `createdInContext`) offered for archival. Opened from the `CleanupSuggestion` toast's "Review" action (`I.ToastAction.Cleanup`); confirms with `skipCascade=true`
- `relation.tsx` - Batch relation editor for multiple objects

### Auth & Security
Expand Down
1 change: 1 addition & 0 deletions src/json/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export default {
chatHistory: 500,
chatMessage: 200,
spacePreload: 2000,
cleanupSuggestions: 3000,
},

fileExtension: {
Expand Down
15 changes: 13 additions & 2 deletions src/json/text.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
"commonHelp": "Help",
"commonSelectAll": "Select all",
"commonDeselectAll": "Deselect all",
"commonCollapseAll": "Collapse all",
"commonExpandAll": "Expand all",
"commonAdd": "Add",
"commonDone": "Done",
"commonUnlink": "Unlink",
Expand Down Expand Up @@ -133,6 +135,7 @@
"commonAddToCollection": "Add to Collection",
"commonLinkTo": "Add Link to Object",
"commonRestore": "Restore",
"commonReview": "Review",
"commonRestoreFromBin": "Restore from Bin",
"commonMoveToBin": "Move to Bin",
"commonMoveTo": "Move to",
Expand Down Expand Up @@ -1507,6 +1510,9 @@
"popupObjectManagerArchiveTypeTextPlural": "Some objects use these types. Select the ones you also want to delete. Objects you don’t select will remain but lose their type.",
"popupObjectManagerArchiveTypeEmptyText": "There are no objects of this type",

"popupCleanupTitle": "Move nested items to Bin?",
"popupCleanupText": "Select the objects and files you’d like to move to Bin.",


"popupInviteRequestAccessTitle": "Access required",
"popupInviteRequestAccessTextData": "This space requires owner approval. Send a request and you'll be notified once it's approved.",
Expand Down Expand Up @@ -2244,9 +2250,8 @@
"toastIsUnlocked": "is unlocked",
"toastMovedTo": "%s moved to",
"toastMovedToBin": "%s moved to Bin",
"toastAutoArchivedToBin": "%s automatically moved to Bin",
"toastMovedFromBin": "%s restored",
"toastAutoRestoredFromBin": "%s automatically restored",
"toastCleanup": "%s can also be moved to Bin",
"toastMovedFrom": "%s moved from",
"toastAddedToCollection": "has been added to Collection",
"toastLinkedTo": "has been linked to",
Expand Down Expand Up @@ -3073,6 +3078,12 @@
"binSwitchToCompact": "Switch to compact view",
"binSwitchToDetailed": "Switch to detailed view",
"binStackCount": "+%d",
"binCleanupTab": "Suggestions",
"binCleanupDescription": "Objects and files no longer linked to the object they were created in, and not used in any other object.",
"binCleanupCreatedIn": "Created in",
"binCleanupLinkRemovedFrom": "Link removed from",
"binCleanupContextDeleted": "Deleted object",
"binCleanupIgnore": "Don’t suggest again",
"binDeleteDisabledTooltip": "You can only delete objects you created"

}
72 changes: 70 additions & 2 deletions src/scss/page/main/archive.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,22 @@
.titleWrapper {
.side.left { display: flex; flex-direction: row; align-items: center; gap: 0px 10px; flex-grow: 1; }
.side.left {
.title { @include text-header1; font-weight: 700; }
.tabs { display: flex; flex-direction: row; align-items: center; gap: 0px 20px; }
.tabs {
.tab { @include text-header1; display: flex; align-items: center; gap: 0px 8px; font-weight: 700; color: var(--color-text-tertiary); transition: color 0.15s ease; }
.tab:hover,
.tab.active { color: var(--color-text-primary); }
.tab {
.cnt {
@include text-common; display: flex; align-items: center; justify-content: center; height: 24px;
min-width: 24px; padding: 0px 7px; border-radius: 12px; font-weight: 500;
color: var(--color-text-secondary); background-color: var(--color-shape-highlight-medium);
// Nudge down: the 32px header line-box centres higher than the text's
// optical centre, so a badge aligned to it reads a couple px high.
position: relative; top: 2px;
}
}
}
}

.side.right { display: flex; flex-direction: row; align-items: center; gap: 0px 8px; flex-shrink: 0; }
Expand Down Expand Up @@ -42,6 +57,57 @@
}
}

// Reuses the Bin table's own structure (.listObject > .table > .row > .cell), so row
// height, the -36px checkbox gutter, hover highlight and the icon/name gap all match.
// Only the bits the Bin table has no concept of are defined here.
.listObject.archiveSuggested { flex-grow: 0; }
.listObject.archiveSuggested {
.table {
// No hover-fill on the header rows (title + group toggle); the caret is the cue.
.row.isTitle:hover::before,
.row.isGroup:hover::before { display: none; }

// Match the Bin's "Name" head row (12px/500, 6px vertical padding) so this first
// row is the same height and the list doesn't jump when switching tabs.
.row.isTitle { font-size: 12px; font-weight: 500; }
.row.isTitle > .cell { padding-top: 6px; padding-bottom: 6px; }
.row.isTitle {
.titleActions { display: flex; align-items: center; gap: 0px 16px; }
.titleActions {
.label { color: var(--color-text-secondary); line-height: 20px; white-space: nowrap; }
.label:hover { color: var(--color-text-primary); }
}
}

// A group row has no .cellCheck, so its name cell is :first-child and misses the
// .cell:nth-child(2) padding-left reset that item rows get — zero it explicitly,
// otherwise the section header sits indented past its own children.
.row.isGroup > .cell { padding: 16px 14px 4px 0px; }
.row.isGroup {
// Tighter than the item rows' 12px so the caret hugs the label and the source
// icon hugs its name.
.cellContent.isName .flex { gap: 0px 8px; }

// gap is uniform, so pull the source name back toward its icon (4px) while the
// caret/prefix keep the 8px gap — icon + name then read as one unit.
.iconObject + span { margin-left: -4px; }

// The ghost icon is inline SVG using fill=currentColor and, unlike other
// icons, is not wrapped in <Icon> — so it inherits the row's text colour
// unless we dim it here. Default icons are <img> and ignore this.
.iconObject { flex-shrink: 0; color: var(--color-text-tertiary); }
.prefix { @include text-small; color: var(--color-text-secondary); flex-shrink: 0; }
.name { font-weight: 600; }
.name.isMissing { color: var(--color-text-secondary); }

// Small disclosure chevron (arrow/button, 8px), rotated for collapse state.
.icon.groupArrow { flex-shrink: 0; color: var(--color-text-tertiary); transform: rotate(-90deg); transition: transform 0.15s ease; }
.icon.groupArrow.isExpanded { transform: rotate(0deg); }
}

}
}

.listObject { flex-grow: 1; margin-left: -36px; }
.listObject {
.table {
Expand Down Expand Up @@ -149,7 +215,9 @@
}

&.isDetailed {
.listObject {
// Detailed view is a Bin-only mode; the Suggestions section stays compact and has
// no view toggle, so keep its sizing out of here (it also carries .listObject).
.listObject:not(.archiveSuggested) {
.cell { padding-top: 16px !important; padding-bottom: 16px !important; }
.row.isHead > .cell { padding-top: 6px !important; padding-bottom: 6px !important; }

Expand Down
59 changes: 59 additions & 0 deletions src/scss/popup/cleanup.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
@import "~scss/_mixins";

.popups {
.popup.popupCleanup {
.innerWrap { width: 432px; max-height: calc(100vh - 128px); padding: 32px 32px 24px 32px; }
.content { height: 100%; display: flex; flex-direction: column; }

.textWrapper { flex-shrink: 0; padding-bottom: 16px; }
.textWrapper {
.title { @include text-header3; margin: 0px 0px 4px 0px; }
.label { color: var(--color-text-secondary); }
}

.items { flex-grow: 1; display: flex; flex-direction: column; min-height: 0px; }
.items {
.head { display: flex; align-items: center; gap: 0px 8px; padding: 0px 0px 8px 0px; }
.head {
.label { color: var(--color-text-secondary); }
}

.list { flex-grow: 1; overflow-y: auto; }
.list {
.row { display: flex; align-items: center; gap: 0px 8px; height: 40px; }
.row {
.info { display: flex; align-items: center; gap: 0px 6px; flex-grow: 1; min-width: 0px; }
.info {
.iconWrap { position: relative; flex-shrink: 0; width: 20px; height: 20px; }
.iconWrap {
.icon.expandArrow {
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) rotate(-90deg);
width: 16px; height: 16px; transition: transform 0.15s ease, opacity 0.15s ease;
color: var(--color-text-primary); opacity: 0; z-index: 1;
}
.icon.expandArrow.isExpanded { transform: translate(-50%, -50%) rotate(0deg); }
}

.name { @include text-overflow-nw; }

.stackBadge {
@include text-small; color: var(--color-text-secondary); background-color: var(--color-shape-highlight-medium);
border-radius: 15px; padding: 0px 6px; flex-shrink: 0; white-space: nowrap;
}
}

&.canExpand:hover .iconWrap,
&.isExpanded .iconWrap {
.iconObject { opacity: 0; }
.icon.expandArrow { opacity: 1; }
}
}
}
}

.buttons { flex-shrink: 0; display: flex; flex-direction: column; gap: 8px; padding-top: 16px; }
.buttons {
.button { width: 100%; }
}
}
}
1 change: 1 addition & 0 deletions src/scss/popup/common.scss
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ html.platformWindows {
}

@import "./about";
@import "./cleanup";
@import "./confirm";
@import "./export";
@import "./help";
Expand Down
6 changes: 6 additions & 0 deletions src/scss/theme/dark/common.scss
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ html.themeDark {
.inner {
.buttons {
.button { color: var(--color-text-primary); }

// Toast buttons are underline-only. The dark .button.black hover rule below
// carries an extra element (html) and so outranks toast.scss's transparent
// rule, painting a pill-shaped background behind the label. Restore it.
.button:not(.disabled):hover,
.button:not(.disabled).hover { background: transparent; }
}
}
}
Expand Down
Loading
Loading