react-cheminfo 0.42.0 → 0.44.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 (75) hide show
  1. package/bin/check-deploy.mjs +3 -1
  2. package/lib/deploy/core/buildGate.d.ts.map +1 -1
  3. package/lib/deploy/core/buildGate.js +4 -34
  4. package/lib/deploy/core/buildGate.js.map +1 -1
  5. package/lib/deploy/core/checkDeploy.d.ts.map +1 -1
  6. package/lib/deploy/core/checkDeploy.js +2 -0
  7. package/lib/deploy/core/checkDeploy.js.map +1 -1
  8. package/lib/deploy/core/index.d.ts +1 -0
  9. package/lib/deploy/core/index.d.ts.map +1 -1
  10. package/lib/deploy/core/index.js +1 -0
  11. package/lib/deploy/core/index.js.map +1 -1
  12. package/lib/deploy/core/json.d.ts +27 -0
  13. package/lib/deploy/core/json.d.ts.map +1 -0
  14. package/lib/deploy/core/json.js +56 -0
  15. package/lib/deploy/core/json.js.map +1 -0
  16. package/lib/deploy/core/reactCopies.d.ts +32 -0
  17. package/lib/deploy/core/reactCopies.d.ts.map +1 -0
  18. package/lib/deploy/core/reactCopies.js +212 -0
  19. package/lib/deploy/core/reactCopies.js.map +1 -0
  20. package/lib/deploy/core/types.d.ts +1 -1
  21. package/lib/deploy/core/types.d.ts.map +1 -1
  22. package/lib/deploy/core/types.js.map +1 -1
  23. package/lib/ecosystem/core/sites.d.ts.map +1 -1
  24. package/lib/ecosystem/core/sites.js +12 -0
  25. package/lib/ecosystem/core/sites.js.map +1 -1
  26. package/lib/ecosystem/core/types.d.ts +1 -1
  27. package/lib/ecosystem/core/types.d.ts.map +1 -1
  28. package/lib/ecosystem/ui/glyphs.d.ts.map +1 -1
  29. package/lib/ecosystem/ui/glyphs.js +4 -0
  30. package/lib/ecosystem/ui/glyphs.js.map +1 -1
  31. package/lib/periodic/core/layout.d.ts +6 -0
  32. package/lib/periodic/core/layout.d.ts.map +1 -1
  33. package/lib/periodic/core/layout.js.map +1 -1
  34. package/lib/periodic/ui/ElementCell.d.ts +22 -2
  35. package/lib/periodic/ui/ElementCell.d.ts.map +1 -1
  36. package/lib/periodic/ui/ElementCell.js +20 -145
  37. package/lib/periodic/ui/ElementCell.js.map +1 -1
  38. package/lib/periodic/ui/PeriodicTable.d.ts +11 -2
  39. package/lib/periodic/ui/PeriodicTable.d.ts.map +1 -1
  40. package/lib/periodic/ui/PeriodicTable.js +21 -3
  41. package/lib/periodic/ui/PeriodicTable.js.map +1 -1
  42. package/lib/periodic/ui/PeriodicTableChrome.d.ts +2 -2
  43. package/lib/periodic/ui/PeriodicTableChrome.d.ts.map +1 -1
  44. package/lib/periodic/ui/PeriodicTableChrome.js +22 -4
  45. package/lib/periodic/ui/PeriodicTableChrome.js.map +1 -1
  46. package/lib/periodic/ui/cellStyles.d.ts +79 -0
  47. package/lib/periodic/ui/cellStyles.d.ts.map +1 -0
  48. package/lib/periodic/ui/cellStyles.js +90 -0
  49. package/lib/periodic/ui/cellStyles.js.map +1 -0
  50. package/lib/periodic/ui/cellType.d.ts +106 -0
  51. package/lib/periodic/ui/cellType.d.ts.map +1 -0
  52. package/lib/periodic/ui/cellType.js +181 -0
  53. package/lib/periodic/ui/cellType.js.map +1 -0
  54. package/lib/periodic/ui/glyphWidths.d.ts +11 -0
  55. package/lib/periodic/ui/glyphWidths.d.ts.map +1 -0
  56. package/lib/periodic/ui/glyphWidths.js +69 -0
  57. package/lib/periodic/ui/glyphWidths.js.map +1 -0
  58. package/package.json +9 -1
  59. package/src/deploy/core/buildGate.ts +4 -35
  60. package/src/deploy/core/checkDeploy.ts +7 -0
  61. package/src/deploy/core/index.ts +1 -0
  62. package/src/deploy/core/json.ts +53 -0
  63. package/src/deploy/core/reactCopies.ts +243 -0
  64. package/src/deploy/core/types.ts +5 -1
  65. package/src/ecosystem/core/sites.ts +13 -0
  66. package/src/ecosystem/core/types.ts +2 -1
  67. package/src/ecosystem/ui/glyphs.tsx +16 -0
  68. package/src/periodic/core/layout.ts +6 -0
  69. package/src/periodic/ui/ElementCell.tsx +69 -160
  70. package/src/periodic/ui/PeriodicTable.tsx +41 -4
  71. package/src/periodic/ui/PeriodicTableChrome.tsx +27 -8
  72. package/src/periodic/ui/cellStyles.ts +100 -0
  73. package/src/periodic/ui/cellType.ts +223 -0
  74. package/src/periodic/ui/glyphWidths.ts +68 -0
  75. package/styles/chrome.css +10 -2
@@ -37,6 +37,13 @@ import {
37
37
  HeaderStrips,
38
38
  InnerTransitionMarkers,
39
39
  } from './PeriodicTableChrome.tsx';
40
+ import {
41
+ ROW_SHARE,
42
+ SHELLS_RESERVED,
43
+ detailType,
44
+ shellFontSize,
45
+ textWidthInEm,
46
+ } from './cellType.ts';
40
47
  import { UNIT_PROPERTY, ofWidth, unitValue } from './unit.ts';
41
48
 
42
49
  /** What {@link PeriodicTable} needs. */
@@ -68,6 +75,13 @@ export interface PeriodicTableProps {
68
75
  * @default the English name
69
76
  */
70
77
  nameOf?: (element: PeriodicElement) => string;
78
+ /**
79
+ * Electrons in each shell of an element, innermost first, written down the
80
+ * right-hand edge of its cell. The energy levels, which a table is also read
81
+ * for: none of ours knows them, so the tool that does says so here.
82
+ * @default undefined — no cell writes any
83
+ */
84
+ shellsOf?: (element: PeriodicElement) => readonly number[] | undefined;
71
85
  /**
72
86
  * Whether an element is inside the set the tool is showing. Everything
73
87
  * outside it is dimmed rather than removed, so the table keeps its shape.
@@ -91,8 +105,10 @@ export interface PeriodicTableProps {
91
105
  */
92
106
  headers?: boolean;
93
107
  /**
94
- * Called when a whole group or period header is clicked. Without it the
95
- * strips are labels rather than buttons.
108
+ * Called when a whole group or period header is clicked. The run says
109
+ * whether the click was additive — Cmd or Ctrl held — so a tool can let a
110
+ * reader put two periods on one chart. Without it the strips are labels
111
+ * rather than buttons.
96
112
  * @default undefined
97
113
  */
98
114
  onSelectRange?: (range: ElementRange) => void;
@@ -137,6 +153,7 @@ export function PeriodicTable(props: PeriodicTableProps): ReactElement {
137
153
  swatchOf = defaultSwatchOf,
138
154
  detailOf,
139
155
  nameOf = defaultNameOf,
156
+ shellsOf,
140
157
  isIncluded,
141
158
  inset,
142
159
  headers = false,
@@ -184,6 +201,23 @@ export function PeriodicTable(props: PeriodicTableProps): ReactElement {
184
201
  onSelect(next.symbol);
185
202
  }
186
203
 
204
+ // One size for the whole table rather than one per cell: a column of values
205
+ // written at eleven sizes is read as eleven things, and a name shrunk to a
206
+ // third of its neighbour's is the one a class cannot make out. The size is
207
+ // what the widest of them fits at, and anything wider than that is condensed
208
+ // by the cell rather than written smaller still.
209
+ const placed = placedElements();
210
+ const widths: number[] = [];
211
+ let deepest = 0;
212
+ for (const { element } of placed) {
213
+ widths.push(textWidthInEm(detailOf?.(element) ?? ''));
214
+ const stack = shellsOf?.(element);
215
+ if (stack !== undefined && stack.length > deepest) deepest = stack.length;
216
+ }
217
+ const reserved = deepest === 0 ? 0 : SHELLS_RESERVED;
218
+ const type = detailType(widths, reserved);
219
+ const shellSize = shellFontSize(deepest);
220
+
187
221
  return (
188
222
  <div ref={rootRef} className={className} style={rootStyle}>
189
223
  <div
@@ -199,13 +233,16 @@ export function PeriodicTable(props: PeriodicTableProps): ReactElement {
199
233
  <div style={insetStyle(offset)}>{inset}</div>
200
234
  )}
201
235
  {markers ? <InnerTransitionMarkers offset={offset} /> : null}
202
- {placedElements().map(({ element, cell }) => (
236
+ {placed.map(({ element, cell }) => (
203
237
  <ElementCell
204
238
  key={element.symbol}
205
239
  atomicNumber={element.atomicNumber}
206
240
  symbol={element.symbol}
207
241
  name={nameOf(element)}
208
242
  detail={detailOf?.(element)}
243
+ detailType={type}
244
+ shells={shellsOf?.(element)}
245
+ shellSize={shellSize}
209
246
  swatch={swatchOf(element)}
210
247
  isSelected={element.symbol === selected}
211
248
  isIncluded={isIncluded?.(element)}
@@ -270,7 +307,7 @@ const MIN_WIDTH = 280;
270
307
  * table: a cell keeps the band it writes a value in whether or not that table
271
308
  * writes one.
272
309
  */
273
- const ROW_HEIGHT = ofWidth(6.8);
310
+ const ROW_HEIGHT = ofWidth(ROW_SHARE);
274
311
 
275
312
  /** The band the two inner-transition series were lifted out across. */
276
313
  const SERIES_GAP = `max(6px, ${ofWidth(1.1)})`;
@@ -3,7 +3,7 @@
3
3
  * markers the inner-transition series were lifted out of.
4
4
  */
5
5
 
6
- import type { CSSProperties, ReactElement } from 'react';
6
+ import type { CSSProperties, MouseEvent, ReactElement } from 'react';
7
7
 
8
8
  import { useChromeT } from '../../i18n/ui/useT.ts';
9
9
  import { TOKEN } from '../../tokens/core/familyTokens.ts';
@@ -19,8 +19,8 @@ import { ofWidth } from './unit.ts';
19
19
  /** What {@link HeaderStrips} needs. */
20
20
  interface HeaderStripsProps {
21
21
  /**
22
- * Called with the run whose header was clicked. Without it the strips are
23
- * labels rather than buttons.
22
+ * Called with the run whose header was clicked, and whether the click was
23
+ * additive. Without it the strips are labels rather than buttons.
24
24
  * @default undefined
25
25
  */
26
26
  onSelectRange?: (range: ElementRange) => void;
@@ -48,8 +48,12 @@ export function HeaderStrips(props: HeaderStripsProps): ReactElement {
48
48
  row={1}
49
49
  onClick={
50
50
  onSelectRange &&
51
- (() => {
52
- onSelectRange({ kind: 'group', value: group });
51
+ ((event) => {
52
+ onSelectRange({
53
+ kind: 'group',
54
+ value: group,
55
+ additive: isAdditive(event),
56
+ });
53
57
  })
54
58
  }
55
59
  />,
@@ -66,8 +70,12 @@ export function HeaderStrips(props: HeaderStripsProps): ReactElement {
66
70
  row={period + 1}
67
71
  onClick={
68
72
  onSelectRange &&
69
- (() => {
70
- onSelectRange({ kind: 'period', value: period });
73
+ ((event) => {
74
+ onSelectRange({
75
+ kind: 'period',
76
+ value: period,
77
+ additive: isAdditive(event),
78
+ });
71
79
  })
72
80
  }
73
81
  />,
@@ -129,7 +137,18 @@ interface HeaderCellProps {
129
137
  title: string;
130
138
  column: number;
131
139
  row: number;
132
- onClick?: (() => void) | undefined;
140
+ onClick?: ((event: MouseEvent<HTMLButtonElement>) => void) | undefined;
141
+ }
142
+
143
+ /**
144
+ * Whether a click asks for the run on top of the selection rather than in
145
+ * place of it: Cmd on a Mac, Ctrl everywhere else, so one gesture reads the
146
+ * same on both.
147
+ * @param event - The click, for its modifier keys.
148
+ * @returns True when the run is to be added to what is already chosen.
149
+ */
150
+ function isAdditive(event: MouseEvent): boolean {
151
+ return event.metaKey || event.ctrlKey;
133
152
  }
134
153
 
135
154
  function HeaderCell(props: HeaderCellProps): ReactElement {
@@ -0,0 +1,100 @@
1
+ /**
2
+ * The three bands a cell is laid out as, and the column the electrons stand in.
3
+ *
4
+ * Kept beside the cell rather than inside it, because what they say — where the
5
+ * atomic number sits, how tall a band is — is read far more often than the
6
+ * markup that carries them.
7
+ */
8
+
9
+ import type { CSSProperties } from 'react';
10
+
11
+ import { SHELLS_GAP, SHELLS_SHARE, SHELL_LEADING } from './cellType.ts';
12
+ import { ofWidth } from './unit.ts';
13
+
14
+ /** The column the electron counts stand in, as a CSS length. */
15
+ export const SHELLS_COLUMN = ofWidth(SHELLS_SHARE);
16
+
17
+ /** The air between the symbol and that column, as a CSS length. */
18
+ export const SHELLS_AIR = ofWidth(SHELLS_GAP);
19
+
20
+ /**
21
+ * The band at each end of a cell: the atomic number at the top, whatever the
22
+ * tool writes at the bottom.
23
+ *
24
+ * They are one length, not two, and that is what makes the symbol between them
25
+ * sit at the centre of the cell rather than near it — a bottom band wider than
26
+ * the top one by a third lifted every symbol in the table two pixels.
27
+ */
28
+ const EDGE_BAND = ofWidth(1.8);
29
+
30
+ /**
31
+ * A cell is three bands of its own, and every cell has all three.
32
+ *
33
+ * The atomic number is in the top band, against its left edge; the symbol is
34
+ * centred in the middle one; whatever the tool writes is centred in the
35
+ * bottom one. The bands keep their height whether or not anything is written
36
+ * in the last of them, so a number sits at one height across the whole table
37
+ * and a symbol at one other — before this, both rode on a stack the cell
38
+ * centred and moved whenever a neighbour's value was written smaller.
39
+ */
40
+ export const cellStyle = {
41
+ border: '1px solid rgb(255 255 255 / 0.55)',
42
+ borderRadius: 3,
43
+ cursor: 'pointer',
44
+ display: 'grid',
45
+ font: 'inherit',
46
+ gridTemplateRows: `${EDGE_BAND} 1fr ${EDGE_BAND}`,
47
+ justifyItems: 'center',
48
+ minWidth: 0,
49
+ overflow: 'hidden',
50
+ padding: '1px',
51
+ transition: 'opacity 120ms ease, filter 120ms ease',
52
+ } as const satisfies CSSProperties;
53
+
54
+ export const numberStyle = {
55
+ alignSelf: 'start',
56
+ gridColumn: 1,
57
+ gridRow: 1,
58
+ fontSize: `max(0.33rem, ${ofWidth(1.1)})`,
59
+ fontVariantNumeric: 'tabular-nums',
60
+ justifySelf: 'start',
61
+ lineHeight: 1,
62
+ opacity: 0.8,
63
+ } as const satisfies CSSProperties;
64
+
65
+ export const symbolStyle = {
66
+ alignSelf: 'center',
67
+ gridColumn: 1,
68
+ gridRow: 2,
69
+ fontWeight: 700,
70
+ letterSpacing: '-0.02em',
71
+ lineHeight: 1.02,
72
+ } as const satisfies CSSProperties;
73
+
74
+ export const detailStyle = {
75
+ alignSelf: 'center',
76
+ gridColumn: 1,
77
+ gridRow: 3,
78
+ fontVariantNumeric: 'tabular-nums',
79
+ lineHeight: 1,
80
+ overflow: 'hidden',
81
+ whiteSpace: 'nowrap',
82
+ } as const satisfies CSSProperties;
83
+
84
+ /**
85
+ * The electrons of each shell, innermost at the top, down the right-hand edge
86
+ * of the whole cell: the stack is as tall as the three bands together, which
87
+ * is what lets seven of them be read.
88
+ */
89
+ export const shellsStyle = {
90
+ alignSelf: 'center',
91
+ gridColumn: 2,
92
+ gridRow: '1 / span 3',
93
+ fontVariantNumeric: 'tabular-nums',
94
+ justifySelf: 'end',
95
+ lineHeight: SHELL_LEADING,
96
+ opacity: 0.85,
97
+ paddingInlineEnd: `max(0.5px, ${ofWidth(0.15)})`,
98
+ textAlign: 'right',
99
+ whiteSpace: 'pre-line',
100
+ } as const satisfies CSSProperties;
@@ -0,0 +1,223 @@
1
+ /**
2
+ * How large the writing in a cell is.
3
+ *
4
+ * Everything a cell holds is sized against the table's own width, so the same
5
+ * drawing reads at 280px beside a chart and across a lecture-hall screen.
6
+ *
7
+ * Nothing is ever trimmed: a value cut short by an ellipsis is a wrong value —
8
+ * `0.0899` read as `0.0…` is worse than not writing it at all. A line that is
9
+ * still too wide at the size its table writes is condensed instead, which keeps
10
+ * its height, its weight and its colour — the thing a reader compares two cells
11
+ * on — while giving back the width it was missing.
12
+ */
13
+
14
+ import { ADVANCE } from './glyphWidths.ts';
15
+ import { ofWidth } from './unit.ts';
16
+
17
+ /**
18
+ * How wide one line of text is, in per cent of the table's width.
19
+ *
20
+ * A column of the grid is 4.85% of it; this is what a cell leaves of that once
21
+ * its border and its padding are allowed for. One share rather than a share
22
+ * less a few pixels, because the size a line is written at and the width it
23
+ * then has are then the same arithmetic — which is what lets the scale that
24
+ * condenses an over-wide line be a plain number rather than a length only the
25
+ * engine can resolve.
26
+ */
27
+ const TEXT_SHARE = 4.5;
28
+
29
+ /** The column the electron counts stand in, as a share of the table's width. */
30
+ export const SHELLS_SHARE = 1.2;
31
+
32
+ /**
33
+ * The air between the symbol and that column, as a share of the table's width.
34
+ *
35
+ * Without it the widest symbols — `Md`, `Tm`, `Cm`, anything carrying an `M` or
36
+ * an `m` — are written at the cap and end a pixel from the first electron, and
37
+ * the cell reads as two things that collided rather than as two columns.
38
+ */
39
+ export const SHELLS_GAP = 0.5;
40
+
41
+ /** What the counts and their air together take off the width of a cell. */
42
+ export const SHELLS_RESERVED = SHELLS_SHARE + SHELLS_GAP;
43
+
44
+ /** Largest the third line is written, as a share of the table's width. */
45
+ const DETAIL_CAP = 1.5;
46
+
47
+ /**
48
+ * Smallest it is written, as a share of the table's width.
49
+ *
50
+ * Under this a name stops being readable at any table size, so the few names
51
+ * that do not fit are condensed rather than written smaller — the table keeps
52
+ * one size for all 118, which is what makes a column of them readable at a
53
+ * glance.
54
+ */
55
+ const DETAIL_FLOOR = 0.95;
56
+
57
+ /** What one tabular glyph advances, as a share of its own size. */
58
+ const TABULAR_EM = 0.6;
59
+
60
+ /** A string of digits, a decimal point, a sign, an exponent and nothing else. */
61
+ const NUMERIC = /^[\d\s.,+\-−×^eE]+$/u;
62
+
63
+ /** How the third line of a whole table is written. */
64
+ export interface DetailType {
65
+ /** Size every cell writes it at, as a CSS length. */
66
+ size: string;
67
+ /** How wide one em of it is, in shares of the table's width. */
68
+ sizeShare: number;
69
+ }
70
+
71
+ /**
72
+ * The one size a table writes its third line at.
73
+ *
74
+ * The largest at which the widest of them fits, between a floor and a cap: a
75
+ * table whose values are short writes them large, and one writing names writes
76
+ * every name at the size the longest needs.
77
+ * @param widths - How wide each line is, in ems of its own size.
78
+ * @param reservedShare - Width taken by the electron counts, as a share of the
79
+ * table's width; zero when none are written.
80
+ * @returns The size, as a CSS length and as the share it was computed from.
81
+ */
82
+ export function detailType(
83
+ widths: readonly number[],
84
+ reservedShare: number,
85
+ ): DetailType {
86
+ let widest = 1;
87
+ for (const width of widths) {
88
+ if (width > widest) widest = width;
89
+ }
90
+ const room = TEXT_SHARE - reservedShare;
91
+ const share = rounded(
92
+ Math.min(DETAIL_CAP, Math.max(DETAIL_FLOOR, room / widest)),
93
+ );
94
+ // A floor under which the type is not readable whatever the table is sized
95
+ // to, which is the one case the share alone does not cover.
96
+ return { size: `max(0.4rem, ${ofWidth(share)})`, sizeShare: share };
97
+ }
98
+
99
+ /**
100
+ * How much a line has to be condensed to fit its cell.
101
+ * @param widthInEm - How wide the line is, in ems of its own size.
102
+ * @param type - What the table writes its third line at.
103
+ * @param reservedShare - Width taken by the electron counts, as a share of the
104
+ * table's width; zero when none are written.
105
+ * @returns A horizontal scale, never above 1.
106
+ */
107
+ export function condensedScale(
108
+ widthInEm: number,
109
+ type: DetailType,
110
+ reservedShare: number,
111
+ ): number {
112
+ const room = TEXT_SHARE - reservedShare;
113
+ return Math.min(1, room / (widthInEm * type.sizeShare));
114
+ }
115
+
116
+ /**
117
+ * The size the symbol is written at.
118
+ *
119
+ * Every symbol is written at the same size, except the widest of them — the
120
+ * ones whose second letter is an `m` — which are written a tenth smaller rather
121
+ * than run into the edges of their cell.
122
+ * @param symbol - The chemical symbol.
123
+ * @param cap - Largest it is written, as a share of the table's width.
124
+ * @param reservedShare - Width taken by the electron counts, as a share of the
125
+ * table's width; zero when none are written.
126
+ * @returns A CSS length.
127
+ */
128
+ export function symbolFontSize(
129
+ symbol: string,
130
+ cap: number,
131
+ reservedShare: number,
132
+ ): string {
133
+ const room = TEXT_SHARE - reservedShare;
134
+ const share = rounded(Math.min(cap, room / (textWidthInEm(symbol) * BOLD)));
135
+ return `max(0.5rem, ${ofWidth(share)})`;
136
+ }
137
+
138
+ /**
139
+ * A share, at the precision a length is written to.
140
+ * @param share - The share, in per cent of the table's width.
141
+ * @returns The same share, rounded.
142
+ */
143
+ export function rounded(share: number): number {
144
+ return Number(share.toFixed(3));
145
+ }
146
+
147
+ /**
148
+ * How wide a string is, in ems of its own size.
149
+ *
150
+ * A value is written with tabular figures, where every glyph — the decimal
151
+ * point and the minus of an exponent included — advances the same width, so
152
+ * counting its characters is the measurement. A name is proportional, and is
153
+ * measured letter by letter: measured as figures, every name in the table would
154
+ * be sized for a width it does not have.
155
+ * @param text - The string.
156
+ * @returns Its width, never zero.
157
+ */
158
+ export function textWidthInEm(text: string): number {
159
+ if (NUMERIC.test(text)) return Math.max(text.length, 1) * TABULAR_EM * SAFETY;
160
+ let width = 0;
161
+ for (const character of text) {
162
+ width += glyphWidth(character);
163
+ }
164
+ return Math.max(width * SAFETY, TABULAR_EM);
165
+ }
166
+
167
+ /**
168
+ * How wide one glyph is, in ems.
169
+ *
170
+ * Measured in the family's own type rather than guessed at by class: the four
171
+ * classes a glyph can be sorted into put `Tm` an eighth wider than it is, which
172
+ * is a symbol written an eighth smaller than its cell can take.
173
+ * @param character - The glyph.
174
+ * @returns Its advance.
175
+ */
176
+ function glyphWidth(character: string): number {
177
+ const measured = ADVANCE[character];
178
+ if (measured !== undefined) return measured;
179
+ return character >= 'a' && character <= 'z' ? 0.54 : 0.65;
180
+ }
181
+
182
+ /** What is added to every measurement, for the faces this one stands in for. */
183
+ const SAFETY = 1.04;
184
+
185
+ /** How much wider the same string is when it is set bold, as the symbol is. */
186
+ const BOLD = 1.08;
187
+
188
+ /**
189
+ * The size a stack of electron counts is written at.
190
+ *
191
+ * Seven shells have to stand in the height of one cell, so the size is what
192
+ * the deepest stack of the table leaves — francium's seven, wherever the table
193
+ * writes any.
194
+ * @param count - How many shells stand in the tallest stack.
195
+ * @returns A CSS length.
196
+ */
197
+ export function shellFontSize(count: number): string {
198
+ const share = rounded(
199
+ Math.min(SHELL_CAP, ROW_SHARE / (Math.max(count, 1) * SHELL_LEADING)),
200
+ );
201
+ return `max(0.3rem, ${ofWidth(share)})`;
202
+ }
203
+
204
+ /**
205
+ * A horizontal scale, written as a transform.
206
+ * @param scale - The scale, from {@link condensedScale}.
207
+ * @returns The transform, or nothing at all when the line fits as it is.
208
+ */
209
+ export function scaleX(scale: number): string | undefined {
210
+ return scale >= 1 ? undefined : `scaleX(${scale.toFixed(3)})`;
211
+ }
212
+
213
+ /** The size the symbol is written at wherever it fits, as a share of the table. */
214
+ export const SYMBOL_CAP = 2.1;
215
+
216
+ /** How tall one row of the table is, as a share of its width. */
217
+ export const ROW_SHARE = 6.8;
218
+
219
+ /** The size the electron counts are written at where they have the room. */
220
+ const SHELL_CAP = 1.1;
221
+
222
+ /** The height one of them takes, in ems of its own size. */
223
+ export const SHELL_LEADING = 1.2;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * What each letter advances, in ems, in the family's type.
3
+ *
4
+ * Read off the rendered page at 100px, rounded up, because sorting a glyph into
5
+ * one of four width classes puts `Tm` an eighth wider than it is — which is a
6
+ * symbol written an eighth smaller than its cell can take. A platform
7
+ * substituting another face moves these by a few per cent, which the safety
8
+ * margin of `cellType` covers.
9
+ */
10
+ export const ADVANCE: Record<string, number> = {
11
+ a: 0.5,
12
+ b: 0.57,
13
+ c: 0.52,
14
+ d: 0.57,
15
+ e: 0.52,
16
+ f: 0.33,
17
+ g: 0.56,
18
+ h: 0.54,
19
+ i: 0.22,
20
+ j: 0.22,
21
+ k: 0.5,
22
+ l: 0.23,
23
+ m: 0.8,
24
+ n: 0.54,
25
+ o: 0.54,
26
+ p: 0.56,
27
+ q: 0.56,
28
+ r: 0.35,
29
+ s: 0.47,
30
+ t: 0.33,
31
+ u: 0.54,
32
+ v: 0.5,
33
+ w: 0.72,
34
+ x: 0.48,
35
+ y: 0.5,
36
+ z: 0.5,
37
+ A: 0.62,
38
+ B: 0.6,
39
+ C: 0.66,
40
+ D: 0.67,
41
+ E: 0.55,
42
+ F: 0.53,
43
+ G: 0.69,
44
+ H: 0.69,
45
+ I: 0.24,
46
+ J: 0.49,
47
+ K: 0.6,
48
+ L: 0.52,
49
+ M: 0.81,
50
+ N: 0.69,
51
+ O: 0.71,
52
+ P: 0.59,
53
+ Q: 0.71,
54
+ R: 0.6,
55
+ S: 0.59,
56
+ T: 0.59,
57
+ U: 0.68,
58
+ V: 0.62,
59
+ W: 0.9,
60
+ X: 0.62,
61
+ Y: 0.6,
62
+ Z: 0.61,
63
+ ' ': 0.27,
64
+ '-': 0.33,
65
+ '–': 0.54,
66
+ '.': 0.25,
67
+ ',': 0.25,
68
+ };
package/styles/chrome.css CHANGED
@@ -374,8 +374,12 @@ button {
374
374
  nothing to push them there. */
375
375
  .share-linkbar {
376
376
  display: flex;
377
+ /* They keep the right edge while there is room, and wrap under each other
378
+ rather than off the dialog once there is not. */
379
+ flex: 0 1 auto;
380
+ flex-wrap: wrap;
377
381
  align-items: center;
378
- flex: 0 0 auto;
382
+ justify-content: flex-end;
379
383
  margin-left: auto;
380
384
  gap: 8px;
381
385
  }
@@ -588,7 +592,11 @@ button {
588
592
  frames at 1920 is one they check at 1920. */
589
593
  .share-dialog.bp6-dialog {
590
594
  overflow: hidden;
591
- min-width: 480px;
595
+ /* The floor is what a drag may shrink the dialog to, so it must not also
596
+ apply to a screen narrower than it: at 480px the dialog stopped shrinking
597
+ and was clipped on both sides, with no horizontal scroll to reach what was
598
+ cut — the iframe snippet and the part checkboxes, on every phone. */
599
+ min-width: min(480px, 96vw);
592
600
  min-height: 420px;
593
601
  max-width: 98vw;
594
602
  max-height: 96dvh;