@scalewing/react 1.59.5 → 1.60.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @scalewing/react
2
2
 
3
+ ## 1.60.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 69e52f9: `PreviewImage`: a real screenshot inside a `ScreenPreview`, at the screen's width with its height following. Decorative, as the preview's `label` names the image (Teisoro F-017 task 2730).
8
+
3
9
  ## 1.59.5
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -39,6 +39,7 @@ import {
39
39
  Hero,
40
40
  ScreenPreview,
41
41
  PreviewGroup,
42
+ PreviewImage,
42
43
  PreviewRow,
43
44
  PreviewTiles,
44
45
  SiteFooter,
@@ -1176,6 +1177,8 @@ const { headings, omissions } = inspectMarkdown(page.body, {
1176
1177
 
1177
1178
  `ScreenPreview` is a drawn stand-in for an app's screen on a public page, until a real screenshot replaces it: a `figure` whose screen is one image (`role="img"`) named by `label`, its alternative text, so a screen reader hears that sentence instead of the rows; an optional `caption` shows under it as the `figcaption`. An optional `eyebrow` and `title` head the screen. Its content is `PreviewGroup`s of `PreviewRow`s (`label` at the start, `value` at the end in tabular numerals; `kind` `plain`, `total` in bold past a hairline, `note` a small label over its words, `filled` on the quiet fill, or `alert` on the danger tint) and `PreviewTiles` (`items` of `{ id, label, value }`, three to a row on a phone, six from `md`). `frame="card"` (default) is a surface card whose groups are parted by hairlines; `frame="device"` draws a 20rem phone from `md` (a bezel in the text colour round a quiet screen with a surface header and each group a card) and the card below `md`. A blank `label`, an unknown `frame` or `kind`, no tiles, or a blank or repeated tile id throws a `RangeError`.
1178
1179
 
1180
+ When the real screenshot exists, a `PreviewImage` (`src`, `width`, `height`) holds it in the same frame, filling the screen's body with its height following. It is decorative: `label` stays the alternative text.
1181
+
1179
1182
  ```tsx
1180
1183
  <ScreenPreview
1181
1184
  frame="device"
@@ -12,14 +12,15 @@ export type ScreenPreviewProps = Omit<HTMLAttributes<HTMLElement>, 'children' |
12
12
  title?: ReactNode;
13
13
  /** A visible caption under the screen, such as what will replace it. */
14
14
  caption?: ReactNode;
15
- /** `PreviewGroup`s, `PreviewRow`s and `PreviewTiles`. */
15
+ /** `PreviewGroup`s, `PreviewRow`s, `PreviewTiles` or a `PreviewImage`. */
16
16
  children: ReactNode;
17
17
  };
18
18
  /**
19
19
  * A drawn stand-in for an app's screen on a public page: a `figure` whose
20
20
  * screen is one image (`role="img"`) named by `label`, so its rows are
21
21
  * read as that one sentence, with an optional visible `caption`. Its
22
- * content is `PreviewGroup`s of `PreviewRow`s and `PreviewTiles`. A blank
22
+ * content is `PreviewGroup`s of `PreviewRow`s, `PreviewTiles` or a
23
+ * `PreviewImage` holding a real screenshot. A blank
23
24
  * label or an unknown frame throws a `RangeError`.
24
25
  */
25
26
  export declare function ScreenPreview({ caption, children, className, eyebrow, frame, label, title, ...rest }: ScreenPreviewProps): import("react").JSX.Element;
@@ -7,7 +7,8 @@ export { screenPreviewFrames };
7
7
  * A drawn stand-in for an app's screen on a public page: a `figure` whose
8
8
  * screen is one image (`role="img"`) named by `label`, so its rows are
9
9
  * read as that one sentence, with an optional visible `caption`. Its
10
- * content is `PreviewGroup`s of `PreviewRow`s and `PreviewTiles`. A blank
10
+ * content is `PreviewGroup`s of `PreviewRow`s, `PreviewTiles` or a
11
+ * `PreviewImage` holding a real screenshot. A blank
11
12
  * label or an unknown frame throws a `RangeError`.
12
13
  */
13
14
  export function ScreenPreview({ caption, children, className, eyebrow, frame = 'card', label, title, ...rest }) {
@@ -0,0 +1,13 @@
1
+ /** The file and its natural size in pixels, so the page keeps its place while it loads. */
2
+ export type PreviewImageProps = {
3
+ src: string;
4
+ width: number;
5
+ height: number;
6
+ };
7
+ /**
8
+ * A real screenshot inside a `ScreenPreview`, drawn at the width of the
9
+ * screen with its height following. It is decorative (`alt=""`): the
10
+ * preview's `label` already says what it shows. A blank `src` or a size
11
+ * that is not a positive whole number throws a `RangeError`.
12
+ */
13
+ export declare function PreviewImage({ height, src, width }: PreviewImageProps): import("react").JSX.Element;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * A real screenshot inside a `ScreenPreview`, drawn at the width of the
4
+ * screen with its height following. It is decorative (`alt=""`): the
5
+ * preview's `label` already says what it shows. A blank `src` or a size
6
+ * that is not a positive whole number throws a `RangeError`.
7
+ */
8
+ export function PreviewImage({ height, src, width }) {
9
+ if (!src.trim())
10
+ throw new RangeError('A PreviewImage needs a src');
11
+ for (const [name, side] of [
12
+ ['width', width],
13
+ ['height', height],
14
+ ])
15
+ if (!Number.isInteger(side) || side <= 0)
16
+ throw new RangeError(`A PreviewImage's ${name} must be a positive whole number, not ${side}`);
17
+ return (_jsx("img", { alt: "", className: "sw-preview-image", height: height, loading: "lazy", src: src, width: width }));
18
+ }
@@ -2,9 +2,11 @@ import { mutedReadsAsText, quietFill } from './quiet-fill.js';
2
2
  /*
3
3
  * The action sits after the row in its own column, centred on the row's
4
4
  * height, outside the row's press area, keeping the row's end padding
5
- * after it. Where :has() is supported the hover fill spans the whole item,
6
- * so the row, its select and its action read as one line; a detail under
7
- * the row is on that fill too, so its muted words take the text colour.
5
+ * after it. It ends at the column's end, so a short action on one row and
6
+ * a long one on the next finish on the same line. Where :has() is supported
7
+ * the hover fill spans the whole item, so the row, its select and its action
8
+ * read as one line; a detail under the row is on that fill too, so its muted
9
+ * words take the text colour.
8
10
  */
9
11
  function actionRules() {
10
12
  return `.sw-fixture-action {
@@ -13,6 +15,7 @@ function actionRules() {
13
15
  display: inline-flex;
14
16
  grid-column: -2 / -1;
15
17
  grid-row: 1;
18
+ justify-content: flex-end;
16
19
  padding-inline-end: var(--sw-space-2);
17
20
  }
18
21
 
@@ -87,6 +87,16 @@ function cardRules() {
87
87
  line-height: ${caption.lineHeight}px;
88
88
  }`;
89
89
  }
90
+ /* A screenshot fills the screen's width; its height follows. */
91
+ function imageRules() {
92
+ return `.sw-preview-image {
93
+ border-radius: var(--sw-radius-sm);
94
+ display: block;
95
+ height: auto;
96
+ max-width: 100%;
97
+ width: 100%;
98
+ }`;
99
+ }
90
100
  function rowRules() {
91
101
  return `.sw-preview-row {
92
102
  display: flex;
@@ -206,7 +216,7 @@ function wideRules() {
206
216
  }`;
207
217
  }
208
218
  export function cssScreenPreviewClasses() {
209
- return [cardRules(), rowRules(), wideRules()].join('\n\n');
219
+ return [cardRules(), imageRules(), rowRules(), wideRules()].join('\n\n');
210
220
  }
211
221
  export function screenPreviewClassCatalog() {
212
222
  return [
@@ -224,6 +234,7 @@ export function screenPreviewClassCatalog() {
224
234
  ...previewRowKinds.map(previewRowKindClass),
225
235
  'sw-preview-row-label',
226
236
  'sw-preview-row-value',
237
+ 'sw-preview-image',
227
238
  'sw-preview-tiles',
228
239
  'sw-preview-tile',
229
240
  'sw-preview-tile-label',
package/dist/index.d.ts CHANGED
@@ -90,6 +90,8 @@ export type { SiteHeaderItem, SiteHeaderLabels, SiteHeaderProps, SiteHeaderSkipL
90
90
  export { ScreenPreview, screenPreviewFrames, } from './components/ScreenPreview.js';
91
91
  export type { ScreenPreviewFrame, ScreenPreviewProps, } from './components/ScreenPreview.js';
92
92
  export { PreviewGroup } from './components/screen-preview/PreviewGroup.js';
93
+ export { PreviewImage } from './components/screen-preview/PreviewImage.js';
94
+ export type { PreviewImageProps } from './components/screen-preview/PreviewImage.js';
93
95
  export { PreviewRow, previewRowKinds, } from './components/screen-preview/PreviewRow.js';
94
96
  export type { PreviewRowKind, PreviewRowProps, } from './components/screen-preview/PreviewRow.js';
95
97
  export { PreviewTiles } from './components/screen-preview/PreviewTiles.js';
package/dist/index.js CHANGED
@@ -81,6 +81,7 @@ export { Band, bandElements, bandTones, bandWidths, } from './components/Band.js
81
81
  export { SiteHeader } from './components/SiteHeader.js';
82
82
  export { ScreenPreview, screenPreviewFrames, } from './components/ScreenPreview.js';
83
83
  export { PreviewGroup } from './components/screen-preview/PreviewGroup.js';
84
+ export { PreviewImage } from './components/screen-preview/PreviewImage.js';
84
85
  export { PreviewRow, previewRowKinds, } from './components/screen-preview/PreviewRow.js';
85
86
  export { PreviewTiles } from './components/screen-preview/PreviewTiles.js';
86
87
  export { Hero } from './components/Hero.js';
package/dist/styles.css CHANGED
@@ -3618,6 +3618,7 @@ button.sw-date-toggle-day[aria-disabled='true'] {
3618
3618
  display: inline-flex;
3619
3619
  grid-column: -2 / -1;
3620
3620
  grid-row: 1;
3621
+ justify-content: flex-end;
3621
3622
  padding-inline-end: var(--sw-space-2);
3622
3623
  }
3623
3624
 
@@ -5299,6 +5300,14 @@ button.sw-date-toggle-day[aria-disabled='true'] {
5299
5300
  line-height: 18px;
5300
5301
  }
5301
5302
 
5303
+ .sw-preview-image {
5304
+ border-radius: var(--sw-radius-sm);
5305
+ display: block;
5306
+ height: auto;
5307
+ max-width: 100%;
5308
+ width: 100%;
5309
+ }
5310
+
5302
5311
  .sw-preview-row {
5303
5312
  display: flex;
5304
5313
  gap: var(--sw-space-3);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalewing/react",
3
- "version": "1.59.5",
3
+ "version": "1.60.0",
4
4
  "description": "Scalewing DOM components and package-owned CSS utilities.",
5
5
  "type": "module",
6
6
  "license": "MIT",