@lvce-editor/component-state-worker 0.1.1 → 0.3.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/README.md CHANGED
@@ -5,3 +5,22 @@ Inspect and edit live LVCE Editor component state as JSON.
5
5
  The worker provides the component-state grid view and the
6
6
  `live-component-state:///<uid>.json` filesystem implementation used by LVCE
7
7
  Editor.
8
+
9
+ ## Development
10
+
11
+ ```sh
12
+ npm ci
13
+ npm run dev
14
+ ```
15
+
16
+ Open `http://localhost:3000`, then run `Developer: Open Component State` from
17
+ the command palette. Select an editable component to open its live state as a
18
+ JSON file. Saving that file applies the new state to the running component.
19
+
20
+ Run the browser tests headlessly with:
21
+
22
+ ```sh
23
+ npm run build
24
+ npm run build:static
25
+ npm run e2e:headless
26
+ ```
@@ -54,6 +54,49 @@ class VError extends Error {
54
54
  }
55
55
  }
56
56
 
57
+ class AssertionError extends Error {
58
+ constructor(message) {
59
+ super(message);
60
+ this.name = 'AssertionError';
61
+ }
62
+ }
63
+ const Object$1 = 1;
64
+ const Number$1 = 2;
65
+ const Array$1 = 3;
66
+ const String$1 = 4;
67
+ const Boolean = 5;
68
+ const Function = 6;
69
+ const Null = 7;
70
+ const Unknown = 8;
71
+ const getType = value => {
72
+ switch (typeof value) {
73
+ case 'number':
74
+ return Number$1;
75
+ case 'function':
76
+ return Function;
77
+ case 'string':
78
+ return String$1;
79
+ case 'object':
80
+ if (value === null) {
81
+ return Null;
82
+ }
83
+ if (Array.isArray(value)) {
84
+ return Array$1;
85
+ }
86
+ return Object$1;
87
+ case 'boolean':
88
+ return Boolean;
89
+ default:
90
+ return Unknown;
91
+ }
92
+ };
93
+ const object = value => {
94
+ const type = getType(value);
95
+ if (type !== Object$1) {
96
+ throw new AssertionError('expected value to be of type object');
97
+ }
98
+ };
99
+
57
100
  const isMessagePort = value => {
58
101
  return value && value instanceof MessagePort;
59
102
  };
@@ -967,6 +1010,9 @@ const {
967
1010
  invoke,
968
1011
  set: set$1
969
1012
  } = create$2(RendererWorker);
1013
+ const getComponents = async () => {
1014
+ return invoke('ComponentState.getComponents');
1015
+ };
970
1016
 
971
1017
  const toCommandId = key => {
972
1018
  const dotIndex = key.indexOf('.');
@@ -1249,12 +1295,20 @@ const create = (uid, x, y, width, height) => {
1249
1295
  set(uid, state, state);
1250
1296
  };
1251
1297
 
1298
+ const cardGap = 8;
1299
+ const cardMinWidth = 180;
1300
+ const viewHorizontalPadding = 32;
1301
+ const getColumnCount = width => {
1302
+ const availableWidth = Math.max(0, width - viewHorizontalPadding);
1303
+ return Math.max(1, Math.floor((availableWidth + cardGap) / (cardMinWidth + cardGap)));
1304
+ };
1305
+
1252
1306
  const diff2 = uid => {
1253
1307
  const {
1254
1308
  oldState,
1255
1309
  scheduledState
1256
1310
  } = get(uid);
1257
- return oldState.components === scheduledState.components && oldState.loaded === scheduledState.loaded ? [] : [1];
1311
+ return oldState.components === scheduledState.components && oldState.loaded === scheduledState.loaded && getColumnCount(oldState.width) === getColumnCount(scheduledState.width) ? [] : [1];
1258
1312
  };
1259
1313
 
1260
1314
  const pattern = /^live-component-state:\/\/\/(\d+)\.json$/;
@@ -1267,11 +1321,6 @@ const getUid = uri => {
1267
1321
  };
1268
1322
  const toUri = uid => `live-component-state:///${uid}.json`;
1269
1323
 
1270
- const assertObject = value => {
1271
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
1272
- throw new TypeError('Component state must be a JSON object');
1273
- }
1274
- };
1275
1324
  const readFile = async uri => {
1276
1325
  const uid = getUid(uri);
1277
1326
  const state = await invoke('ComponentState.getState', uid);
@@ -1280,11 +1329,11 @@ const readFile = async uri => {
1280
1329
  const writeFile = async (uri, content) => {
1281
1330
  const uid = getUid(uri);
1282
1331
  const state = JSON.parse(content);
1283
- assertObject(state);
1332
+ object(state);
1284
1333
  await invoke('ComponentState.setState', uid, state);
1285
1334
  };
1286
1335
  const readDirWithFileTypes = async () => {
1287
- const components = await invoke('ComponentState.getComponents');
1336
+ const components = await getComponents();
1288
1337
  return components.filter(component => component.editable).map(component => ({
1289
1338
  name: `${component.uid}.json`,
1290
1339
  type: File
@@ -1293,7 +1342,7 @@ const readDirWithFileTypes = async () => {
1293
1342
  const isReadonly = () => false;
1294
1343
  const exists = async uri => {
1295
1344
  const uid = getUid(uri);
1296
- const components = await invoke('ComponentState.getComponents');
1345
+ const components = await getComponents();
1297
1346
  return components.some(component => component.uid === uid && component.editable);
1298
1347
  };
1299
1348
 
@@ -1303,7 +1352,7 @@ const handleClick = async (state, uid) => {
1303
1352
  };
1304
1353
 
1305
1354
  const loadContent = async state => {
1306
- const components = await invoke('ComponentState.getComponents');
1355
+ const components = await getComponents();
1307
1356
  return {
1308
1357
  ...state,
1309
1358
  components,
@@ -1319,48 +1368,81 @@ const text = data => {
1319
1368
  };
1320
1369
  };
1321
1370
 
1371
+ const Card = 'ComponentStateCard';
1372
+ const CardStatus = 'ComponentStateCardStatus';
1373
+ const CardTitle = 'ComponentStateCardTitle';
1374
+ const CardUid = 'ComponentStateCardUid';
1375
+ const Description = 'ComponentStateDescription';
1376
+ const Heading = 'ComponentStateHeading';
1377
+ const Row = 'ComponentStateRow';
1378
+ const Rows = 'ComponentStateRows';
1379
+ const View = 'ComponentStateView';
1380
+
1381
+ const HandleClick = 1;
1382
+
1383
+ const titleNode = {
1384
+ childCount: 1,
1385
+ className: CardTitle,
1386
+ type: Strong
1387
+ };
1388
+ const uidNode = {
1389
+ childCount: 1,
1390
+ className: CardUid,
1391
+ type: Span
1392
+ };
1393
+ const statusNode = {
1394
+ childCount: 1,
1395
+ className: CardStatus,
1396
+ type: Span
1397
+ };
1322
1398
  const getCard = component => {
1323
1399
  const status = component.editable ? 'Open JSON state' : 'State API unavailable';
1324
1400
  return [{
1325
1401
  childCount: 3,
1326
- className: 'ComponentStateCard',
1402
+ className: Card,
1327
1403
  'data-uid': String(component.uid),
1328
1404
  disabled: !component.editable,
1329
- onClick: 1,
1405
+ onClick: HandleClick,
1330
1406
  type: Button
1331
- }, {
1332
- childCount: 1,
1333
- className: 'ComponentStateCardTitle',
1334
- type: Strong
1335
- }, text(component.moduleId), {
1336
- childCount: 1,
1337
- className: 'ComponentStateCardUid',
1338
- type: Span
1339
- }, text(`uid ${component.uid}`), {
1340
- childCount: 1,
1341
- className: 'ComponentStateCardStatus',
1342
- type: Span
1343
- }, text(status)];
1344
- };
1345
- const getComponentStateVirtualDom = (components, loaded) => {
1407
+ }, titleNode, text(component.moduleId), uidNode, text(`uid ${component.uid}`), statusNode, text(status)];
1408
+ };
1409
+
1410
+ const viewNode = {
1411
+ childCount: 3,
1412
+ className: View,
1413
+ type: Div
1414
+ };
1415
+ const headingNode = {
1416
+ childCount: 1,
1417
+ className: Heading,
1418
+ type: H2
1419
+ };
1420
+ const descriptionNode = {
1421
+ childCount: 1,
1422
+ className: Description,
1423
+ type: Div
1424
+ };
1425
+ const getRows = (components, columnCount) => {
1426
+ const rows = [];
1427
+ for (let index = 0; index < components.length; index += columnCount) {
1428
+ const row = components.slice(index, index + columnCount);
1429
+ rows.push({
1430
+ childCount: row.length,
1431
+ className: Row,
1432
+ type: Div
1433
+ }, ...row.flatMap(getCard));
1434
+ }
1435
+ return rows;
1436
+ };
1437
+ const getComponentStateVirtualDom = (components, loaded, width) => {
1346
1438
  const description = loaded ? `${components.length} live components` : 'Loading live components…';
1347
- return [{
1348
- childCount: 3,
1349
- className: 'ComponentStateView',
1350
- type: Div
1351
- }, {
1352
- childCount: 1,
1353
- className: 'ComponentStateHeading',
1354
- type: H2
1355
- }, text('Live Component State'), {
1356
- childCount: 1,
1357
- className: 'ComponentStateDescription',
1358
- type: Div
1359
- }, text(description), {
1360
- childCount: components.length,
1361
- className: 'ComponentStateGrid',
1439
+ const columnCount = getColumnCount(width);
1440
+ const rowCount = Math.ceil(components.length / columnCount);
1441
+ return [viewNode, headingNode, text('Live Component State'), descriptionNode, text(description), {
1442
+ childCount: rowCount,
1443
+ className: Rows,
1362
1444
  type: Div
1363
- }, ...components.flatMap(getCard)];
1445
+ }, ...getRows(components, columnCount)];
1364
1446
  };
1365
1447
 
1366
1448
  const render2 = (uid, diffResult) => {
@@ -1371,11 +1453,11 @@ const render2 = (uid, diffResult) => {
1371
1453
  if (diffResult.length === 0) {
1372
1454
  return [];
1373
1455
  }
1374
- return [[SetDom2, uid, getComponentStateVirtualDom(newState.components, newState.loaded)]];
1456
+ return [[SetDom2, uid, getComponentStateVirtualDom(newState.components, newState.loaded, newState.width)]];
1375
1457
  };
1376
1458
 
1377
1459
  const renderEventListeners = () => [{
1378
- name: 1,
1460
+ name: HandleClick,
1379
1461
  params: ['handleClick', 'event.currentTarget.dataset.uid'],
1380
1462
  preventDefault: true
1381
1463
  }];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/component-state-worker",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "Inspect and edit live LVCE component state",
5
5
  "keywords": [
6
6
  "component-state",