lucid-extension-sdk 0.0.156 → 0.0.158

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/commandtypes.d.ts CHANGED
@@ -120,6 +120,7 @@ export declare const enum CommandName {
120
120
  UnhookSelection = "us",
121
121
  UnhookTextEdit = "ute",
122
122
  WithMutex = "wm",
123
+ WithSilentActions = "wsa",
123
124
  ZOrder = "z"
124
125
  }
125
126
  /** @ignore */
@@ -530,6 +531,10 @@ export declare type CommandArgs = {
530
531
  query: WithMutexQuery;
531
532
  result: WithMutexResult;
532
533
  };
534
+ [CommandName.WithSilentActions]: {
535
+ query: WithSilentActionsQuery;
536
+ result: WithSilentActionsResult;
537
+ };
533
538
  [CommandName.ZOrder]: {
534
539
  query: ZOrderQuery;
535
540
  result: ZOrderResult;
@@ -1294,6 +1299,11 @@ export declare type WithMutexQuery = {
1294
1299
  };
1295
1300
  /** Resolves true if operation succeeded, or false if the mutex was held by someone else */
1296
1301
  export declare type WithMutexResult = Promise<boolean>;
1302
+ export declare type WithSilentActionsQuery = {
1303
+ /** Name of the synchronous action to run with undo/redo history suppressed */
1304
+ 'a': string;
1305
+ };
1306
+ export declare type WithSilentActionsResult = void;
1297
1307
  export declare enum ZOrderOperation {
1298
1308
  UP = 1,
1299
1309
  TOP = 2,
package/commandtypes.js CHANGED
@@ -100,6 +100,7 @@ exports.commandTitles = new Map([
100
100
  ["us" /* CommandName.UnhookSelection */, 'UnhookSelection'],
101
101
  ["ute" /* CommandName.UnhookTextEdit */, 'UnhookTextEdit'],
102
102
  ["wm" /* CommandName.WithMutex */, 'WithMutex'],
103
+ ["wsa" /* CommandName.WithSilentActions */, 'WithSilentActions'],
103
104
  ["z" /* CommandName.ZOrder */, 'ZOrder'],
104
105
  ]);
105
106
  var GetItemsAtSearchType;
@@ -5,10 +5,19 @@ import { UpstreamUpdateType } from './upstreamupdatetype';
5
5
  /** @ignore */
6
6
  export declare function alphabetize(n: number): string;
7
7
  /** @ignore */
8
+ export declare function normalizeName(name: string): string;
9
+ /** @ignore */
10
+ export declare function makeNameReadablyUnique(originalName: string, usedNamesNormalized: Set<string>, transform?: typeof normalizeName): string;
11
+ /** @ignore */
8
12
  export declare const MetadataPK = "__PK__";
9
13
  /** @ignore until spreadsheet integration is ready for launch (CHART-51946) */
10
- export declare function makeSerializedImportedCollection(id: string, hash: string, name: string, rawSchema: string[], data: SerializedFields[], metadata?: {
14
+ export declare function makeSerializedImportedCollection(name: string, rawFieldNames: string[], data: SerializedFields[], upstreamConfig: {
15
+ [key: string]: any;
16
+ }, metadata?: {
11
17
  [key: string]: SerializedFields[];
18
+ }, schemaFromData?: {
19
+ headerRow: number;
20
+ primaryKey: string[];
12
21
  }): SerializedImportedCollection;
13
22
  /** @ignore until spreadsheet integration is ready for launch (CHART-51946) */
14
23
  export declare function makeSerializedImportedDataSource(id: string, name: string, collections: SerializedImportedCollection[]): {
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.makeSerializedImportedDataSource = exports.makeSerializedImportedCollection = exports.MetadataPK = exports.alphabetize = void 0;
3
+ exports.makeSerializedImportedDataSource = exports.makeSerializedImportedCollection = exports.MetadataPK = exports.makeNameReadablyUnique = exports.normalizeName = exports.alphabetize = void 0;
4
+ const checks_1 = require("../../checks");
4
5
  const scalarfieldtype_1 = require("../fieldtypedefinition/scalarfieldtype");
5
6
  const alphabet_1 = require("./alphabet");
6
7
  const datasourcetype_1 = require("./datasourcetype");
@@ -34,23 +35,84 @@ exports.alphabetize = alphabetize;
34
35
  function normalizeName(name) {
35
36
  return name.trim().toLowerCase();
36
37
  }
38
+ exports.normalizeName = normalizeName;
39
+ const appendedNumberRegex = /(.*) \(([0-9]+)\)$/;
40
+ /** @ignore */
41
+ function makeNameReadablyUnique(originalName, usedNamesNormalized, transform = normalizeName) {
42
+ if (!usedNamesNormalized.has(transform(originalName))) {
43
+ return originalName;
44
+ }
45
+ const match = appendedNumberRegex.exec(originalName);
46
+ let count = 1;
47
+ if (match && (0, checks_1.isString)(match[1]) && (0, checks_1.isString)(match[2])) {
48
+ originalName = match[1];
49
+ count = Number(match[2]) + 1;
50
+ }
51
+ let newName = originalName;
52
+ do {
53
+ newName = originalName + ` (${count})`;
54
+ count++;
55
+ } while (usedNamesNormalized.has(transform(newName)));
56
+ return newName;
57
+ }
58
+ exports.makeNameReadablyUnique = makeNameReadablyUnique;
37
59
  /** @ignore */
38
60
  exports.MetadataPK = '__PK__';
39
61
  /** @ignore */
40
- function makeSchema(schemaFields) {
62
+ function makePrimaryCollectionSchema(rawSchemaFields, data, schemaFromData) {
63
+ var _a, _b;
64
+ const headerRow = (_a = schemaFromData === null || schemaFromData === void 0 ? void 0 : schemaFromData.headerRow) !== null && _a !== void 0 ? _a : -1;
65
+ const headerRowData = headerRow >= 0 ? data[headerRow] : undefined;
66
+ const headerRowAsStringArray = headerRowData &&
67
+ rawSchemaFields.map((rawSchemaField, index) => {
68
+ var _a;
69
+ const headerRowDataAsString = `${(_a = headerRowData[rawSchemaField]) !== null && _a !== void 0 ? _a : ''}`;
70
+ return headerRowDataAsString.trim() === '' ? alphabetize(index) : headerRowDataAsString;
71
+ });
72
+ const schemaFields = headerRowAsStringArray !== null && headerRowAsStringArray !== void 0 ? headerRowAsStringArray : rawSchemaFields;
41
73
  const fields = [];
42
74
  const oldToNewFields = new Map();
43
75
  const usedFieldsNormalized = new Set([normalizeName(exports.MetadataPK)]);
44
76
  schemaFields.forEach((field, index) => {
45
- // don't add duplicates
46
- if (!usedFieldsNormalized.has(normalizeName(field))) {
47
- usedFieldsNormalized.add(normalizeName(field));
48
- // for now we're changing all schema to the alphabet until preview data can be skipped
49
- fields.push({ 'Name': alphabetize(index), 'Type': scalarfieldtype_1.ScalarFieldTypeEnum.ANY });
50
- oldToNewFields.set(field, alphabetize(index));
77
+ const finalFieldName = makeNameReadablyUnique(field, usedFieldsNormalized);
78
+ usedFieldsNormalized.add(normalizeName(finalFieldName));
79
+ fields.push({ 'Name': finalFieldName, 'Type': scalarfieldtype_1.ScalarFieldTypeEnum.ANY });
80
+ oldToNewFields.set(rawSchemaFields[index], finalFieldName);
81
+ });
82
+ const oldPrimaryKeys = (_b = schemaFromData === null || schemaFromData === void 0 ? void 0 : schemaFromData.primaryKey) !== null && _b !== void 0 ? _b : [];
83
+ const translatedPrimaryKeys = oldPrimaryKeys
84
+ .map((oldPrimaryKey) => oldToNewFields.get(oldPrimaryKey))
85
+ .filter(checks_1.isDefAndNotNull); // There should be no untranslatable primary keys, but we need to satisfy the type system
86
+ return {
87
+ sheetSchema: {
88
+ 'Fields': fields,
89
+ 'PrimaryKey': translatedPrimaryKeys,
90
+ },
91
+ oldToNewFields,
92
+ };
93
+ }
94
+ function getPrimaryKeysForData(oldToNewFields, data, schemaFromData) {
95
+ if (!schemaFromData || schemaFromData.primaryKey.length === 0) {
96
+ return data.map((row, index) => `${index + 1}`);
97
+ }
98
+ const primaryKey = schemaFromData.primaryKey;
99
+ const primaryKeyInNewFields = primaryKey.map((oldField) => { var _a; return (_a = oldToNewFields.get(oldField)) !== null && _a !== void 0 ? _a : oldField; });
100
+ // For reasons I have forgotten, the actual primary key order is determined by the alphabetized name order.
101
+ const sortedPrimaryKeyInNewFields = primaryKeyInNewFields.slice().sort();
102
+ const sortedPrimaryKey = sortedPrimaryKeyInNewFields.map((newField) => primaryKey[primaryKeyInNewFields.indexOf(newField)]);
103
+ const usedNames = new Set();
104
+ return data.map((serializedField) => {
105
+ const primaryKeyValues = sortedPrimaryKey.map((key) => serializedField[key]);
106
+ const compositeValue = JSON.stringify(primaryKeyValues);
107
+ const nameWithoutContext = compositeValue.slice(1, compositeValue.length - 1);
108
+ let name = nameWithoutContext;
109
+ let dupCount = -1;
110
+ while (usedNames.has(name)) {
111
+ name = `${nameWithoutContext},${++dupCount}`;
51
112
  }
113
+ usedNames.add(name);
114
+ return name;
52
115
  });
53
- return { sheetSchema: { 'Fields': fields, 'PrimaryKey': [] }, oldToNewFields };
54
116
  }
55
117
  /** @ignore */
56
118
  function makeMetadataCollection(metadataType, sheetName, dataItems, sheetSchema) {
@@ -61,11 +123,15 @@ function makeMetadataCollection(metadataType, sheetName, dataItems, sheetSchema)
61
123
  };
62
124
  }
63
125
  /** @ignore */
64
- function makeItemOrderCollection(sheetLength, sheetName) {
126
+ function makeItemOrderCollection(primaryKeys, sheetName, headerRow) {
65
127
  const itemOrderItems = {};
66
- for (let i = 1; i <= sheetLength; ++i) {
67
- const rowIndex = i;
68
- itemOrderItems[`${rowIndex}`] = { 'Key': `${rowIndex}`, 'Order': rowIndex };
128
+ let rowIndex = 0;
129
+ for (let i = 0; i < primaryKeys.length; i++) {
130
+ const primaryKey = primaryKeys[i];
131
+ if (i !== headerRow) {
132
+ rowIndex++;
133
+ itemOrderItems[`${rowIndex}`] = { 'Key': `${primaryKey}`, 'Order': rowIndex };
134
+ }
69
135
  }
70
136
  const itemOrderSchema = {
71
137
  'Fields': [
@@ -77,52 +143,70 @@ function makeItemOrderCollection(sheetLength, sheetName) {
77
143
  return makeMetadataCollection('ItemOrder', sheetName, itemOrderItems, itemOrderSchema);
78
144
  }
79
145
  /** @ignore */
80
- function makeSerializedItems(rawItems, oldToNewFields, isMetadata = false) {
146
+ function makeHeaderrowMetadataCollection(headerRowPrimaryKey, headerRowData, oldToNewFields, sheetName, schema) {
147
+ const dataItem = {};
148
+ for (const [key, field] of Object.entries(headerRowData)) {
149
+ const newKey = oldToNewFields.get(key);
150
+ if (newKey) {
151
+ dataItem[newKey] = field;
152
+ }
153
+ }
154
+ const itemOrderItems = {
155
+ [headerRowPrimaryKey]: dataItem,
156
+ };
157
+ return makeMetadataCollection('HeaderRowMetadata', sheetName, itemOrderItems, schema);
158
+ }
159
+ /** @ignore */
160
+ function makeSerializedItems(primaryKeys, rawItems, oldToNewFields, headerRow, isMetadata = false) {
81
161
  const serializedItems = {};
82
162
  rawItems.forEach((rawItem, rowIndex) => {
83
- const dataItem = {};
84
- for (const [key, field] of Object.entries(rawItem)) {
85
- const newKey = oldToNewFields.get(key);
86
- if (newKey) {
87
- dataItem[newKey] = field;
163
+ if (isMetadata || rowIndex !== headerRow) {
164
+ const dataItem = {};
165
+ for (const [key, field] of Object.entries(rawItem)) {
166
+ const newKey = oldToNewFields.get(key);
167
+ if (newKey) {
168
+ dataItem[newKey] = field;
169
+ }
170
+ }
171
+ const primaryKey = primaryKeys[rowIndex];
172
+ if (isMetadata && Object.keys(dataItem).length > 0) {
173
+ const metadataPrimaryKey = `${primaryKey}`;
174
+ dataItem[exports.MetadataPK] = metadataPrimaryKey;
175
+ serializedItems[`${JSON.stringify(metadataPrimaryKey)}`] = dataItem;
176
+ }
177
+ else if (!isMetadata) {
178
+ serializedItems[primaryKey] = dataItem;
88
179
  }
89
- }
90
- const primaryKey = rowIndex + 1;
91
- if (isMetadata && Object.keys(dataItem).length > 0) {
92
- dataItem[exports.MetadataPK] = `${primaryKey}`;
93
- serializedItems[`"${primaryKey}"`] = dataItem;
94
- }
95
- else if (!isMetadata) {
96
- serializedItems[primaryKey] = dataItem;
97
180
  }
98
181
  });
99
182
  return serializedItems;
100
183
  }
101
184
  /** @ignore until spreadsheet integration is ready for launch (CHART-51946) */
102
- function makeSerializedImportedCollection(id, hash, name, rawSchema, data, metadata) {
103
- const { sheetSchema, oldToNewFields } = makeSchema(rawSchema);
185
+ function makeSerializedImportedCollection(name, rawFieldNames, data, upstreamConfig, metadata, schemaFromData) {
186
+ var _a;
187
+ const { sheetSchema, oldToNewFields } = makePrimaryCollectionSchema(rawFieldNames, data, schemaFromData);
188
+ const primaryKeys = getPrimaryKeysForData(oldToNewFields, data, schemaFromData);
189
+ const headerRow = (_a = schemaFromData === null || schemaFromData === void 0 ? void 0 : schemaFromData.headerRow) !== null && _a !== void 0 ? _a : -1;
104
190
  const metadataCollections = {};
105
- const itemOrderCollection = makeItemOrderCollection(data.length, name);
106
- metadataCollections['ItemOrder'] = itemOrderCollection;
191
+ metadataCollections['ItemOrder'] = makeItemOrderCollection(primaryKeys, name, headerRow);
192
+ if (headerRow > -1 && headerRow < data.length) {
193
+ const headerRowData = data[headerRow];
194
+ const headerRowPrimaryKey = primaryKeys[headerRow];
195
+ metadataCollections['HeaderRowMetadata'] = makeHeaderrowMetadataCollection(headerRowPrimaryKey, headerRowData, oldToNewFields, name, sheetSchema);
196
+ }
107
197
  if (metadata) {
108
198
  const metadataFields = [{ 'Name': exports.MetadataPK, 'Type': scalarfieldtype_1.ScalarFieldTypeEnum.STRING }];
109
199
  sheetSchema['Fields'].forEach((field) => metadataFields.push({ 'Name': field['Name'], 'Type': scalarfieldtype_1.ScalarFieldTypeEnum.STRING }));
110
200
  const metadataSchema = { 'Fields': metadataFields, 'PrimaryKey': [exports.MetadataPK] };
111
201
  for (const [type, data] of Object.entries(metadata)) {
112
- metadataCollections[type] = makeMetadataCollection(type, name, makeSerializedItems(data, oldToNewFields, true), metadataSchema);
202
+ metadataCollections[type] = makeMetadataCollection(type, name, makeSerializedItems(primaryKeys, data, oldToNewFields, headerRow, true), metadataSchema);
113
203
  }
114
204
  }
115
205
  return {
116
206
  'Name': name,
117
207
  'Schema': sheetSchema,
118
- 'Items': makeSerializedItems(data, oldToNewFields),
119
- 'UpstreamConfig': {
120
- 'properties': {
121
- 'UpstreamType': upstreamupdatetype_1.UpstreamUpdateType.EVENTPULL,
122
- 'sheetId': id,
123
- 'hash': hash,
124
- },
125
- },
208
+ 'Items': makeSerializedItems(primaryKeys, data, oldToNewFields, headerRow),
209
+ 'UpstreamConfig': upstreamConfig,
126
210
  'Metadata': metadataCollections,
127
211
  };
128
212
  }
@@ -4,5 +4,6 @@ export declare enum MetadataTypes {
4
4
  FillColor = "FillColor",
5
5
  TextColor = "TextColor",
6
6
  Comment = "Comment",
7
- Formula = "Formula"
7
+ Formula = "Formula",
8
+ HeaderRowMetadata = "HeaderRowMetadata"
8
9
  }
@@ -11,4 +11,5 @@ var MetadataTypes;
11
11
  MetadataTypes["TextColor"] = "TextColor";
12
12
  MetadataTypes["Comment"] = "Comment";
13
13
  MetadataTypes["Formula"] = "Formula";
14
+ MetadataTypes["HeaderRowMetadata"] = "HeaderRowMetadata";
14
15
  })(MetadataTypes = exports.MetadataTypes || (exports.MetadataTypes = {}));
package/editorclient.d.ts CHANGED
@@ -325,5 +325,12 @@ export declare class EditorClient {
325
325
  * @returns A promise resolving to a boolean indicating whether the mutex was successfully locked
326
326
  */
327
327
  withIntraDocumentMutex(name: string, callback: () => void | Promise<void>): Promise<boolean>;
328
+ /**
329
+ * @param callback Callback that will be executed with the user's local undo/redo history suppressed. This is
330
+ * useful when you want to make changes to a document that will not be erased if the user uses undo or redo,
331
+ * for example adding shape data onto shapes as a result of a background process that collects data from a
332
+ * remote API.
333
+ */
334
+ withSilentActions(callback: () => void): void;
328
335
  constructor();
329
336
  }
package/editorclient.js CHANGED
@@ -545,7 +545,28 @@ class EditorClient {
545
545
  return await this.sendCommand("wm" /* CommandName.WithMutex */, { 'n': name, 'a': action });
546
546
  }
547
547
  finally {
548
- this.deleteAction(name);
548
+ this.deleteAction(action);
549
+ }
550
+ }
551
+ /**
552
+ * @param callback Callback that will be executed with the user's local undo/redo history suppressed. This is
553
+ * useful when you want to make changes to a document that will not be erased if the user uses undo or redo,
554
+ * for example adding shape data onto shapes as a result of a background process that collects data from a
555
+ * remote API.
556
+ */
557
+ withSilentActions(callback) {
558
+ const action = this.getUniqueActionName();
559
+ this.registerAction(action, () => {
560
+ const result = callback();
561
+ if ((0, checks_1.isPromise)(result)) {
562
+ throw new Error('withSilentActions cannot be used with an async callback');
563
+ }
564
+ });
565
+ try {
566
+ this.sendCommand("wsa" /* CommandName.WithSilentActions */, { 'a': action });
567
+ }
568
+ finally {
569
+ this.deleteAction(action);
549
570
  }
550
571
  }
551
572
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lucid-extension-sdk",
3
- "version": "0.0.156",
3
+ "version": "0.0.158",
4
4
  "description": "Utility classes for writing Lucid Software editor extensions",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",