@portabletext/block-tools 3.5.0 → 3.5.2
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/lib/rules/index.cjs
CHANGED
|
@@ -9,16 +9,17 @@ function createFlattenTableRule({
|
|
|
9
9
|
deserialize: (node, next) => {
|
|
10
10
|
if (!helpers.isElement(node) || helpers.tagName(node) !== "table")
|
|
11
11
|
return;
|
|
12
|
-
const
|
|
13
|
-
if (!
|
|
12
|
+
const columnCounts = [...node.querySelectorAll("tr")].map((row) => row.querySelectorAll("td, th").length), firstColumnCount = columnCounts[0];
|
|
13
|
+
if (!firstColumnCount || !columnCounts.every((count) => count === firstColumnCount))
|
|
14
14
|
return;
|
|
15
|
-
const headerRow =
|
|
16
|
-
|
|
15
|
+
const headerRow = node.querySelector("thead")?.querySelector("tr"), tbody = node.querySelector("tbody");
|
|
16
|
+
let bodyRows = tbody ? [...tbody.querySelectorAll("tr")] : [];
|
|
17
|
+
if (!headerRow || !bodyRows)
|
|
17
18
|
return;
|
|
18
|
-
const headerResults = [...headerRow.querySelectorAll("th")].map(
|
|
19
|
+
const headerResults = [...headerRow.querySelectorAll("th, td")].map(
|
|
19
20
|
(headerCell) => next(headerCell)
|
|
20
|
-
), rows = []
|
|
21
|
-
for (const row of
|
|
21
|
+
), rows = [];
|
|
22
|
+
for (const row of bodyRows) {
|
|
22
23
|
const cells = row.querySelectorAll("td");
|
|
23
24
|
let cellIndex = 0;
|
|
24
25
|
for (const cell of cells) {
|
package/lib/rules/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../src/rules/flatten-tables.ts"],"sourcesContent":["import {\n isTextBlock,\n type PortableTextObject,\n type PortableTextSpan,\n type Schema,\n} from '@portabletext/schema'\nimport {flattenNestedBlocks} from '../HtmlDeserializer/flatten-nested-blocks'\nimport {isElement, tagName} from '../HtmlDeserializer/helpers'\nimport type {\n ArbitraryTypedObject,\n DeserializerRule,\n TypedObject,\n} from '../types'\n\n/**\n * An opinionated `DeserializerRule` that flattens tables in a way that repeats\n * the header row for each cell in the row.\n *\n * @example\n * ```html\n * <table>\n * <thead>\n * <tr>\n * <th>Header 1</th>\n * <th>Header 2</th>\n * </tr>\n * </thead>\n * <tbody>\n * <tr>\n * <td>Cell 1</td>\n * <td>Cell 2</td>\n * </tr>\n * </tbody>\n * </table>\n * ```\n * Turns into\n * ```json\n * [\n * {\n * _type: 'block',\n * children: [\n * {\n * _type: 'text',\n * text: 'Header 1'\n * },\n * {\n * _type: 'text',\n * text: 'Cell 1'\n * }\n * ]\n * },\n * {\n * _type: 'block',\n * children: [\n * {\n * _type: 'text',\n * text: 'Header 2'\n * },\n * {\n * _type: 'text',\n * text: 'Cell 2'\n * }\n * ]\n * }\n * ]\n * ```\n *\n * Use the `separator` option to control if a child element should separate\n * headers and cells.\n *\n * @beta\n */\nexport function createFlattenTableRule({\n schema,\n separator,\n}: {\n schema: Schema\n separator?: () =>\n | (Omit<PortableTextSpan, '_key'> & {_key?: string})\n | (Omit<PortableTextObject, '_key'> & {_key?: string})\n | undefined\n}): DeserializerRule {\n return {\n deserialize: (node, next) => {\n if (!isElement(node) || tagName(node) !== 'table') {\n return undefined\n }\n\n const
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/rules/flatten-tables.ts"],"sourcesContent":["import {\n isTextBlock,\n type PortableTextObject,\n type PortableTextSpan,\n type Schema,\n} from '@portabletext/schema'\nimport {flattenNestedBlocks} from '../HtmlDeserializer/flatten-nested-blocks'\nimport {isElement, tagName} from '../HtmlDeserializer/helpers'\nimport type {\n ArbitraryTypedObject,\n DeserializerRule,\n TypedObject,\n} from '../types'\n\n/**\n * An opinionated `DeserializerRule` that flattens tables in a way that repeats\n * the header row for each cell in the row.\n *\n * @example\n * ```html\n * <table>\n * <thead>\n * <tr>\n * <th>Header 1</th>\n * <th>Header 2</th>\n * </tr>\n * </thead>\n * <tbody>\n * <tr>\n * <td>Cell 1</td>\n * <td>Cell 2</td>\n * </tr>\n * </tbody>\n * </table>\n * ```\n * Turns into\n * ```json\n * [\n * {\n * _type: 'block',\n * children: [\n * {\n * _type: 'text',\n * text: 'Header 1'\n * },\n * {\n * _type: 'text',\n * text: 'Cell 1'\n * }\n * ]\n * },\n * {\n * _type: 'block',\n * children: [\n * {\n * _type: 'text',\n * text: 'Header 2'\n * },\n * {\n * _type: 'text',\n * text: 'Cell 2'\n * }\n * ]\n * }\n * ]\n * ```\n *\n * Use the `separator` option to control if a child element should separate\n * headers and cells.\n *\n * @beta\n */\nexport function createFlattenTableRule({\n schema,\n separator,\n}: {\n schema: Schema\n separator?: () =>\n | (Omit<PortableTextSpan, '_key'> & {_key?: string})\n | (Omit<PortableTextObject, '_key'> & {_key?: string})\n | undefined\n}): DeserializerRule {\n return {\n deserialize: (node, next) => {\n if (!isElement(node) || tagName(node) !== 'table') {\n return undefined\n }\n\n const columnCounts = [...node.querySelectorAll('tr')].map((row) => {\n const cells = row.querySelectorAll('td, th')\n return cells.length\n })\n\n const firstColumnCount = columnCounts[0]\n\n if (\n !firstColumnCount ||\n !columnCounts.every((count) => count === firstColumnCount)\n ) {\n // If the column counts are not all the same, we return undefined.\n return undefined\n }\n\n const thead = node.querySelector('thead')\n const headerRow = thead?.querySelector('tr')\n const tbody = node.querySelector('tbody')\n let bodyRows = tbody ? [...tbody.querySelectorAll('tr')] : []\n\n if (!headerRow || !bodyRows) {\n return undefined\n }\n\n const headerCells = headerRow.querySelectorAll('th, td')\n const headerResults = [...headerCells].map((headerCell) =>\n next(headerCell),\n )\n\n // Process tbody rows and combine with headers\n const rows: TypedObject[] = []\n\n for (const row of bodyRows) {\n const cells = row.querySelectorAll('td')\n\n let cellIndex = 0\n for (const cell of cells) {\n const result = next(cell)\n\n if (!result) {\n cellIndex++\n continue\n }\n\n const headerResult = headerResults[cellIndex]\n\n if (!headerResult) {\n // If we can't find a corresponding header, then we just push\n // the deserialized cell as is.\n if (Array.isArray(result)) {\n rows.push(...result)\n } else {\n rows.push(result)\n }\n cellIndex++\n continue\n }\n\n const flattenedHeaderResult = flattenNestedBlocks(\n {schema},\n (Array.isArray(headerResult)\n ? headerResult\n : [headerResult]) as Array<ArbitraryTypedObject>,\n )\n const firstFlattenedHeaderResult = flattenedHeaderResult[0]\n const flattenedResult = flattenNestedBlocks(\n {schema},\n (Array.isArray(result)\n ? result\n : [result]) as Array<ArbitraryTypedObject>,\n )\n const firstFlattenedResult = flattenedResult[0]\n\n if (\n flattenedHeaderResult.length === 1 &&\n isTextBlock({schema}, firstFlattenedHeaderResult) &&\n flattenedResult.length === 1 &&\n isTextBlock({schema}, firstFlattenedResult)\n ) {\n const separatorChild = separator?.()\n // If the header result and the cell result are text blocks then\n // we merge them together.\n const mergedTextBlock = {\n ...firstFlattenedHeaderResult,\n children: [\n ...firstFlattenedHeaderResult.children,\n ...(separatorChild ? [separatorChild] : []),\n ...firstFlattenedResult.children,\n ],\n markDefs: [\n ...(firstFlattenedHeaderResult.markDefs ?? []),\n ...(firstFlattenedResult.markDefs ?? []),\n ],\n }\n\n rows.push(mergedTextBlock)\n cellIndex++\n continue\n }\n\n // Otherwise, we push the header result and the cell result as is.\n if (Array.isArray(headerResult)) {\n rows.push(...headerResult)\n } else {\n rows.push(headerResult)\n }\n\n if (Array.isArray(result)) {\n rows.push(...result)\n } else {\n rows.push(result)\n }\n\n cellIndex++\n }\n }\n\n // Return the processed rows as individual text blocks\n return rows\n },\n }\n}\n"],"names":["schema","isElement","tagName","flattenNestedBlocks","isTextBlock"],"mappings":";;;AAwEO,SAAS,uBAAuB;AAAA,EAAA,QACrCA;AAAAA,EACA;AACF,GAMqB;AACnB,SAAO;AAAA,IACL,aAAa,CAAC,MAAM,SAAS;AAC3B,UAAI,CAACC,QAAAA,UAAU,IAAI,KAAKC,QAAAA,QAAQ,IAAI,MAAM;AACxC;AAGF,YAAM,eAAe,CAAC,GAAG,KAAK,iBAAiB,IAAI,CAAC,EAAE,IAAI,CAAC,QAC3C,IAAI,iBAAiB,QAAQ,EAC9B,MACd,GAEK,mBAAmB,aAAa,CAAC;AAEvC,UACE,CAAC,oBACD,CAAC,aAAa,MAAM,CAAC,UAAU,UAAU,gBAAgB;AAGzD;AAIF,YAAM,YADQ,KAAK,cAAc,OAAO,GACf,cAAc,IAAI,GACrC,QAAQ,KAAK,cAAc,OAAO;AACxC,UAAI,WAAW,QAAQ,CAAC,GAAG,MAAM,iBAAiB,IAAI,CAAC,IAAI,CAAA;AAE3D,UAAI,CAAC,aAAa,CAAC;AACjB;AAIF,YAAM,gBAAgB,CAAC,GADH,UAAU,iBAAiB,QAAQ,CAClB,EAAE;AAAA,QAAI,CAAC,eAC1C,KAAK,UAAU;AAAA,MAAA,GAIX,OAAsB,CAAA;AAE5B,iBAAW,OAAO,UAAU;AAC1B,cAAM,QAAQ,IAAI,iBAAiB,IAAI;AAEvC,YAAI,YAAY;AAChB,mBAAW,QAAQ,OAAO;AACxB,gBAAM,SAAS,KAAK,IAAI;AAExB,cAAI,CAAC,QAAQ;AACX;AACA;AAAA,UACF;AAEA,gBAAM,eAAe,cAAc,SAAS;AAE5C,cAAI,CAAC,cAAc;AAGb,kBAAM,QAAQ,MAAM,IACtB,KAAK,KAAK,GAAG,MAAM,IAEnB,KAAK,KAAK,MAAM,GAElB;AACA;AAAA,UACF;AAEA,gBAAM,wBAAwBC,QAAAA;AAAAA,YAC5B,EAAA,QAACH,SAAA;AAAA,YACA,MAAM,QAAQ,YAAY,IACvB,eACA,CAAC,YAAY;AAAA,UAAA,GAEb,6BAA6B,sBAAsB,CAAC,GACpD,kBAAkBG,QAAAA;AAAAA,YACtB,EAAA,QAACH,SAAA;AAAA,YACA,MAAM,QAAQ,MAAM,IACjB,SACA,CAAC,MAAM;AAAA,UAAA,GAEP,uBAAuB,gBAAgB,CAAC;AAE9C,cACE,sBAAsB,WAAW,KACjCI,OAAAA,YAAY,EAAA,QAACJ,YAAS,0BAA0B,KAChD,gBAAgB,WAAW,KAC3BI,OAAAA,YAAY,EAAA,QAACJ,SAAA,GAAS,oBAAoB,GAC1C;AACA,kBAAM,iBAAiB,YAAA,GAGjB,kBAAkB;AAAA,cACtB,GAAG;AAAA,cACH,UAAU;AAAA,gBACR,GAAG,2BAA2B;AAAA,gBAC9B,GAAI,iBAAiB,CAAC,cAAc,IAAI,CAAA;AAAA,gBACxC,GAAG,qBAAqB;AAAA,cAAA;AAAA,cAE1B,UAAU;AAAA,gBACR,GAAI,2BAA2B,YAAY,CAAA;AAAA,gBAC3C,GAAI,qBAAqB,YAAY,CAAA;AAAA,cAAC;AAAA,YACxC;AAGF,iBAAK,KAAK,eAAe,GACzB;AACA;AAAA,UACF;AAGI,gBAAM,QAAQ,YAAY,IAC5B,KAAK,KAAK,GAAG,YAAY,IAEzB,KAAK,KAAK,YAAY,GAGpB,MAAM,QAAQ,MAAM,IACtB,KAAK,KAAK,GAAG,MAAM,IAEnB,KAAK,KAAK,MAAM,GAGlB;AAAA,QACF;AAAA,MACF;AAGA,aAAO;AAAA,IACT;AAAA,EAAA;AAEJ;;"}
|
package/lib/rules/index.js
CHANGED
|
@@ -8,16 +8,17 @@ function createFlattenTableRule({
|
|
|
8
8
|
deserialize: (node, next) => {
|
|
9
9
|
if (!isElement(node) || tagName(node) !== "table")
|
|
10
10
|
return;
|
|
11
|
-
const
|
|
12
|
-
if (!
|
|
11
|
+
const columnCounts = [...node.querySelectorAll("tr")].map((row) => row.querySelectorAll("td, th").length), firstColumnCount = columnCounts[0];
|
|
12
|
+
if (!firstColumnCount || !columnCounts.every((count) => count === firstColumnCount))
|
|
13
13
|
return;
|
|
14
|
-
const headerRow =
|
|
15
|
-
|
|
14
|
+
const headerRow = node.querySelector("thead")?.querySelector("tr"), tbody = node.querySelector("tbody");
|
|
15
|
+
let bodyRows = tbody ? [...tbody.querySelectorAll("tr")] : [];
|
|
16
|
+
if (!headerRow || !bodyRows)
|
|
16
17
|
return;
|
|
17
|
-
const headerResults = [...headerRow.querySelectorAll("th")].map(
|
|
18
|
+
const headerResults = [...headerRow.querySelectorAll("th, td")].map(
|
|
18
19
|
(headerCell) => next(headerCell)
|
|
19
|
-
), rows = []
|
|
20
|
-
for (const row of
|
|
20
|
+
), rows = [];
|
|
21
|
+
for (const row of bodyRows) {
|
|
21
22
|
const cells = row.querySelectorAll("td");
|
|
22
23
|
let cellIndex = 0;
|
|
23
24
|
for (const cell of cells) {
|
package/lib/rules/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/rules/flatten-tables.ts"],"sourcesContent":["import {\n isTextBlock,\n type PortableTextObject,\n type PortableTextSpan,\n type Schema,\n} from '@portabletext/schema'\nimport {flattenNestedBlocks} from '../HtmlDeserializer/flatten-nested-blocks'\nimport {isElement, tagName} from '../HtmlDeserializer/helpers'\nimport type {\n ArbitraryTypedObject,\n DeserializerRule,\n TypedObject,\n} from '../types'\n\n/**\n * An opinionated `DeserializerRule` that flattens tables in a way that repeats\n * the header row for each cell in the row.\n *\n * @example\n * ```html\n * <table>\n * <thead>\n * <tr>\n * <th>Header 1</th>\n * <th>Header 2</th>\n * </tr>\n * </thead>\n * <tbody>\n * <tr>\n * <td>Cell 1</td>\n * <td>Cell 2</td>\n * </tr>\n * </tbody>\n * </table>\n * ```\n * Turns into\n * ```json\n * [\n * {\n * _type: 'block',\n * children: [\n * {\n * _type: 'text',\n * text: 'Header 1'\n * },\n * {\n * _type: 'text',\n * text: 'Cell 1'\n * }\n * ]\n * },\n * {\n * _type: 'block',\n * children: [\n * {\n * _type: 'text',\n * text: 'Header 2'\n * },\n * {\n * _type: 'text',\n * text: 'Cell 2'\n * }\n * ]\n * }\n * ]\n * ```\n *\n * Use the `separator` option to control if a child element should separate\n * headers and cells.\n *\n * @beta\n */\nexport function createFlattenTableRule({\n schema,\n separator,\n}: {\n schema: Schema\n separator?: () =>\n | (Omit<PortableTextSpan, '_key'> & {_key?: string})\n | (Omit<PortableTextObject, '_key'> & {_key?: string})\n | undefined\n}): DeserializerRule {\n return {\n deserialize: (node, next) => {\n if (!isElement(node) || tagName(node) !== 'table') {\n return undefined\n }\n\n const
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/rules/flatten-tables.ts"],"sourcesContent":["import {\n isTextBlock,\n type PortableTextObject,\n type PortableTextSpan,\n type Schema,\n} from '@portabletext/schema'\nimport {flattenNestedBlocks} from '../HtmlDeserializer/flatten-nested-blocks'\nimport {isElement, tagName} from '../HtmlDeserializer/helpers'\nimport type {\n ArbitraryTypedObject,\n DeserializerRule,\n TypedObject,\n} from '../types'\n\n/**\n * An opinionated `DeserializerRule` that flattens tables in a way that repeats\n * the header row for each cell in the row.\n *\n * @example\n * ```html\n * <table>\n * <thead>\n * <tr>\n * <th>Header 1</th>\n * <th>Header 2</th>\n * </tr>\n * </thead>\n * <tbody>\n * <tr>\n * <td>Cell 1</td>\n * <td>Cell 2</td>\n * </tr>\n * </tbody>\n * </table>\n * ```\n * Turns into\n * ```json\n * [\n * {\n * _type: 'block',\n * children: [\n * {\n * _type: 'text',\n * text: 'Header 1'\n * },\n * {\n * _type: 'text',\n * text: 'Cell 1'\n * }\n * ]\n * },\n * {\n * _type: 'block',\n * children: [\n * {\n * _type: 'text',\n * text: 'Header 2'\n * },\n * {\n * _type: 'text',\n * text: 'Cell 2'\n * }\n * ]\n * }\n * ]\n * ```\n *\n * Use the `separator` option to control if a child element should separate\n * headers and cells.\n *\n * @beta\n */\nexport function createFlattenTableRule({\n schema,\n separator,\n}: {\n schema: Schema\n separator?: () =>\n | (Omit<PortableTextSpan, '_key'> & {_key?: string})\n | (Omit<PortableTextObject, '_key'> & {_key?: string})\n | undefined\n}): DeserializerRule {\n return {\n deserialize: (node, next) => {\n if (!isElement(node) || tagName(node) !== 'table') {\n return undefined\n }\n\n const columnCounts = [...node.querySelectorAll('tr')].map((row) => {\n const cells = row.querySelectorAll('td, th')\n return cells.length\n })\n\n const firstColumnCount = columnCounts[0]\n\n if (\n !firstColumnCount ||\n !columnCounts.every((count) => count === firstColumnCount)\n ) {\n // If the column counts are not all the same, we return undefined.\n return undefined\n }\n\n const thead = node.querySelector('thead')\n const headerRow = thead?.querySelector('tr')\n const tbody = node.querySelector('tbody')\n let bodyRows = tbody ? [...tbody.querySelectorAll('tr')] : []\n\n if (!headerRow || !bodyRows) {\n return undefined\n }\n\n const headerCells = headerRow.querySelectorAll('th, td')\n const headerResults = [...headerCells].map((headerCell) =>\n next(headerCell),\n )\n\n // Process tbody rows and combine with headers\n const rows: TypedObject[] = []\n\n for (const row of bodyRows) {\n const cells = row.querySelectorAll('td')\n\n let cellIndex = 0\n for (const cell of cells) {\n const result = next(cell)\n\n if (!result) {\n cellIndex++\n continue\n }\n\n const headerResult = headerResults[cellIndex]\n\n if (!headerResult) {\n // If we can't find a corresponding header, then we just push\n // the deserialized cell as is.\n if (Array.isArray(result)) {\n rows.push(...result)\n } else {\n rows.push(result)\n }\n cellIndex++\n continue\n }\n\n const flattenedHeaderResult = flattenNestedBlocks(\n {schema},\n (Array.isArray(headerResult)\n ? headerResult\n : [headerResult]) as Array<ArbitraryTypedObject>,\n )\n const firstFlattenedHeaderResult = flattenedHeaderResult[0]\n const flattenedResult = flattenNestedBlocks(\n {schema},\n (Array.isArray(result)\n ? result\n : [result]) as Array<ArbitraryTypedObject>,\n )\n const firstFlattenedResult = flattenedResult[0]\n\n if (\n flattenedHeaderResult.length === 1 &&\n isTextBlock({schema}, firstFlattenedHeaderResult) &&\n flattenedResult.length === 1 &&\n isTextBlock({schema}, firstFlattenedResult)\n ) {\n const separatorChild = separator?.()\n // If the header result and the cell result are text blocks then\n // we merge them together.\n const mergedTextBlock = {\n ...firstFlattenedHeaderResult,\n children: [\n ...firstFlattenedHeaderResult.children,\n ...(separatorChild ? [separatorChild] : []),\n ...firstFlattenedResult.children,\n ],\n markDefs: [\n ...(firstFlattenedHeaderResult.markDefs ?? []),\n ...(firstFlattenedResult.markDefs ?? []),\n ],\n }\n\n rows.push(mergedTextBlock)\n cellIndex++\n continue\n }\n\n // Otherwise, we push the header result and the cell result as is.\n if (Array.isArray(headerResult)) {\n rows.push(...headerResult)\n } else {\n rows.push(headerResult)\n }\n\n if (Array.isArray(result)) {\n rows.push(...result)\n } else {\n rows.push(result)\n }\n\n cellIndex++\n }\n }\n\n // Return the processed rows as individual text blocks\n return rows\n },\n }\n}\n"],"names":[],"mappings":";;AAwEO,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA;AACF,GAMqB;AACnB,SAAO;AAAA,IACL,aAAa,CAAC,MAAM,SAAS;AAC3B,UAAI,CAAC,UAAU,IAAI,KAAK,QAAQ,IAAI,MAAM;AACxC;AAGF,YAAM,eAAe,CAAC,GAAG,KAAK,iBAAiB,IAAI,CAAC,EAAE,IAAI,CAAC,QAC3C,IAAI,iBAAiB,QAAQ,EAC9B,MACd,GAEK,mBAAmB,aAAa,CAAC;AAEvC,UACE,CAAC,oBACD,CAAC,aAAa,MAAM,CAAC,UAAU,UAAU,gBAAgB;AAGzD;AAIF,YAAM,YADQ,KAAK,cAAc,OAAO,GACf,cAAc,IAAI,GACrC,QAAQ,KAAK,cAAc,OAAO;AACxC,UAAI,WAAW,QAAQ,CAAC,GAAG,MAAM,iBAAiB,IAAI,CAAC,IAAI,CAAA;AAE3D,UAAI,CAAC,aAAa,CAAC;AACjB;AAIF,YAAM,gBAAgB,CAAC,GADH,UAAU,iBAAiB,QAAQ,CAClB,EAAE;AAAA,QAAI,CAAC,eAC1C,KAAK,UAAU;AAAA,MAAA,GAIX,OAAsB,CAAA;AAE5B,iBAAW,OAAO,UAAU;AAC1B,cAAM,QAAQ,IAAI,iBAAiB,IAAI;AAEvC,YAAI,YAAY;AAChB,mBAAW,QAAQ,OAAO;AACxB,gBAAM,SAAS,KAAK,IAAI;AAExB,cAAI,CAAC,QAAQ;AACX;AACA;AAAA,UACF;AAEA,gBAAM,eAAe,cAAc,SAAS;AAE5C,cAAI,CAAC,cAAc;AAGb,kBAAM,QAAQ,MAAM,IACtB,KAAK,KAAK,GAAG,MAAM,IAEnB,KAAK,KAAK,MAAM,GAElB;AACA;AAAA,UACF;AAEA,gBAAM,wBAAwB;AAAA,YAC5B,EAAC,OAAA;AAAA,YACA,MAAM,QAAQ,YAAY,IACvB,eACA,CAAC,YAAY;AAAA,UAAA,GAEb,6BAA6B,sBAAsB,CAAC,GACpD,kBAAkB;AAAA,YACtB,EAAC,OAAA;AAAA,YACA,MAAM,QAAQ,MAAM,IACjB,SACA,CAAC,MAAM;AAAA,UAAA,GAEP,uBAAuB,gBAAgB,CAAC;AAE9C,cACE,sBAAsB,WAAW,KACjC,YAAY,EAAC,UAAS,0BAA0B,KAChD,gBAAgB,WAAW,KAC3B,YAAY,EAAC,OAAA,GAAS,oBAAoB,GAC1C;AACA,kBAAM,iBAAiB,YAAA,GAGjB,kBAAkB;AAAA,cACtB,GAAG;AAAA,cACH,UAAU;AAAA,gBACR,GAAG,2BAA2B;AAAA,gBAC9B,GAAI,iBAAiB,CAAC,cAAc,IAAI,CAAA;AAAA,gBACxC,GAAG,qBAAqB;AAAA,cAAA;AAAA,cAE1B,UAAU;AAAA,gBACR,GAAI,2BAA2B,YAAY,CAAA;AAAA,gBAC3C,GAAI,qBAAqB,YAAY,CAAA;AAAA,cAAC;AAAA,YACxC;AAGF,iBAAK,KAAK,eAAe,GACzB;AACA;AAAA,UACF;AAGI,gBAAM,QAAQ,YAAY,IAC5B,KAAK,KAAK,GAAG,YAAY,IAEzB,KAAK,KAAK,YAAY,GAGpB,MAAM,QAAQ,MAAM,IACtB,KAAK,KAAK,GAAG,MAAM,IAEnB,KAAK,KAAK,MAAM,GAGlB;AAAA,QACF;AAAA,MACF;AAGA,aAAO;AAAA,IACT;AAAA,EAAA;AAEJ;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portabletext/block-tools",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.2",
|
|
4
4
|
"description": "Can format HTML, Slate JSON or Sanity block array into any other format.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"portable-text",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"@portabletext/schema": "^1.2.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@sanity/pkg-utils": "^8.1.
|
|
51
|
+
"@sanity/pkg-utils": "^8.1.4",
|
|
52
52
|
"@sanity/schema": "^4.6.0",
|
|
53
53
|
"@sanity/types": "^4.6.0",
|
|
54
54
|
"@types/jsdom": "^20.0.0",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"@vercel/stega": "0.1.2",
|
|
58
58
|
"@vitest/coverage-v8": "^3.2.4",
|
|
59
59
|
"jsdom": "^26.0.0",
|
|
60
|
-
"typescript": "
|
|
60
|
+
"typescript": "5.9.2",
|
|
61
61
|
"vitest": "^3.2.4",
|
|
62
62
|
"@portabletext/test": "^0.0.0"
|
|
63
63
|
},
|
|
@@ -125,6 +125,195 @@ describe(createFlattenTableRule.name, () => {
|
|
|
125
125
|
])
|
|
126
126
|
})
|
|
127
127
|
|
|
128
|
+
test('ordinary table without thead and tbody', () => {
|
|
129
|
+
/**
|
|
130
|
+
* | Year | Sales | Expenses | Profit |
|
|
131
|
+
* | 2022 | \$8,000 | \$5,000 | \$3,000 |
|
|
132
|
+
* | 2023 | \$10,000 | \$6,500 | \$3,500 |
|
|
133
|
+
* | 2024 | \$15,000 | \$9,000 | \$6,000 |
|
|
134
|
+
*/
|
|
135
|
+
const html = [
|
|
136
|
+
'<table>',
|
|
137
|
+
'<tr>',
|
|
138
|
+
'<td>Year</td>',
|
|
139
|
+
'<td>Sales</td>',
|
|
140
|
+
'<td>Expenses</td>',
|
|
141
|
+
'<td>Profit</td>',
|
|
142
|
+
'</tr>',
|
|
143
|
+
'<tr>',
|
|
144
|
+
'<td>2022</td>',
|
|
145
|
+
'<td>$8,000</td>',
|
|
146
|
+
'<td>$5,000</td>',
|
|
147
|
+
'<td>$3,000</td>',
|
|
148
|
+
'</tr>',
|
|
149
|
+
'<tr>',
|
|
150
|
+
'<td>2023</td>',
|
|
151
|
+
'<td>$10,000</td>',
|
|
152
|
+
'<td>$6,500</td>',
|
|
153
|
+
'<td>$3,500</td>',
|
|
154
|
+
'</tr>',
|
|
155
|
+
'<tr>',
|
|
156
|
+
'<td>2024</td>',
|
|
157
|
+
'<td>$15,000</td>',
|
|
158
|
+
'<td>$9,000</td>',
|
|
159
|
+
'<td>$6,000</td>',
|
|
160
|
+
'</tr>',
|
|
161
|
+
'</table>',
|
|
162
|
+
].join('')
|
|
163
|
+
|
|
164
|
+
expect(
|
|
165
|
+
getTersePt({
|
|
166
|
+
schema,
|
|
167
|
+
value: transform(html, {
|
|
168
|
+
rules: [flattenTableRule],
|
|
169
|
+
}),
|
|
170
|
+
}),
|
|
171
|
+
).toEqual([
|
|
172
|
+
'Year',
|
|
173
|
+
'Sales',
|
|
174
|
+
'Expenses',
|
|
175
|
+
'Profit',
|
|
176
|
+
'2022',
|
|
177
|
+
'$8,000',
|
|
178
|
+
'$5,000',
|
|
179
|
+
'$3,000',
|
|
180
|
+
'2023',
|
|
181
|
+
'$10,000',
|
|
182
|
+
'$6,500',
|
|
183
|
+
'$3,500',
|
|
184
|
+
'2024',
|
|
185
|
+
'$15,000',
|
|
186
|
+
'$9,000',
|
|
187
|
+
'$6,000',
|
|
188
|
+
])
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
test('ordinary table without thead', () => {
|
|
192
|
+
/**
|
|
193
|
+
* | Year | Sales | Expenses | Profit |
|
|
194
|
+
* | 2022 | \$8,000 | \$5,000 | \$3,000 |
|
|
195
|
+
* | 2023 | \$10,000 | \$6,500 | \$3,500 |
|
|
196
|
+
* | 2024 | \$15,000 | \$9,000 | \$6,000 |
|
|
197
|
+
*/
|
|
198
|
+
const html = [
|
|
199
|
+
'<table>',
|
|
200
|
+
'<tbody>',
|
|
201
|
+
'<tr>',
|
|
202
|
+
'<td>Year</td>',
|
|
203
|
+
'<td>Sales</td>',
|
|
204
|
+
'<td>Expenses</td>',
|
|
205
|
+
'<td>Profit</td>',
|
|
206
|
+
'</tr>',
|
|
207
|
+
'<tr>',
|
|
208
|
+
'<td>2022</td>',
|
|
209
|
+
'<td>$8,000</td>',
|
|
210
|
+
'<td>$5,000</td>',
|
|
211
|
+
'<td>$3,000</td>',
|
|
212
|
+
'</tr>',
|
|
213
|
+
'<tr>',
|
|
214
|
+
'<td>2023</td>',
|
|
215
|
+
'<td>$10,000</td>',
|
|
216
|
+
'<td>$6,500</td>',
|
|
217
|
+
'<td>$3,500</td>',
|
|
218
|
+
'</tr>',
|
|
219
|
+
'<tr>',
|
|
220
|
+
'<td>2024</td>',
|
|
221
|
+
'<td>$15,000</td>',
|
|
222
|
+
'<td>$9,000</td>',
|
|
223
|
+
'<td>$6,000</td>',
|
|
224
|
+
'</tr>',
|
|
225
|
+
'</tbody>',
|
|
226
|
+
'</table>',
|
|
227
|
+
].join('')
|
|
228
|
+
|
|
229
|
+
expect(
|
|
230
|
+
getTersePt({
|
|
231
|
+
schema,
|
|
232
|
+
value: transform(html, {
|
|
233
|
+
rules: [flattenTableRule],
|
|
234
|
+
}),
|
|
235
|
+
}),
|
|
236
|
+
).toEqual([
|
|
237
|
+
'Year',
|
|
238
|
+
'Sales',
|
|
239
|
+
'Expenses',
|
|
240
|
+
'Profit',
|
|
241
|
+
'2022',
|
|
242
|
+
'$8,000',
|
|
243
|
+
'$5,000',
|
|
244
|
+
'$3,000',
|
|
245
|
+
'2023',
|
|
246
|
+
'$10,000',
|
|
247
|
+
'$6,500',
|
|
248
|
+
'$3,500',
|
|
249
|
+
'2024',
|
|
250
|
+
'$15,000',
|
|
251
|
+
'$9,000',
|
|
252
|
+
'$6,000',
|
|
253
|
+
])
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
test('ordinary table without tbody', () => {
|
|
257
|
+
/**
|
|
258
|
+
* | Year | Sales | Expenses | Profit |
|
|
259
|
+
* | 2022 | \$8,000 | \$5,000 | \$3,000 |
|
|
260
|
+
* | 2023 | \$10,000 | \$6,500 | \$3,500 |
|
|
261
|
+
* | 2024 | \$15,000 | \$9,000 | \$6,000 |
|
|
262
|
+
*/
|
|
263
|
+
const html = [
|
|
264
|
+
'<table>',
|
|
265
|
+
'<thead>',
|
|
266
|
+
'<tr>',
|
|
267
|
+
'<td>Year</td>',
|
|
268
|
+
'<td>Sales</td>',
|
|
269
|
+
'<td>Expenses</td>',
|
|
270
|
+
'<td>Profit</td>',
|
|
271
|
+
'</tr>',
|
|
272
|
+
'</thead>',
|
|
273
|
+
'<tr>',
|
|
274
|
+
'<td>2022</td>',
|
|
275
|
+
'<td>$8,000</td>',
|
|
276
|
+
'<td>$5,000</td>',
|
|
277
|
+
'<td>$3,000</td>',
|
|
278
|
+
'</tr>',
|
|
279
|
+
'<tr>',
|
|
280
|
+
'<td>2023</td>',
|
|
281
|
+
'<td>$10,000</td>',
|
|
282
|
+
'<td>$6,500</td>',
|
|
283
|
+
'<td>$3,500</td>',
|
|
284
|
+
'</tr>',
|
|
285
|
+
'<tr>',
|
|
286
|
+
'<td>2024</td>',
|
|
287
|
+
'<td>$15,000</td>',
|
|
288
|
+
'<td>$9,000</td>',
|
|
289
|
+
'<td>$6,000</td>',
|
|
290
|
+
'</tr>',
|
|
291
|
+
'</table>',
|
|
292
|
+
].join('')
|
|
293
|
+
|
|
294
|
+
expect(
|
|
295
|
+
getTersePt({
|
|
296
|
+
schema,
|
|
297
|
+
value: transform(html, {
|
|
298
|
+
rules: [flattenTableRule],
|
|
299
|
+
}),
|
|
300
|
+
}),
|
|
301
|
+
).toEqual([
|
|
302
|
+
'Year, ,2022',
|
|
303
|
+
'Sales, ,$8,000',
|
|
304
|
+
'Expenses, ,$5,000',
|
|
305
|
+
'Profit, ,$3,000',
|
|
306
|
+
'Year, ,2023',
|
|
307
|
+
'Sales, ,$10,000',
|
|
308
|
+
'Expenses, ,$6,500',
|
|
309
|
+
'Profit, ,$3,500',
|
|
310
|
+
'Year, ,2024',
|
|
311
|
+
'Sales, ,$15,000',
|
|
312
|
+
'Expenses, ,$9,000',
|
|
313
|
+
'Profit, ,$6,000',
|
|
314
|
+
])
|
|
315
|
+
})
|
|
316
|
+
|
|
128
317
|
describe('table with images', () => {
|
|
129
318
|
/**
|
|
130
319
|
* | Name | Photo |
|
|
@@ -86,31 +86,39 @@ export function createFlattenTableRule({
|
|
|
86
86
|
return undefined
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
89
|
+
const columnCounts = [...node.querySelectorAll('tr')].map((row) => {
|
|
90
|
+
const cells = row.querySelectorAll('td, th')
|
|
91
|
+
return cells.length
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const firstColumnCount = columnCounts[0]
|
|
95
|
+
|
|
96
|
+
if (
|
|
97
|
+
!firstColumnCount ||
|
|
98
|
+
!columnCounts.every((count) => count === firstColumnCount)
|
|
99
|
+
) {
|
|
100
|
+
// If the column counts are not all the same, we return undefined.
|
|
94
101
|
return undefined
|
|
95
102
|
}
|
|
96
103
|
|
|
97
|
-
|
|
98
|
-
const headerRow = thead
|
|
104
|
+
const thead = node.querySelector('thead')
|
|
105
|
+
const headerRow = thead?.querySelector('tr')
|
|
106
|
+
const tbody = node.querySelector('tbody')
|
|
107
|
+
let bodyRows = tbody ? [...tbody.querySelectorAll('tr')] : []
|
|
99
108
|
|
|
100
|
-
if (!headerRow) {
|
|
109
|
+
if (!headerRow || !bodyRows) {
|
|
101
110
|
return undefined
|
|
102
111
|
}
|
|
103
112
|
|
|
104
|
-
const headerCells = headerRow.querySelectorAll('th')
|
|
113
|
+
const headerCells = headerRow.querySelectorAll('th, td')
|
|
105
114
|
const headerResults = [...headerCells].map((headerCell) =>
|
|
106
115
|
next(headerCell),
|
|
107
116
|
)
|
|
108
117
|
|
|
109
118
|
// Process tbody rows and combine with headers
|
|
110
119
|
const rows: TypedObject[] = []
|
|
111
|
-
const rowElements = tbody.querySelectorAll('tr')
|
|
112
120
|
|
|
113
|
-
for (const row of
|
|
121
|
+
for (const row of bodyRows) {
|
|
114
122
|
const cells = row.querySelectorAll('td')
|
|
115
123
|
|
|
116
124
|
let cellIndex = 0
|