@grantcodes/ui 2.7.0 → 2.8.1
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.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.8.1](https://github.com/grantcodes/ui/compare/ui-v2.8.0...ui-v2.8.1) (2026-04-07)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **gallery:** filmstrip images should fill height with auto width, not square crop ([62d436f](https://github.com/grantcodes/ui/commit/62d436fe75f42c6d99c380c9d4384e53a97abef1))
|
|
9
|
+
* **gallery:** SSR-safe filmstrip variant with variant prop refactor ([2af368c](https://github.com/grantcodes/ui/commit/2af368cba60b55021f563a06c1416219abb0b024))
|
|
10
|
+
* **gallery:** use CSS custom properties for filmstrip styles instead of JS attribute propagation ([f83efcf](https://github.com/grantcodes/ui/commit/f83efcfcf24a896dcdcb943b5988a9daefcf8947))
|
|
11
|
+
|
|
12
|
+
## [2.8.0](https://github.com/grantcodes/ui/compare/ui-v2.7.0...ui-v2.8.0) (2026-04-07)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* **09-gallery-film-strip:** add filmstrip and marquee gallery variants ([0a4ff3e](https://github.com/grantcodes/ui/commit/0a4ff3e620bc21a6d291943acc72608d1c08d371))
|
|
18
|
+
* **ui:** gallery filmstrip variant ([d22a93d](https://github.com/grantcodes/ui/commit/d22a93d615874ca76fcf7525ea8d1dfa71721e2b))
|
|
19
|
+
|
|
3
20
|
## [2.7.0](https://github.com/grantcodes/ui/compare/ui-v2.6.0...ui-v2.7.0) (2026-04-06)
|
|
4
21
|
|
|
5
22
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grantcodes/ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.1",
|
|
4
4
|
"description": "A personal component system built with Lit web components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main.js",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"@lit/react": "^1.0.8",
|
|
48
48
|
"lit": "^3.3.1",
|
|
49
49
|
"shiki": "^3.17.1",
|
|
50
|
-
"@grantcodes/style-dictionary": "^1.4.
|
|
50
|
+
"@grantcodes/style-dictionary": "^1.4.2"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@arcmantle/vite-plugin-import-css-sheet": "^1.0.12",
|
|
@@ -3,17 +3,19 @@ import { html } from "lit/static-html.js";
|
|
|
3
3
|
import galleryStyles from "./gallery.css" with { type: "css" };
|
|
4
4
|
|
|
5
5
|
export class GrantCodesGallery extends LitElement {
|
|
6
|
-
// Styles are scoped to this element: they won't conflict with styles
|
|
7
|
-
// on the main page or in other components. Styling API can be exposed
|
|
8
|
-
// via CSS custom properties.
|
|
9
6
|
static styles = [galleryStyles];
|
|
10
7
|
|
|
8
|
+
static properties = {
|
|
9
|
+
variant: { type: String, reflect: true },
|
|
10
|
+
};
|
|
11
|
+
|
|
11
12
|
/** @type {any[]} */
|
|
12
13
|
images = [];
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
constructor() {
|
|
16
|
+
super();
|
|
17
|
+
this.variant = "default";
|
|
18
|
+
}
|
|
17
19
|
|
|
18
20
|
render() {
|
|
19
21
|
return html`
|
|
@@ -38,8 +38,6 @@
|
|
|
38
38
|
display: block;
|
|
39
39
|
width: 100%;
|
|
40
40
|
height: auto;
|
|
41
|
-
/* Make the image square */
|
|
42
|
-
aspect-ratio: 1;
|
|
43
41
|
object-fit: cover;
|
|
44
42
|
object-position: center;
|
|
45
43
|
}
|
|
@@ -55,3 +53,89 @@
|
|
|
55
53
|
color: white;
|
|
56
54
|
}
|
|
57
55
|
|
|
56
|
+
/* =============================================
|
|
57
|
+
GALLERY-IMAGE DEFAULT STYLES
|
|
58
|
+
(applied inside grantcodes-gallery-image shadow DOM via :host)
|
|
59
|
+
============================================= */
|
|
60
|
+
|
|
61
|
+
/* Default square crop. :host selector comes after .gallery__image img so
|
|
62
|
+
wins on equal specificity. Uses custom properties so filmstrip mode can
|
|
63
|
+
override them via inheritance from the parent gallery element. */
|
|
64
|
+
:host .gallery__image img {
|
|
65
|
+
aspect-ratio: var(--_gallery-img-aspect-ratio, 1);
|
|
66
|
+
block-size: var(--_gallery-img-block-size, auto);
|
|
67
|
+
width: var(--_gallery-img-width, 100%);
|
|
68
|
+
height: var(--_gallery-img-height, auto);
|
|
69
|
+
max-inline-size: var(--_gallery-img-max-inline-size, unset);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
:host .gallery__image {
|
|
73
|
+
block-size: var(--_gallery-image-block-size, auto);
|
|
74
|
+
width: var(--_gallery-image-width, 100%);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* =============================================
|
|
78
|
+
FILMSTRIP VARIANT
|
|
79
|
+
============================================= */
|
|
80
|
+
|
|
81
|
+
/* Set inherited CSS custom properties on the gallery host when in filmstrip
|
|
82
|
+
mode. These cascade down to slotted grantcodes-gallery-image children
|
|
83
|
+
without requiring JS attribute propagation, making the filmstrip work
|
|
84
|
+
correctly with SSR-rendered content. */
|
|
85
|
+
:host([variant="filmstrip"]) {
|
|
86
|
+
--_gallery-image-block-size: 100%;
|
|
87
|
+
--_gallery-image-width: fit-content;
|
|
88
|
+
--_gallery-img-aspect-ratio: auto;
|
|
89
|
+
--_gallery-img-block-size: 100%;
|
|
90
|
+
--_gallery-img-width: auto;
|
|
91
|
+
--_gallery-img-height: 100%;
|
|
92
|
+
--_gallery-img-max-inline-size: none;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
:host([variant="filmstrip"]) .gallery {
|
|
96
|
+
overflow: hidden;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
:host([variant="filmstrip"]) .gallery__slot {
|
|
100
|
+
display: flex;
|
|
101
|
+
flex-wrap: nowrap;
|
|
102
|
+
overflow-x: auto;
|
|
103
|
+
overflow-y: hidden;
|
|
104
|
+
block-size: var(--filmstrip-height, 240px);
|
|
105
|
+
gap: var(--gallery-gap, 4px);
|
|
106
|
+
scroll-snap-type: x mandatory;
|
|
107
|
+
overscroll-behavior-x: contain;
|
|
108
|
+
scrollbar-width: none;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
:host([variant="filmstrip"]) .gallery__slot::-webkit-scrollbar {
|
|
112
|
+
display: none;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
:host([variant="filmstrip"]) .gallery__slot::slotted(*) {
|
|
116
|
+
flex: 0 0 auto;
|
|
117
|
+
block-size: 100%;
|
|
118
|
+
scroll-snap-align: start;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* Scroll-driven reveal animation */
|
|
122
|
+
@supports (animation-timeline: view()) {
|
|
123
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
124
|
+
:host([variant="filmstrip"]) .gallery__slot::slotted(*) {
|
|
125
|
+
animation: filmstrip-reveal linear both;
|
|
126
|
+
animation-timeline: view(inline);
|
|
127
|
+
animation-range: entry 0% entry 60%;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@keyframes filmstrip-reveal {
|
|
133
|
+
from {
|
|
134
|
+
opacity: 0.3;
|
|
135
|
+
scale: 0.92;
|
|
136
|
+
}
|
|
137
|
+
to {
|
|
138
|
+
opacity: 1;
|
|
139
|
+
scale: 1;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -58,3 +58,46 @@ const meta = {
|
|
|
58
58
|
export default meta;
|
|
59
59
|
|
|
60
60
|
export const Gallery = {};
|
|
61
|
+
|
|
62
|
+
const filmstripImages = [
|
|
63
|
+
{ w: 400, h: 240 },
|
|
64
|
+
{ w: 160, h: 240 },
|
|
65
|
+
{ w: 360, h: 240 },
|
|
66
|
+
{ w: 180, h: 240 },
|
|
67
|
+
{ w: 160, h: 900 }, // tall portrait image
|
|
68
|
+
{ w: 420, h: 240 },
|
|
69
|
+
{ w: 160, h: 240 },
|
|
70
|
+
{ w: 380, h: 240 },
|
|
71
|
+
{ w: 200, h: 240 },
|
|
72
|
+
{ w: 440, h: 240 },
|
|
73
|
+
{ w: 160, h: 240 },
|
|
74
|
+
{ w: 350, h: 240 },
|
|
75
|
+
{ w: 170, h: 240 },
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
export const Filmstrip = {
|
|
79
|
+
args: {
|
|
80
|
+
variant: "filmstrip",
|
|
81
|
+
},
|
|
82
|
+
render: (args) =>
|
|
83
|
+
template(
|
|
84
|
+
args,
|
|
85
|
+
html`${filmstripImages.map(
|
|
86
|
+
({ w, h }, i) =>
|
|
87
|
+
html`<grantcodes-gallery-image
|
|
88
|
+
src="https://picsum.photos/seed/${i + 10}/${w}/${h}"
|
|
89
|
+
alt="Photo ${i + 1}"
|
|
90
|
+
width="${w}"
|
|
91
|
+
height="${h}"
|
|
92
|
+
></grantcodes-gallery-image>`,
|
|
93
|
+
)}`,
|
|
94
|
+
),
|
|
95
|
+
parameters: {
|
|
96
|
+
docs: {
|
|
97
|
+
description: {
|
|
98
|
+
story:
|
|
99
|
+
"Filmstrip variant: images in a scrollable horizontal row at uniform height. Uses scroll-snap and scroll-driven animations.",
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
};
|
|
@@ -2,6 +2,7 @@ import { describe, it, afterEach } from "node:test";
|
|
|
2
2
|
import { strict as assert } from "node:assert";
|
|
3
3
|
import { fixture, cleanup } from "../../test-utils/index.js";
|
|
4
4
|
import "./gallery.js";
|
|
5
|
+
import "./gallery-image.js";
|
|
5
6
|
|
|
6
7
|
describe("Gallery Component", () => {
|
|
7
8
|
let element;
|
|
@@ -57,4 +58,28 @@ describe("Gallery Component", () => {
|
|
|
57
58
|
"Images array should be empty initially",
|
|
58
59
|
);
|
|
59
60
|
});
|
|
61
|
+
|
|
62
|
+
describe("Gallery filmstrip variant", () => {
|
|
63
|
+
let element;
|
|
64
|
+
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
cleanup(element);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("should have variant property default to 'default'", async () => {
|
|
70
|
+
element = await fixture("grantcodes-gallery");
|
|
71
|
+
assert.strictEqual(element.variant, "default", "variant should default to 'default'");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should reflect variant attribute when property is set", async () => {
|
|
75
|
+
element = await fixture("grantcodes-gallery");
|
|
76
|
+
element.variant = "filmstrip";
|
|
77
|
+
await element.updateComplete;
|
|
78
|
+
assert.strictEqual(
|
|
79
|
+
element.getAttribute("variant"),
|
|
80
|
+
"filmstrip",
|
|
81
|
+
"variant attribute should be reflected",
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
60
85
|
});
|