@expofp/ui 3.45.9 → 3.45.10

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.
@@ -0,0 +1,34 @@
1
+ import './LogoStrip.scss';
2
+ import React from 'react';
3
+ export interface LogoStripItem {
4
+ key: string | number;
5
+ src: string;
6
+ alt: string;
7
+ href: string;
8
+ /** Tile width in pixels. */
9
+ width: number;
10
+ /** Tile height in pixels. */
11
+ height: number;
12
+ }
13
+ export interface LogoStripProps {
14
+ items: readonly LogoStripItem[];
15
+ /**
16
+ * Horizontal space around each tile in pixels; half goes on each side. The
17
+ * row is centered, so this is the whole gap between tiles — see `logoRowGap`.
18
+ */
19
+ spacing: number;
20
+ /** The strip does not call `preventDefault()`; a handler that opens the item in-app must call it to keep the `href` from navigating. */
21
+ onItemClick?: (key: LogoStripItem['key'], event: React.MouseEvent<HTMLAnchorElement>) => void;
22
+ onPointerEnter?: React.PointerEventHandler<HTMLElement>;
23
+ onPointerLeave?: React.PointerEventHandler<HTMLElement>;
24
+ className?: string;
25
+ style?: React.CSSProperties;
26
+ ref?: React.Ref<HTMLElement>;
27
+ }
28
+ /**
29
+ * A row of logo links. Sizes come from the caller (see `logoTileSize`); the
30
+ * strip only lays them out and fades each new tile in.
31
+ */
32
+ declare const LogoStrip: ({ items, spacing, onItemClick, onPointerEnter, onPointerLeave, className, style, ref, }: LogoStripProps) => React.JSX.Element;
33
+ export default LogoStrip;
34
+ //# sourceMappingURL=LogoStrip.d.ts.map
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import './LogoStrip.scss';
3
+ import classNames from 'classnames';
4
+ /**
5
+ * A row of logo links. Sizes come from the caller (see `logoTileSize`); the
6
+ * strip only lays them out and fades each new tile in.
7
+ */
8
+ const LogoStrip = ({ items, spacing, onItemClick, onPointerEnter, onPointerLeave, className, style, ref, }) => (_jsx("section", { ref: ref, className: classNames('efp-logo-strip', className), style: style, onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, children: items.map((item) => (_jsx("a", { href: item.href, className: "efp-logo-strip__item", style: { margin: `0 ${spacing / 2}px` }, onClick: (event) => onItemClick?.(item.key, event), children: _jsx("img", { src: item.src, alt: item.alt, crossOrigin: "anonymous", style: { width: `${item.width}px`, height: `${item.height}px` } }) }, item.key))) }));
9
+ export default LogoStrip;
@@ -0,0 +1,5 @@
1
+ export { default } from './LogoStrip.js';
2
+ export type { LogoStripItem, LogoStripProps } from './LogoStrip.js';
3
+ export { fitLogoRow, logoRowGap, logoTileSize } from './logo-layout.js';
4
+ export type { LogoTileOptions, LogoTileSize } from './logo-layout.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,3 @@
1
+ /* eslint-disable simple-import-sort/exports */
2
+ export { default } from './LogoStrip.js';
3
+ export { fitLogoRow, logoRowGap, logoTileSize } from './logo-layout.js';
@@ -0,0 +1,53 @@
1
+ /** How a logo tile is sized from its aspect ratio. All lengths are in pixels. */
2
+ export interface LogoTileOptions {
3
+ /** Side of a square logo. */
4
+ base: number;
5
+ /**
6
+ * 0 gives every logo the same width, 0.5 the same area, 1 the same height
7
+ * (the legacy behavior), since `height = base · ar^(exponent − 1)`.
8
+ */
9
+ exponent: number;
10
+ /** Floor for the height, so wide logos stay readable. */
11
+ minHeight: number;
12
+ /** Hard ceiling for the height. Wins over every other bound. */
13
+ maxHeight: number;
14
+ /** Ceiling for the width. Wins over `minHeight`. */
15
+ maxWidth: number;
16
+ }
17
+ export interface LogoTileSize {
18
+ width: number;
19
+ height: number;
20
+ }
21
+ /**
22
+ * Sizes a logo so logos of different proportions carry a similar visual weight.
23
+ *
24
+ * Width is `base · aspectRatio^exponent`; the bounds then apply in order
25
+ * `minHeight` → `maxWidth` → `maxHeight`, each keeping the aspect ratio, so a
26
+ * later bound wins over an earlier one.
27
+ * @param aspectRatio - The logo's width divided by its height.
28
+ * @param options - Base size, exponent and bounds.
29
+ * @returns The tile size in pixels.
30
+ */
31
+ export declare function logoTileSize(aspectRatio: number, options: LogoTileOptions): LogoTileSize;
32
+ /**
33
+ * Counts how many leading tiles fit a row. Each tile takes its width plus
34
+ * `spacing` (half on each side), so `n` tiles need `Σwidth + n·spacing`.
35
+ * @param widths - Tile widths in row order.
36
+ * @param maxWidth - The width the row may fill.
37
+ * @param spacing - Space around each tile.
38
+ * @returns The number of leading tiles that fit; 0 when even the first does not.
39
+ */
40
+ export declare function fitLogoRow(widths: readonly number[], maxWidth: number, spacing: number): number;
41
+ /**
42
+ * The even space around each tile of a row: the leftover width shared across
43
+ * the tiles, clamped to `[spacing, maxGap]`. A centered row then puts whatever
44
+ * the cap leaves over equally at both ends, so a few logos on a wide bar do not
45
+ * drift apart.
46
+ * @param widths - Tile widths in row order.
47
+ * @param maxWidth - The width the row may fill.
48
+ * @param spacing - The least space around each tile.
49
+ * @param maxGap - The most space around each tile.
50
+ * @returns Space around each tile (half on each side); `spacing` for an empty or overfull row.
51
+ */
52
+ export declare function logoRowGap(widths: readonly number[], maxWidth: number, spacing: number, maxGap: number): number;
53
+ //# sourceMappingURL=logo-layout.d.ts.map
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Sizes a logo so logos of different proportions carry a similar visual weight.
3
+ *
4
+ * Width is `base · aspectRatio^exponent`; the bounds then apply in order
5
+ * `minHeight` → `maxWidth` → `maxHeight`, each keeping the aspect ratio, so a
6
+ * later bound wins over an earlier one.
7
+ * @param aspectRatio - The logo's width divided by its height.
8
+ * @param options - Base size, exponent and bounds.
9
+ * @returns The tile size in pixels.
10
+ */
11
+ export function logoTileSize(aspectRatio, options) {
12
+ const ar = Number.isFinite(aspectRatio) && aspectRatio > 0 ? aspectRatio : 1;
13
+ let width = options.base * Math.pow(ar, options.exponent);
14
+ let height = width / ar;
15
+ if (height < options.minHeight) {
16
+ height = options.minHeight;
17
+ width = height * ar;
18
+ }
19
+ if (width > options.maxWidth) {
20
+ width = options.maxWidth;
21
+ height = width / ar;
22
+ }
23
+ if (height > options.maxHeight) {
24
+ height = options.maxHeight;
25
+ width = height * ar;
26
+ }
27
+ return { width, height };
28
+ }
29
+ /**
30
+ * Counts how many leading tiles fit a row. Each tile takes its width plus
31
+ * `spacing` (half on each side), so `n` tiles need `Σwidth + n·spacing`.
32
+ * @param widths - Tile widths in row order.
33
+ * @param maxWidth - The width the row may fill.
34
+ * @param spacing - Space around each tile.
35
+ * @returns The number of leading tiles that fit; 0 when even the first does not.
36
+ */
37
+ export function fitLogoRow(widths, maxWidth, spacing) {
38
+ let used = 0;
39
+ for (let i = 0; i < widths.length; i++) {
40
+ used += widths[i] + spacing;
41
+ if (used > maxWidth)
42
+ return i;
43
+ }
44
+ return widths.length;
45
+ }
46
+ /**
47
+ * The even space around each tile of a row: the leftover width shared across
48
+ * the tiles, clamped to `[spacing, maxGap]`. A centered row then puts whatever
49
+ * the cap leaves over equally at both ends, so a few logos on a wide bar do not
50
+ * drift apart.
51
+ * @param widths - Tile widths in row order.
52
+ * @param maxWidth - The width the row may fill.
53
+ * @param spacing - The least space around each tile.
54
+ * @param maxGap - The most space around each tile.
55
+ * @returns Space around each tile (half on each side); `spacing` for an empty or overfull row.
56
+ */
57
+ export function logoRowGap(widths, maxWidth, spacing, maxGap) {
58
+ const n = widths.length;
59
+ if (!n)
60
+ return spacing;
61
+ let used = 0;
62
+ for (const width of widths)
63
+ used += width;
64
+ return Math.max(spacing, Math.min(maxGap, (maxWidth - used) / n));
65
+ }
package/dist/index.d.ts CHANGED
@@ -27,6 +27,8 @@ export * from './components/LevelBadge';
27
27
  export { default as LevelBadge } from './components/LevelBadge';
28
28
  export * from './components/LevelSelector';
29
29
  export { default as LevelSelector } from './components/LevelSelector';
30
+ export * from './components/LogoStrip/index.js';
31
+ export { default as LogoStrip } from './components/LogoStrip/index.js';
30
32
  export * from './components/MapControls';
31
33
  export { default as MapControls } from './components/MapControls';
32
34
  export * from './components/MobileToggleButton';
package/dist/index.js CHANGED
@@ -32,6 +32,8 @@ export * from './components/LevelBadge';
32
32
  export { default as LevelBadge } from './components/LevelBadge';
33
33
  export * from './components/LevelSelector';
34
34
  export { default as LevelSelector } from './components/LevelSelector';
35
+ export * from './components/LogoStrip/index.js';
36
+ export { default as LogoStrip } from './components/LogoStrip/index.js';
35
37
  export * from './components/MapControls';
36
38
  export { default as MapControls } from './components/MapControls';
37
39
  export * from './components/MobileToggleButton';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expofp/ui",
3
- "version": "3.45.9",
3
+ "version": "3.45.10",
4
4
  "type": "module",
5
5
  "description": "ExpoFP SDK internal: shared pure React UI components",
6
6
  "homepage": "https://developer.expofp.com/",