@internetarchive/collection-browser 4.3.0 → 4.3.1-alpha-webdev8257.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js.map +1 -1
  3. package/dist/src/app-root.d.ts +11 -0
  4. package/dist/src/app-root.js +107 -0
  5. package/dist/src/app-root.js.map +1 -1
  6. package/dist/src/collection-browser.d.ts +8 -4
  7. package/dist/src/collection-browser.js +19 -20
  8. package/dist/src/collection-browser.js.map +1 -1
  9. package/dist/src/data-source/collection-browser-data-source.js.map +1 -1
  10. package/dist/src/manage/manage-bar.d.ts +7 -2
  11. package/dist/src/manage/manage-bar.js +25 -3
  12. package/dist/src/manage/manage-bar.js.map +1 -1
  13. package/dist/src/styles/tile-action-styles.d.ts +14 -0
  14. package/dist/src/styles/tile-action-styles.js +52 -0
  15. package/dist/src/styles/tile-action-styles.js.map +1 -0
  16. package/dist/src/tiles/base-tile-component.d.ts +17 -1
  17. package/dist/src/tiles/base-tile-component.js +48 -1
  18. package/dist/src/tiles/base-tile-component.js.map +1 -1
  19. package/dist/src/tiles/grid/item-tile.js +1 -0
  20. package/dist/src/tiles/grid/item-tile.js.map +1 -1
  21. package/dist/src/tiles/list/tile-list-compact-header.js +66 -46
  22. package/dist/src/tiles/list/tile-list-compact-header.js.map +1 -1
  23. package/dist/src/tiles/list/tile-list-compact.d.ts +1 -1
  24. package/dist/src/tiles/list/tile-list-compact.js +132 -100
  25. package/dist/src/tiles/list/tile-list-compact.js.map +1 -1
  26. package/dist/src/tiles/list/tile-list.d.ts +1 -1
  27. package/dist/src/tiles/list/tile-list.js +316 -298
  28. package/dist/src/tiles/list/tile-list.js.map +1 -1
  29. package/dist/src/tiles/models.d.ts +14 -0
  30. package/dist/src/tiles/models.js.map +1 -1
  31. package/dist/src/tiles/tile-dispatcher.d.ts +14 -0
  32. package/dist/src/tiles/tile-dispatcher.js +107 -4
  33. package/dist/src/tiles/tile-dispatcher.js.map +1 -1
  34. package/dist/src/tiles/tile-display-value-provider.js.map +1 -1
  35. package/dist/test/data-source/collection-browser-data-source.test.js +2 -2
  36. package/dist/test/data-source/collection-browser-data-source.test.js.map +1 -1
  37. package/dist/test/manage/manage-bar.test.d.ts +1 -0
  38. package/dist/test/manage/manage-bar.test.js +123 -1
  39. package/dist/test/manage/manage-bar.test.js.map +1 -1
  40. package/dist/test/tiles/list/tile-list-compact-header.test.js +12 -12
  41. package/dist/test/tiles/list/tile-list-compact-header.test.js.map +1 -1
  42. package/dist/test/tiles/list/tile-list.test.js +134 -134
  43. package/dist/test/tiles/list/tile-list.test.js.map +1 -1
  44. package/index.ts +1 -0
  45. package/package.json +1 -1
  46. package/src/app-root.ts +115 -0
  47. package/src/collection-browser.ts +16 -30
  48. package/src/data-source/collection-browser-data-source.ts +1465 -1465
  49. package/src/manage/manage-bar.ts +33 -4
  50. package/src/styles/tile-action-styles.ts +52 -0
  51. package/src/tiles/base-tile-component.ts +57 -1
  52. package/src/tiles/grid/item-tile.ts +1 -0
  53. package/src/tiles/list/tile-list-compact-header.ts +106 -86
  54. package/src/tiles/list/tile-list-compact.ts +273 -239
  55. package/src/tiles/list/tile-list.ts +718 -700
  56. package/src/tiles/models.ts +16 -0
  57. package/src/tiles/tile-dispatcher.ts +114 -4
  58. package/src/tiles/tile-display-value-provider.ts +134 -134
  59. package/test/data-source/collection-browser-data-source.test.ts +193 -193
  60. package/test/manage/manage-bar.test.ts +192 -2
  61. package/test/tiles/list/tile-list-compact-header.test.ts +43 -43
  62. package/test/tiles/list/tile-list.test.ts +576 -576
  63. package/.claude/settings.local.json +0 -11
@@ -1,4 +1,4 @@
1
- import { msg } from '@lit/localize';
1
+ import { msg, str } from '@lit/localize';
2
2
  import { LitElement, html, css, TemplateResult, CSSResultGroup } from 'lit';
3
3
  import { customElement, property } from 'lit/decorators.js';
4
4
  import { when } from 'lit/directives/when.js';
@@ -7,6 +7,7 @@ import {
7
7
  type ModalManagerInterface,
8
8
  } from '@internetarchive/modal-manager';
9
9
  import type { ManageableItem } from '../models';
10
+ import { PageElementName } from '@internetarchive/search-service';
10
11
  import iaButtonStyle from '../styles/ia-button';
11
12
  import './remove-items-modal-content';
12
13
 
@@ -25,12 +26,12 @@ export class ManageBar extends LitElement {
25
26
  /**
26
27
  * Array of items that have been selected for management
27
28
  */
28
- @property({ type: Object }) selectedItems: Array<ManageableItem> = [];
29
+ @property({ type: Array }) selectedItems: Array<ManageableItem> = [];
29
30
 
30
31
  /**
31
- * Message shows as note in the modal when removing items
32
+ * Which section of the profile page searches are for (e.g., uploads, reviews, ...)
32
33
  */
33
- @property({ type: String }) manageViewModalMsg?: string;
34
+ @property({ type: String }) profileElement?: PageElementName;
34
35
 
35
36
  /**
36
37
  * Whether to show the "Select All" button (default false)
@@ -105,6 +106,34 @@ export class ManageBar extends LitElement {
105
106
  `;
106
107
  }
107
108
 
109
+ /**
110
+ * Message to show in the manage view modal, depending on context.
111
+ */
112
+ private get manageViewModalMsg(): string {
113
+ const pluralize = this.selectedItems.length > 1;
114
+ const subject = pluralize ? 'these items' : 'this item';
115
+
116
+ let listName = '';
117
+
118
+ switch (this.profileElement) {
119
+ case 'uploads':
120
+ listName = 'uploads list';
121
+ break;
122
+ case 'web_archives':
123
+ listName = 'web archives list';
124
+ break;
125
+ case 'favorites':
126
+ listName = 'favorites list';
127
+ break;
128
+ default:
129
+ return '';
130
+ }
131
+
132
+ return msg(
133
+ str`Note: It may take a few minutes for ${subject} to stop appearing in your ${listName}.`,
134
+ );
135
+ }
136
+
108
137
  private cancelClicked(): void {
109
138
  this.dispatchEvent(new CustomEvent('cancel'));
110
139
  }
@@ -0,0 +1,52 @@
1
+ import { css } from 'lit';
2
+
3
+ /**
4
+ * Shared styles for tile action buttons rendered in grid, list-detail, and
5
+ * list-compact display modes. Layout positioning is handled by each tile
6
+ * component; these styles cover only the button appearance and the row
7
+ * container's default flex behavior.
8
+ *
9
+ * Customizable via CSS custom properties:
10
+ * - `--tileActionColor` (default: #d9534f)
11
+ * - `--tileActionBg` (default: #fff)
12
+ * - `--tileActionBorderColor` (default: #d9534f)
13
+ * - `--tileActionHoverBg` (default: rgba(217, 83, 79, 0.1))
14
+ * - `--tileActionHoverColor` (defaults to --tileActionColor)
15
+ */
16
+ export const tileActionStyles = css`
17
+ .tile-actions {
18
+ flex-shrink: 0;
19
+ display: flex;
20
+ gap: var(--tileActionGap, 0);
21
+ }
22
+
23
+ .tile-action-btn {
24
+ flex: 1;
25
+ padding: 6px 5px;
26
+ border: 2px solid var(--tileActionBorderColor, #d9534f);
27
+ border-radius: var(--tileActionBorderRadius, 0);
28
+ /* Inherit from the surrounding tile rather than the UA default for <button> */
29
+ font-family: inherit;
30
+ font-size: 1.2rem;
31
+ font-weight: bold;
32
+ cursor: pointer;
33
+ color: var(--tileActionColor, #d9534f);
34
+ background: var(--tileActionBg, #fff);
35
+ transition:
36
+ background 0.15s,
37
+ color 0.15s;
38
+ }
39
+
40
+ /*
41
+ * When buttons are flush against each other (no gap), overlap their shared
42
+ * edge by 1px so adjacent borders don't double up.
43
+ */
44
+ .tile-action-btn + .tile-action-btn {
45
+ margin-left: -1px;
46
+ }
47
+
48
+ .tile-action-btn:hover {
49
+ background: var(--tileActionHoverBg, rgba(217, 83, 79, 0.2));
50
+ color: var(--tileActionHoverColor, var(--tileActionColor, #d9534f));
51
+ }
52
+ `;
@@ -1,13 +1,17 @@
1
- import { LitElement, PropertyValues } from 'lit';
1
+ import { html, LitElement, nothing, PropertyValues, TemplateResult } from 'lit';
2
2
  import { property } from 'lit/decorators.js';
3
3
  import type { SortParam } from '@internetarchive/search-service';
4
4
  import { TileDisplayValueProvider } from './tile-display-value-provider';
5
5
  import type { TileModel } from '../models';
6
6
  import { DateFormat, formatDate } from '../utils/format-date';
7
+ import type { TileAction } from './models';
7
8
 
8
9
  export abstract class BaseTileComponent extends LitElement {
9
10
  @property({ type: Object }) model?: TileModel;
10
11
 
12
+ /** Action buttons to display on this tile (rendered by subclasses) */
13
+ @property({ type: Array }) tileActions: TileAction[] = [];
14
+
11
15
  @property({ type: Number }) currentWidth?: number;
12
16
 
13
17
  @property({ type: Number }) currentHeight?: number;
@@ -62,4 +66,56 @@ export abstract class BaseTileComponent extends LitElement {
62
66
  const { useLocalTime } = this;
63
67
  return formatDate(date, format, { useLocalTime });
64
68
  }
69
+
70
+ /**
71
+ * Renders the action buttons configured for this tile, or `nothing` if
72
+ * there are none. Optional `extraClass` is appended to the container's
73
+ * class list so subclasses can target their own layout tweaks.
74
+ */
75
+ protected renderTileActions(
76
+ extraClass: string = '',
77
+ ): TemplateResult | typeof nothing {
78
+ if (!this.tileActions.length) return nothing;
79
+
80
+ const containerClass = extraClass
81
+ ? `tile-actions ${extraClass}`
82
+ : 'tile-actions';
83
+ return html`
84
+ <div class=${containerClass}>
85
+ ${this.tileActions.map(
86
+ action => html`
87
+ <button
88
+ class="tile-action-btn"
89
+ @click=${(e: Event) => this.handleTileActionClick(e, action)}
90
+ >
91
+ ${action.label}
92
+ </button>
93
+ `,
94
+ )}
95
+ </div>
96
+ `;
97
+ }
98
+
99
+ /**
100
+ * Click handler for tile action buttons. Stops propagation so the click
101
+ * doesn't activate a wrapping tile link, and dispatches a
102
+ * `tileActionClicked` event (bubbling + composed) carrying the action ID
103
+ * and the tile model.
104
+ */
105
+ protected handleTileActionClick(e: Event, action: TileAction): void {
106
+ e.preventDefault();
107
+ e.stopPropagation();
108
+ // Pre-set the hover pane controller's clicking flag so that focus
109
+ // restoration after a consumer-opened modal won't trigger the hover pane.
110
+ this.dispatchEvent(
111
+ new PointerEvent('pointerdown', { bubbles: true, composed: true }),
112
+ );
113
+ this.dispatchEvent(
114
+ new CustomEvent('tileActionClicked', {
115
+ detail: { actionId: action.id, model: this.model },
116
+ bubbles: true,
117
+ composed: true,
118
+ }),
119
+ );
120
+ }
65
121
  }
@@ -24,6 +24,7 @@ export class ItemTile extends BaseTileComponent {
24
24
  /*
25
25
  * Reactive properties inherited from BaseTileComponent:
26
26
  * - model?: TileModel;
27
+ * - tileActions: TileAction[] = [];
27
28
  * - currentWidth?: number;
28
29
  * - currentHeight?: number;
29
30
  * - baseNavigationUrl?: string;
@@ -1,86 +1,106 @@
1
- import { css, html } from 'lit';
2
- import { customElement } from 'lit/decorators.js';
3
- import { msg } from '@lit/localize';
4
- import { BaseTileComponent } from '../base-tile-component';
5
-
6
- @customElement('tile-list-compact-header')
7
- export class TileListCompactHeader extends BaseTileComponent {
8
- /*
9
- * Reactive properties inherited from BaseTileComponent:
10
- * - model?: TileModel;
11
- * - currentWidth?: number;
12
- * - currentHeight?: number;
13
- * - baseNavigationUrl?: string;
14
- * - baseImageUrl?: string;
15
- * - collectionPagePath?: string;
16
- * - sortParam: SortParam | null = null;
17
- * - creatorFilter?: string;
18
- * - mobileBreakpoint?: number;
19
- * - loggedIn = false;
20
- * - suppressBlurring = false;
21
- */
22
-
23
- render() {
24
- return html`
25
- <div id="list-line-header" class="${this.classSize}">
26
- <div id="thumb"></div>
27
- <div id="title">${msg('Title')}</div>
28
- <div id="creator">${msg('Creator')}</div>
29
- <div id="date">
30
- ${this.displayValueProvider.dateLabel || msg('Published')}
31
- </div>
32
- <div id="icon">${msg('Type')}</div>
33
- <div id="views">${this.displayValueProvider.viewsLabel}</div>
34
- </div>
35
- `;
36
- }
37
-
38
- private get classSize(): string {
39
- if (
40
- this.mobileBreakpoint &&
41
- this.currentWidth &&
42
- this.currentWidth < this.mobileBreakpoint
43
- ) {
44
- return 'mobile';
45
- }
46
- return 'desktop';
47
- }
48
-
49
- static get styles() {
50
- return css`
51
- html {
52
- font-size: unset;
53
- }
54
-
55
- div {
56
- font-size: 14px;
57
- font-weight: bold;
58
- line-height: 20px;
59
- }
60
-
61
- .mobile #views {
62
- display: none;
63
- }
64
-
65
- #views {
66
- text-align: right;
67
- padding-right: 8px;
68
- }
69
-
70
- #list-line-header {
71
- display: grid;
72
- column-gap: 10px;
73
- align-items: flex-end;
74
- padding-bottom: 2px;
75
- }
76
-
77
- #list-line-header.mobile {
78
- grid-template-columns: 36px 3fr 2fr 68px 35px;
79
- }
80
-
81
- #list-line-header.desktop {
82
- grid-template-columns: 51px 3fr 2fr 95px 30px 115px;
83
- }
84
- `;
85
- }
86
- }
1
+ import { css, html, nothing } from 'lit';
2
+ import { customElement } from 'lit/decorators.js';
3
+ import { msg } from '@lit/localize';
4
+ import { BaseTileComponent } from '../base-tile-component';
5
+
6
+ @customElement('tile-list-compact-header')
7
+ export class TileListCompactHeader extends BaseTileComponent {
8
+ /*
9
+ * Reactive properties inherited from BaseTileComponent:
10
+ * - model?: TileModel;
11
+ * - tileActions: TileAction[] = [];
12
+ * - currentWidth?: number;
13
+ * - currentHeight?: number;
14
+ * - baseNavigationUrl?: string;
15
+ * - baseImageUrl?: string;
16
+ * - collectionPagePath?: string;
17
+ * - sortParam: SortParam | null = null;
18
+ * - creatorFilter?: string;
19
+ * - mobileBreakpoint?: number;
20
+ * - loggedIn = false;
21
+ * - suppressBlurring = false;
22
+ */
23
+
24
+ render() {
25
+ const hasActions = this.tileActions.length > 0;
26
+ const headerClasses = `${this.classSize}${hasActions ? ' has-actions' : ''}`;
27
+ return html`
28
+ <div id="list-line-header" class="${headerClasses}">
29
+ <div id="thumb"></div>
30
+ ${hasActions ? html`<div id="actions-header"></div>` : nothing}
31
+ <div id="title">${msg('Title')}</div>
32
+ <div id="creator">${msg('Creator')}</div>
33
+ <div id="date">
34
+ ${this.displayValueProvider.dateLabel || msg('Published')}
35
+ </div>
36
+ <div id="icon">${msg('Type')}</div>
37
+ <div id="views">${this.displayValueProvider.viewsLabel}</div>
38
+ </div>
39
+ `;
40
+ }
41
+
42
+ private get classSize(): string {
43
+ if (
44
+ this.mobileBreakpoint &&
45
+ this.currentWidth &&
46
+ this.currentWidth < this.mobileBreakpoint
47
+ ) {
48
+ return 'mobile';
49
+ }
50
+ return 'desktop';
51
+ }
52
+
53
+ static get styles() {
54
+ return css`
55
+ html {
56
+ font-size: unset;
57
+ }
58
+
59
+ div {
60
+ font-size: 14px;
61
+ font-weight: bold;
62
+ line-height: 20px;
63
+ }
64
+
65
+ .mobile #views {
66
+ display: none;
67
+ }
68
+
69
+ #views {
70
+ text-align: right;
71
+ padding-right: 8px;
72
+ }
73
+
74
+ #list-line-header {
75
+ display: grid;
76
+ column-gap: 10px;
77
+ align-items: flex-end;
78
+ padding-bottom: 2px;
79
+ }
80
+
81
+ #list-line-header.mobile {
82
+ grid-template-columns: 36px 3fr 2fr 68px 35px;
83
+ }
84
+
85
+ #list-line-header.desktop {
86
+ grid-template-columns: 51px 3fr 2fr 95px 30px 115px;
87
+ }
88
+
89
+ /*
90
+ * When tile actions are present in the rows below, reserve a matching
91
+ * column here so the columns stay aligned with each row.
92
+ */
93
+ #list-line-header.mobile.has-actions {
94
+ grid-template-columns:
95
+ 36px var(--tileActionColumnWidth, 90px) 3fr 2fr
96
+ 68px 35px;
97
+ }
98
+
99
+ #list-line-header.desktop.has-actions {
100
+ grid-template-columns:
101
+ 51px var(--tileActionColumnWidth, 100px) 3fr 2fr
102
+ 95px 30px 115px;
103
+ }
104
+ `;
105
+ }
106
+ }