@jsenv/navi 0.29.112 → 0.29.115
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/dist/jsenv_navi.js +658 -567
- package/dist/jsenv_navi.js.map +52 -58
- package/dist/jsenv_navi_side_effects.js +37 -0
- package/dist/jsenv_navi_side_effects.js.map +2 -2
- package/docs/AI_INSTRUCTIONS.md +4 -4
- package/docs/actions.md +14 -0
- package/docs/create_and_edit.md +3 -1
- package/docs/typography.md +52 -69
- package/package.json +1 -1
package/docs/AI_INSTRUCTIONS.md
CHANGED
|
@@ -214,11 +214,11 @@ consistency across the app, not from any single call site.
|
|
|
214
214
|
not it), what makes truncation actually happen in a flex row, and the two
|
|
215
215
|
opposite shapes of "icon | text | icon" — the end icon kept outside the
|
|
216
216
|
truncating text, or `attachLastChild` so it never lands alone on a line —
|
|
217
|
-
and why
|
|
218
|
-
|
|
219
|
-
|
|
217
|
+
and why every text — controls included — is written on one line height
|
|
218
|
+
(`--navi-line-height`, 1.25): the number below which an emoji is clipped and
|
|
219
|
+
above which the rows drift apart.
|
|
220
220
|
Read it before writing an overflowing label or a row with a trailing icon,
|
|
221
|
-
and before touching
|
|
221
|
+
and before touching any `line-height`.
|
|
222
222
|
- `docs/i18n.md` — where the texts an app displays live: `interpolateText` /
|
|
223
223
|
`<Interpolate>` for one sentence, `createI18n` for the app's registry,
|
|
224
224
|
`naviI18n` for navi's own texts. Read it before writing a user-visible
|
package/docs/actions.md
CHANGED
|
@@ -97,6 +97,20 @@ it — and why a run settles with its error rather than rejecting — is
|
|
|
97
97
|
state at once — `{ idle, loading, completed, aborted, error, data, params }` —
|
|
98
98
|
for a component that needs to look at it rather than render it.
|
|
99
99
|
|
|
100
|
+
**Reading does not run.** `useAsyncData` waits for data someone else asked for
|
|
101
|
+
— for a page, the route, through `routeAction`. A component reading an action
|
|
102
|
+
that nobody ran suspends, and `<Loading>` draws nothing for an idle action (no
|
|
103
|
+
spinner for a request nobody sent): the whole subtree stays blank, for good. The
|
|
104
|
+
obvious fix does not work — a `useEffect` in that same component that would
|
|
105
|
+
`run()` it never fires, because a suspended component has no effects, and the
|
|
106
|
+
run it was about to start is exactly what the suspension waits for. So either
|
|
107
|
+
the action is started **before** anything reads it — bound where the screen is
|
|
108
|
+
decided: a `routeAction`, a `<Button action>`, the `action` of the `<Link>` one
|
|
109
|
+
came in by — or the component owns its request and does not suspend on it: a
|
|
110
|
+
folding panel, a slice a button asks for. That one reads `action.dataSignal` and
|
|
111
|
+
`action.errorSignal` (or `useActionStatus`) directly, `run()`s the action from an
|
|
112
|
+
effect, and draws its own skeleton until the data is there.
|
|
113
|
+
|
|
100
114
|
`{ onLoad }` is what the screen does with the data **once, when it becomes
|
|
101
115
|
known** — seed the fields someone is about to edit, focus something, remember
|
|
102
116
|
where a list was:
|
package/docs/create_and_edit.md
CHANGED
|
@@ -134,7 +134,9 @@ Two things to know or the screen stays empty:
|
|
|
134
134
|
- **Reading an action does not start it.** `useAsyncData` waits for data
|
|
135
135
|
someone else asked for; what asks is the route (`routeAction`). A component
|
|
136
136
|
reading an action nobody runs suspends forever, and the whole `<Loading>`
|
|
137
|
-
subtree stays blank
|
|
137
|
+
subtree stays blank — and an effect in that component cannot rescue it, a
|
|
138
|
+
suspended component has no effects. What to do instead is under "Reading an
|
|
139
|
+
action" in [actions.md](./actions.md#reading-an-action).
|
|
138
140
|
- **Handle the error where it happens**, or hand it to an `<ErrorBoundary>`:
|
|
139
141
|
`useAsyncData(action, { loading: true, error: true })` returns
|
|
140
142
|
`[data, loading, error]`, which is what lets a page draw its own "the server
|
package/docs/typography.md
CHANGED
|
@@ -159,75 +159,58 @@ Two things opt out of that flow:
|
|
|
159
159
|
|
|
160
160
|
**An emoji is not a character the layout can absorb.** The system emoji fonts
|
|
161
161
|
(Apple Color Emoji, Segoe UI Emoji, Noto Color Emoji) have a taller
|
|
162
|
-
ascent/descent than any text font,
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
`
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
an emoji (see the dense cases in `text_emoji_demo.html`). A `lineHeight` is a
|
|
215
|
-
typographic choice for the text itself — a paragraph that wants air — never a
|
|
216
|
-
workaround for a glyph that grew too tall; that glyph gets `emojiAsIcon`.
|
|
217
|
-
|
|
218
|
-
**A control one types in is the exception, and there the line height is the
|
|
219
|
-
whole answer.** `emojiAsIcon` rewrites strings into markup, and there is no
|
|
220
|
-
markup inside a `<textarea>` or an `<input>`: the value is raw text the browser
|
|
221
|
-
draws itself, so no glyph in it can be wrapped in anything. Under
|
|
222
|
-
`line-height: normal` a line box takes the height of the tallest font it holds,
|
|
223
|
-
so the one line carrying an emoji stands taller than the ones around it — rows
|
|
224
|
-
of uneven height, and a box sized in `lh` (`minRows`/`maxRows`) that jumps as
|
|
225
|
-
soon as one is typed. `Textarea` therefore fixes its line height, and picks a
|
|
226
|
-
value tall enough to contain an emoji's own box so the glyph is not clipped
|
|
227
|
-
either: a tighter one would keep the rows even and cut the top off the emoji.
|
|
228
|
-
Do not remove it, and do not tighten it — `34_textarea_demo.html` shows the two
|
|
229
|
-
side by side. The rule above still holds everywhere the text is rendered rather
|
|
230
|
-
than typed.
|
|
162
|
+
ascent/descent than any text font, and under `line-height: normal` a line box
|
|
163
|
+
takes the height of the tallest font it holds. So the moment an emoji sits in a
|
|
164
|
+
line, that line is taller than the ones around it: a row shifts down next to its
|
|
165
|
+
neighbours, a paragraph's lines are unevenly spaced, a truncated label no longer
|
|
166
|
+
lines up with its siblings. Tighten the line to get the rows even again and the
|
|
167
|
+
glyph is clipped instead — its box is taller than a letter's, and what sticks
|
|
168
|
+
out is simply cut.
|
|
169
|
+
|
|
170
|
+
**One number answers both: `--navi-line-height`, 1.25.** Tall enough to contain
|
|
171
|
+
an emoji's own box, so nothing is clipped; tight enough that a line carrying one
|
|
172
|
+
is exactly as tall as a line of plain text, so nothing moves. 1 cuts the top off
|
|
173
|
+
the glyph, 1.5 spaces the rows out more than reading them asks for. **An app
|
|
174
|
+
that displays what people typed cannot go below 1.25** — that is the floor this
|
|
175
|
+
token encodes, and the reason it is a token rather than a number repeated in
|
|
176
|
+
every component.
|
|
177
|
+
|
|
178
|
+
**Everything is written on that line, controls included.** The document sets it
|
|
179
|
+
on `:root`, and the components that would otherwise come with a line of their
|
|
180
|
+
own from the browser — `Button`, `Input`, `Textarea`, `Select` — are handed it
|
|
181
|
+
by name: a form control inherits nothing from the page on its own, and what it
|
|
182
|
+
starts from is `normal`, the one value the emoji breaks. `Picker` and the items
|
|
183
|
+
of a selectable `List` take it too, though they are drawn by hand: they stand
|
|
184
|
+
in a row with those, sized in `lh` like them, and have to be the same number
|
|
185
|
+
of pixels tall. That is what makes a
|
|
186
|
+
value keep its line, and its emoji its size, as it passes from the field it was
|
|
187
|
+
typed in to whatever displays it afterwards — an emoji typed in an input is not
|
|
188
|
+
clipped, and the same emoji displayed in a row, a bubble or a button does not
|
|
189
|
+
push anything around. `text_emoji_demo.html` shows it by swapping the field and
|
|
190
|
+
the rendering in place.
|
|
191
|
+
|
|
192
|
+
A control takes it snapped to the pixel (`--navi-control-line-height`,
|
|
193
|
+
`round(1.25em, 1px)`). The browser lays a line out at its exact height but
|
|
194
|
+
paints the glyph on a pixel row, and at the default control size (13.333px)
|
|
195
|
+
the line is 16.666px: the two-thirds that do not fit go entirely under the
|
|
196
|
+
glyph, which then sits a pixel above the middle of its field — the placeholder
|
|
197
|
+
looks too high. A whole number of pixels has no remainder to put anywhere, and
|
|
198
|
+
a `Textarea` sized in `lh` lands on the same grid as the placeholder it
|
|
199
|
+
measures. The page's text keeps the plain number: a length would stop
|
|
200
|
+
following the font size the moment it is inherited.
|
|
201
|
+
|
|
202
|
+
Change it on `:root` for a whole app. Do not unset it on a component, do not let
|
|
203
|
+
one fall back to `normal`, and do not raise a local `lineHeight` because of an
|
|
204
|
+
emoji: a `lineHeight` is a typographic choice for the text itself — a paragraph
|
|
205
|
+
that wants air — never a workaround for a glyph that grew too tall.
|
|
206
|
+
|
|
207
|
+
**Where an emoji belongs is a separate question, and it is not a layout one.**
|
|
208
|
+
A field can still refuse emoji outright (`noEmoji`) where the value is an
|
|
209
|
+
identifier rather than free text. A name is the case worth stating: people are
|
|
210
|
+
called what they are called, and a whitelist on a name field ends up refusing
|
|
211
|
+
somebody's real name — a name field keeps to the rules that are about what can
|
|
212
|
+
be drawn at all (`data-displayable`: marks stacked into zalgo, a value that
|
|
213
|
+
shows nothing, blank lines in series).
|
|
231
214
|
|
|
232
215
|
## Text that must not move when its style changes
|
|
233
216
|
|