@zubra/runtime 5.13.0 → 5.13.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/README.md +1 -1
- package/dist/production/element.js +16 -5
- package/package.json +1 -1
- package/src/element.js +16 -5
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ ships one, this package deletes code instead of components being rewritten.
|
|
|
28
28
|
`// @hot-end` in `src/`). Bundlers pick it through the `production` export condition (Vite and
|
|
29
29
|
webpack do in production builds), and `@zubra/server` serves it when `NODE_ENV` is `production`.
|
|
30
30
|
It's generated by `npm run build:production` and checked in; `npm run test:tools` fails if it's
|
|
31
|
-
out of date, and measures both builds against their size budgets (
|
|
31
|
+
out of date, and measures both builds against their size budgets (16.25 KB and 15.25 KB gzipped).
|
|
32
32
|
|
|
33
33
|
## Signals
|
|
34
34
|
|
|
@@ -821,7 +821,7 @@ function bind(part, node, hydrating, context) {
|
|
|
821
821
|
node[part.property] = $r(part.value());
|
|
822
822
|
});
|
|
823
823
|
} else if (part.attribute) {
|
|
824
|
-
bindAttribute(part, node);
|
|
824
|
+
bindAttribute(part, node, hydrating);
|
|
825
825
|
} else if (part.when) {
|
|
826
826
|
bindWhen(part, node, hydrating, context);
|
|
827
827
|
} else if (part.await) {
|
|
@@ -1130,13 +1130,24 @@ function routeChildren(host, context, slots, tag) {
|
|
|
1130
1130
|
// `checked={done}` with clicks.
|
|
1131
1131
|
const LIVE = new Set(['input.value', 'textarea.value', 'select.value', 'input.checked', 'option.selected']);
|
|
1132
1132
|
|
|
1133
|
-
|
|
1133
|
+
// Changed by someone before the element started: no longer the server's default.
|
|
1134
|
+
const touched = (n, k) => (n.options ? [...n.options].some((o) => o.selected !== o.defaultSelected) : n[k] !== n[`default${k[0].toUpperCase()}${k.slice(1)}`]);
|
|
1135
|
+
|
|
1136
|
+
function bindAttribute(part, node, hydrating = false) {
|
|
1134
1137
|
const name = part.attribute;
|
|
1135
1138
|
// For live state: the last value written, so the property is written only when the bound
|
|
1136
1139
|
// value changes (what the user typed stays until then).
|
|
1137
1140
|
let written;
|
|
1141
|
+
// Server HTML someone already used keeps what they did until the bound value changes.
|
|
1142
|
+
const live = LIVE.has(`${node.localName}.${name}`);
|
|
1143
|
+
let keep = hydrating && live && touched(node, name);
|
|
1138
1144
|
effect(() => {
|
|
1139
1145
|
const value = $r(part.value());
|
|
1146
|
+
if (keep) {
|
|
1147
|
+
keep = false;
|
|
1148
|
+
written = value;
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1140
1151
|
// Another library's custom element: a property when it has one (React 19's rule).
|
|
1141
1152
|
if (part.prop && part.prop in node) {
|
|
1142
1153
|
node[part.prop] = value;
|
|
@@ -1149,14 +1160,14 @@ function bindAttribute(part, node) {
|
|
|
1149
1160
|
}
|
|
1150
1161
|
// A control's live state is its property (the attribute is only the default once the
|
|
1151
1162
|
// user has touched it).
|
|
1152
|
-
if (
|
|
1163
|
+
if (live) {
|
|
1153
1164
|
if (value !== written) node[name] = written = value;
|
|
1154
1165
|
return;
|
|
1155
1166
|
}
|
|
1156
1167
|
node.toggleAttribute(name, value);
|
|
1157
1168
|
return;
|
|
1158
1169
|
}
|
|
1159
|
-
if (
|
|
1170
|
+
if (live) {
|
|
1160
1171
|
const text = value == null ? '' : String(value);
|
|
1161
1172
|
if (text !== written) {
|
|
1162
1173
|
// Unchanged text isn't written: that would move the caret and lose a selection.
|
|
@@ -1164,7 +1175,7 @@ function bindAttribute(part, node) {
|
|
|
1164
1175
|
written = text;
|
|
1165
1176
|
// A <select>'s options may come from a list inside it that renders after this part:
|
|
1166
1177
|
// until the option exists the value doesn't take, so set it again once they're there.
|
|
1167
|
-
if (node.
|
|
1178
|
+
if (node.options && node.value !== text) {
|
|
1168
1179
|
queueMicrotask(() => {
|
|
1169
1180
|
if (written === text && node.value !== text) node.value = text;
|
|
1170
1181
|
});
|
package/package.json
CHANGED
package/src/element.js
CHANGED
|
@@ -846,7 +846,7 @@ function bind(part, node, hydrating, context) {
|
|
|
846
846
|
node[part.property] = $r(part.value());
|
|
847
847
|
});
|
|
848
848
|
} else if (part.attribute) {
|
|
849
|
-
bindAttribute(part, node);
|
|
849
|
+
bindAttribute(part, node, hydrating);
|
|
850
850
|
} else if (part.when) {
|
|
851
851
|
bindWhen(part, node, hydrating, context);
|
|
852
852
|
} else if (part.await) {
|
|
@@ -1158,13 +1158,24 @@ function routeChildren(host, context, slots, tag) {
|
|
|
1158
1158
|
// `checked={done}` with clicks.
|
|
1159
1159
|
const LIVE = new Set(['input.value', 'textarea.value', 'select.value', 'input.checked', 'option.selected']);
|
|
1160
1160
|
|
|
1161
|
-
|
|
1161
|
+
// Changed by someone before the element started: no longer the server's default.
|
|
1162
|
+
const touched = (n, k) => (n.options ? [...n.options].some((o) => o.selected !== o.defaultSelected) : n[k] !== n[`default${k[0].toUpperCase()}${k.slice(1)}`]);
|
|
1163
|
+
|
|
1164
|
+
function bindAttribute(part, node, hydrating = false) {
|
|
1162
1165
|
const name = part.attribute;
|
|
1163
1166
|
// For live state: the last value written, so the property is written only when the bound
|
|
1164
1167
|
// value changes (what the user typed stays until then).
|
|
1165
1168
|
let written;
|
|
1169
|
+
// Server HTML someone already used keeps what they did until the bound value changes.
|
|
1170
|
+
const live = LIVE.has(`${node.localName}.${name}`);
|
|
1171
|
+
let keep = hydrating && live && touched(node, name);
|
|
1166
1172
|
effect(() => {
|
|
1167
1173
|
const value = $r(part.value());
|
|
1174
|
+
if (keep) {
|
|
1175
|
+
keep = false;
|
|
1176
|
+
written = value;
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1168
1179
|
// Another library's custom element: a property when it has one (React 19's rule).
|
|
1169
1180
|
if (part.prop && part.prop in node) {
|
|
1170
1181
|
node[part.prop] = value;
|
|
@@ -1177,14 +1188,14 @@ function bindAttribute(part, node) {
|
|
|
1177
1188
|
}
|
|
1178
1189
|
// A control's live state is its property (the attribute is only the default once the
|
|
1179
1190
|
// user has touched it).
|
|
1180
|
-
if (
|
|
1191
|
+
if (live) {
|
|
1181
1192
|
if (value !== written) node[name] = written = value;
|
|
1182
1193
|
return;
|
|
1183
1194
|
}
|
|
1184
1195
|
node.toggleAttribute(name, value);
|
|
1185
1196
|
return;
|
|
1186
1197
|
}
|
|
1187
|
-
if (
|
|
1198
|
+
if (live) {
|
|
1188
1199
|
const text = value == null ? '' : String(value);
|
|
1189
1200
|
if (text !== written) {
|
|
1190
1201
|
// Unchanged text isn't written: that would move the caret and lose a selection.
|
|
@@ -1192,7 +1203,7 @@ function bindAttribute(part, node) {
|
|
|
1192
1203
|
written = text;
|
|
1193
1204
|
// A <select>'s options may come from a list inside it that renders after this part:
|
|
1194
1205
|
// until the option exists the value doesn't take, so set it again once they're there.
|
|
1195
|
-
if (node.
|
|
1206
|
+
if (node.options && node.value !== text) {
|
|
1196
1207
|
queueMicrotask(() => {
|
|
1197
1208
|
if (written === text && node.value !== text) node.value = text;
|
|
1198
1209
|
});
|