@teleporthq/teleport-plugin-next-data-source 0.43.59 → 0.43.61
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/__tests__/_helpers/pagination-widget-fixture.ts +230 -0
- package/__tests__/ecommerce-product-discounts.test.ts +196 -0
- package/__tests__/infinite-scroll.test.ts +143 -0
- package/__tests__/numbered-pagination.test.ts +113 -0
- package/__tests__/page-reset-on-filter-sort.test.ts +182 -0
- package/__tests__/page-url-sync.test.ts +208 -0
- package/__tests__/product-price-sort.ts +313 -0
- package/__tests__/search-url-sync.test.ts +7 -3
- package/dist/cjs/array-mapper-pagination.d.ts +9 -0
- package/dist/cjs/array-mapper-pagination.d.ts.map +1 -1
- package/dist/cjs/array-mapper-pagination.js.map +1 -1
- package/dist/cjs/cache/ast.d.ts.map +1 -1
- package/dist/cjs/cache/ast.js +2 -2
- package/dist/cjs/cache/ast.js.map +1 -1
- package/dist/cjs/count-effect.d.ts +42 -0
- package/dist/cjs/count-effect.d.ts.map +1 -0
- package/dist/cjs/count-effect.js +81 -0
- package/dist/cjs/count-effect.js.map +1 -0
- package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
- package/dist/cjs/fetchers/postgresql.js +2 -1
- package/dist/cjs/fetchers/postgresql.js.map +1 -1
- package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
- package/dist/cjs/fetchers/teleport.js +3 -2
- package/dist/cjs/fetchers/teleport.js.map +1 -1
- package/dist/cjs/infinite-scroll.d.ts +65 -0
- package/dist/cjs/infinite-scroll.d.ts.map +1 -0
- package/dist/cjs/infinite-scroll.js +190 -0
- package/dist/cjs/infinite-scroll.js.map +1 -0
- package/dist/cjs/numbered-pagination.d.ts +42 -0
- package/dist/cjs/numbered-pagination.d.ts.map +1 -0
- package/dist/cjs/numbered-pagination.js +167 -0
- package/dist/cjs/numbered-pagination.js.map +1 -0
- package/dist/cjs/page-reset.d.ts +42 -0
- package/dist/cjs/page-reset.d.ts.map +1 -0
- package/dist/cjs/page-reset.js +116 -0
- package/dist/cjs/page-reset.js.map +1 -0
- package/dist/cjs/page-url-sync.d.ts +42 -0
- package/dist/cjs/page-url-sync.d.ts.map +1 -0
- package/dist/cjs/page-url-sync.js +180 -0
- package/dist/cjs/page-url-sync.js.map +1 -0
- package/dist/cjs/pagination-controls.d.ts +55 -0
- package/dist/cjs/pagination-controls.d.ts.map +1 -0
- package/dist/cjs/pagination-controls.js +159 -0
- package/dist/cjs/pagination-controls.js.map +1 -0
- package/dist/cjs/pagination-plugin.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.js +508 -164
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/product-price-sort.d.ts +116 -0
- package/dist/cjs/product-price-sort.d.ts.map +1 -0
- package/dist/cjs/product-price-sort.js +169 -0
- package/dist/cjs/product-price-sort.js.map +1 -0
- package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -1
- package/dist/cjs/transformations/ecommerce-product.js +1 -1
- package/dist/cjs/transformations/ecommerce-product.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils.d.ts.map +1 -1
- package/dist/cjs/utils.js +5 -7
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/array-mapper-pagination.d.ts +9 -0
- package/dist/esm/array-mapper-pagination.d.ts.map +1 -1
- package/dist/esm/array-mapper-pagination.js.map +1 -1
- package/dist/esm/cache/ast.d.ts.map +1 -1
- package/dist/esm/cache/ast.js +2 -2
- package/dist/esm/cache/ast.js.map +1 -1
- package/dist/esm/count-effect.d.ts +42 -0
- package/dist/esm/count-effect.d.ts.map +1 -0
- package/dist/esm/count-effect.js +53 -0
- package/dist/esm/count-effect.js.map +1 -0
- package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
- package/dist/esm/fetchers/postgresql.js +2 -1
- package/dist/esm/fetchers/postgresql.js.map +1 -1
- package/dist/esm/fetchers/teleport.d.ts.map +1 -1
- package/dist/esm/fetchers/teleport.js +3 -2
- package/dist/esm/fetchers/teleport.js.map +1 -1
- package/dist/esm/infinite-scroll.d.ts +65 -0
- package/dist/esm/infinite-scroll.d.ts.map +1 -0
- package/dist/esm/infinite-scroll.js +159 -0
- package/dist/esm/infinite-scroll.js.map +1 -0
- package/dist/esm/numbered-pagination.d.ts +42 -0
- package/dist/esm/numbered-pagination.d.ts.map +1 -0
- package/dist/esm/numbered-pagination.js +138 -0
- package/dist/esm/numbered-pagination.js.map +1 -0
- package/dist/esm/page-reset.d.ts +42 -0
- package/dist/esm/page-reset.d.ts.map +1 -0
- package/dist/esm/page-reset.js +87 -0
- package/dist/esm/page-reset.js.map +1 -0
- package/dist/esm/page-url-sync.d.ts +42 -0
- package/dist/esm/page-url-sync.d.ts.map +1 -0
- package/dist/esm/page-url-sync.js +152 -0
- package/dist/esm/page-url-sync.js.map +1 -0
- package/dist/esm/pagination-controls.d.ts +55 -0
- package/dist/esm/pagination-controls.d.ts.map +1 -0
- package/dist/esm/pagination-controls.js +126 -0
- package/dist/esm/pagination-controls.js.map +1 -0
- package/dist/esm/pagination-plugin.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.js +510 -166
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/product-price-sort.d.ts +116 -0
- package/dist/esm/product-price-sort.d.ts.map +1 -0
- package/dist/esm/product-price-sort.js +160 -0
- package/dist/esm/product-price-sort.js.map +1 -0
- package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -1
- package/dist/esm/transformations/ecommerce-product.js +2 -2
- package/dist/esm/transformations/ecommerce-product.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils.d.ts.map +1 -1
- package/dist/esm/utils.js +6 -8
- package/dist/esm/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/array-mapper-pagination.ts +10 -0
- package/src/cache/ast.ts +2 -2
- package/src/count-effect.ts +137 -0
- package/src/fetchers/postgresql.ts +52 -13
- package/src/fetchers/teleport.ts +53 -13
- package/src/infinite-scroll.ts +401 -0
- package/src/numbered-pagination.ts +292 -0
- package/src/page-reset.ts +162 -0
- package/src/page-url-sync.ts +241 -0
- package/src/pagination-controls.ts +176 -0
- package/src/pagination-plugin.ts +758 -351
- package/src/product-price-sort.ts +204 -0
- package/src/transformations/ecommerce-product.ts +33 -7
- package/src/utils.ts +6 -8
- package/dist/cjs/array-mapper-registry.d.ts +0 -85
- package/dist/cjs/array-mapper-registry.d.ts.map +0 -1
- package/dist/cjs/array-mapper-registry.js +0 -296
- package/dist/cjs/array-mapper-registry.js.map +0 -1
- package/dist/cjs/pagination-with-count.d.ts +0 -6
- package/dist/cjs/pagination-with-count.d.ts.map +0 -1
- package/dist/cjs/pagination-with-count.js +0 -63
- package/dist/cjs/pagination-with-count.js.map +0 -1
- package/dist/esm/array-mapper-registry.d.ts +0 -85
- package/dist/esm/array-mapper-registry.d.ts.map +0 -1
- package/dist/esm/array-mapper-registry.js +0 -292
- package/dist/esm/array-mapper-registry.js.map +0 -1
- package/dist/esm/pagination-with-count.d.ts +0 -6
- package/dist/esm/pagination-with-count.d.ts.map +0 -1
- package/dist/esm/pagination-with-count.js +0 -34
- package/dist/esm/pagination-with-count.js.map +0 -1
- package/src/array-mapper-registry.ts +0 -416
- package/src/pagination-with-count.ts +0 -89
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgresql.js","sourceRoot":"","sources":["../../../src/fetchers/postgresql.ts"],"names":[],"mappings":";;;AAAA,kCAKiB;
|
|
1
|
+
{"version":3,"file":"postgresql.js","sourceRoot":"","sources":["../../../src/fetchers/postgresql.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teleport.d.ts","sourceRoot":"","sources":["../../../src/fetchers/teleport.ts"],"names":[],"mappings":"AAMA,OAAO,EAIL,KAAK,gCAAgC,EACtC,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"teleport.d.ts","sourceRoot":"","sources":["../../../src/fetchers/teleport.ts"],"names":[],"mappings":"AAMA,OAAO,EAIL,KAAK,gCAAgC,EACtC,MAAM,oBAAoB,CAAA;AAmC3B,eAAO,MAAM,sBAAsB,WACzB,OAAO,MAAM,EAAE,OAAO,CAAC,KAC9B;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAMpC,CAAA;AAED,eAAO,MAAM,uBAAuB,WAC1B,OAAO,MAAM,EAAE,OAAO,CAAC,aACpB,MAAM,qBACC,gCAAgC,KACjD,MAoZF,CAAA;AAED,eAAO,MAAM,4BAA4B,WAC/B,OAAO,MAAM,EAAE,OAAO,CAAC,aACpB,MAAM,KAChB,MA0FF,CAAA"}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.generateTeleportCountFetcher = exports.generateTeleportFetcher = exports.validateTeleportConfig = void 0;
|
|
4
4
|
var utils_1 = require("../utils");
|
|
5
5
|
var transformations_1 = require("../transformations");
|
|
6
|
+
var product_price_sort_1 = require("../product-price-sort");
|
|
6
7
|
var DEFAULT_ENV_KEYS = {
|
|
7
8
|
host: 'TELEPORT_DB_HOST',
|
|
8
9
|
port: 'TELEPORT_DB_PORT',
|
|
@@ -45,9 +46,9 @@ var generateTeleportFetcher = function (config, tableName, transformOptions) {
|
|
|
45
46
|
? "key: ".concat(resolveEnvReference(dbConfig.sslConfig.key, 'TELEPORT_DB_SSL_KEY'), ",")
|
|
46
47
|
: '', "\n rejectUnauthorized: false\n }")
|
|
47
48
|
: "(process.env.".concat(DEFAULT_ENV_KEYS.ssl, " === 'false' ? false : process.env.").concat(DEFAULT_ENV_KEYS.ssl, " === 'true' ? { rejectUnauthorized: false } : undefined)");
|
|
48
|
-
return "import { Client } from 'pg'\n\nfunction normalizePostgresConnectionString(connectionString) {\n if (!connectionString || typeof connectionString !== 'string') return connectionString;\n if (/^postgresql:\\/(?!\\/)/i.test(connectionString)) {\n return connectionString.replace(/^postgresql:\\//i, 'postgresql://');\n }\n return connectionString;\n}\n\nfunction stripSslQueryParamsFromConnectionString(connectionString) {\n if (!connectionString || typeof connectionString !== 'string') return connectionString;\n try {\n var u = new URL(connectionString.replace(/^postgresql:/i, 'postgres:'));\n u.searchParams.delete('sslmode');\n u.searchParams.delete('ssl');\n u.searchParams.delete('sslrootcert');\n u.searchParams.delete('sslcert');\n u.searchParams.delete('sslkey');\n return u.toString().replace(/^postgres:/i, 'postgresql:');\n } catch (e) {\n return connectionString;\n }\n}\n\nconst getClient = () => {\n var ssl = ".concat(sslCode, ";\n var connStr = process.env.TELEPORT_DB_CONNECTION_STRING;\n if (connStr) {\n connStr = normalizePostgresConnectionString(connStr);\n }\n if (ssl === false && connStr) {\n connStr = stripSslQueryParamsFromConnectionString(connStr);\n }\n if (connStr) {\n return new Client(Object.assign(\n { connectionString: connStr },\n ssl !== undefined ? { ssl: ssl } : {}\n ));\n }\n return new Client(Object.assign(\n {\n host: ").concat(hostRef, ",\n port: parseInt(").concat(portRef, " || '5432', 10),\n user: ").concat(userRef, ",\n password: ").concat(passwordRef, ",\n database: ").concat(databaseRef, ",\n },\n ssl !== undefined ? { ssl: ssl } : {}\n ));\n}\n\n").concat((0, utils_1.generateSafeJSONParseCode)(), "\n\n").concat((0, utils_1.generateSearchEscapeHelpersCode)(), "\n").concat((0, transformations_1.getTransformationCode)(tableName, transformOptions), "\n").concat((0, transformations_1.getTransformWrapperCode)(tableName), "\nconst processFilters = (filters, conditions, queryParams, paramIndex) => {\n if (!filters) return paramIndex\n \n const parsedFilters = safeJSONParse(filters)\n \n if (Array.isArray(parsedFilters)) {\n parsedFilters.forEach((filter) => {\n if (!filter.source || filter.destination === undefined) return\n \n const field = filter.source\n const value = filter.destination\n const operand = filter.operand || '='\n \n if (Array.isArray(value)) {\n if (value.length === 0) return\n if (operand === 'array_overlap') {\n // Row column is a JSON array (e.g. category_ids). Match when it\n // shares any element with the destination set. jsonb ?| (function\n // form) avoids the '?' placeholder-token ambiguity some drivers hit.\n conditions.push(`jsonb_exists_any(NULLIF(${field}, '')::jsonb, $${paramIndex}::text[])`)\n queryParams.push(value.map((entry) => String(entry)))\n paramIndex++\n return\n }\n const placeholders = value.map(() => `$${paramIndex++}`)\n queryParams.push(...value)\n if (operand === '!=') {\n conditions.push(`${field} NOT IN (${placeholders.join(', ')})`)\n } else {\n conditions.push(`${field} IN (${placeholders.join(', ')})`)\n }\n } else {\n if (operand === 'array_overlap') {\n if (value === '' || value === null || value === undefined) return\n // A single comma-joined string (the multi-select Category Filter's\n // ?categoryFilter=a,b,c) expands to multiple ids; one id stays one.\n const overlapValues = String(value).split(',').map((entry) => entry.trim()).filter(Boolean)\n if (overlapValues.length === 0) return\n conditions.push(`jsonb_exists_any(NULLIF(${field}, '')::jsonb, $${paramIndex}::text[])`)\n queryParams.push(overlapValues)\n paramIndex++\n return\n }\n if (value === null) {\n if (operand === '=') {\n conditions.push(`${field} IS NULL`)\n } else if (operand === '!=') {\n conditions.push(`${field} IS NOT NULL`)\n }\n } else {\n const validOps = ['=', '!=', '>', '<', '>=', '<=']\n const sqlOperator = validOps.includes(operand) ? operand : '='\n conditions.push(`${field} ${sqlOperator} $${paramIndex}`)\n queryParams.push(value)\n paramIndex++\n }\n }\n })\n } else {\n Object.entries(parsedFilters).forEach(([key, value]) => {\n if (Array.isArray(value)) {\n const placeholders = value.map(() => `$${paramIndex++}`)\n queryParams.push(...value)\n conditions.push(`${key} IN (${placeholders.join(', ')})`)\n } else {\n conditions.push(`${key} = $${paramIndex}`)\n queryParams.push(value)\n paramIndex++\n }\n })\n }\n \n return paramIndex\n}\n\n").concat((0, utils_1.generateDateFormatterCode)(), "\n\n// Matches DDL / dangerous statements the raw-query branch should refuse.\n// Keep this list conservative \u2014 anything destructive or schema-changing is\n// out of scope for a client-triggered fetch. SELECT and CTEs (WITH ...) are\n// the only shapes consumers legitimately need.\nconst BLOCKED_RAW_QUERY_PATTERNS = [\n /\\bcreate\\s+(?:temporary\\s+|temp\\s+|unlogged\\s+|global\\s+|local\\s+)?table\\b/i,\n /\\bcreate\\s+(?:unique\\s+)?index\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?(?:materialized\\s+)?view\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?trigger\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?(?:aggregate\\s+)?function\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?procedure\\b/i,\n /\\bcreate\\s+(?:database|schema|sequence|extension|type|role|user)\\b/i,\n /\\bdrop\\s+(?:table|view|index|schema|database|sequence|trigger|function|procedure|role|user|extension|type|materialized)\\b/i,\n /\\balter\\s+(?:table|view|index|schema|database|sequence|role|user|system)\\b/i,\n /\\btruncate\\b/i,\n /\\bgrant\\b/i,\n /\\brevoke\\b/i,\n /\\binsert\\b/i,\n /\\bupdate\\b/i,\n /\\bdelete\\b/i,\n /\\bcopy\\b/i,\n /\\bvacuum\\b/i,\n /\\breindex\\b/i,\n /\\bcluster\\b/i,\n]\n\nfunction assertRawQuerySafe(rawQuery) {\n if (typeof rawQuery !== 'string' || rawQuery.length === 0) {\n throw new Error('rawQuery must be a non-empty string')\n }\n // Reject multi-statement payloads \u2014 only single SELECT / WITH statements.\n // A trailing semicolon is tolerated but any content after it fails.\n var trimmed = rawQuery.trim().replace(/;\\s*$/, '')\n if (trimmed.indexOf(';') !== -1) {\n throw new Error('rawQuery must contain exactly one statement')\n }\n for (var i = 0; i < BLOCKED_RAW_QUERY_PATTERNS.length; i++) {\n if (BLOCKED_RAW_QUERY_PATTERNS[i].test(trimmed)) {\n throw new Error('rawQuery contains a blocked statement')\n }\n }\n}\n\nexport default async function handler(req, res) {\n const client = getClient()\n\n try {\n await client.connect()\n ").concat(schema ? "await client.query('SET search_path TO ".concat(schema, "')") : '', "\n\n // If the caller supplied a rawQuery, run it verbatim (after a safety\n // guard). The schema-driven branch below builds `SELECT * FROM ").concat(tableName, "`\n // with optional filters \u2014 it's the default read path and ignores\n // rawQuery. Page-load workflows and other consumers that need a JOIN\n // or a user-scoped filter pass their fully-rendered SQL here via\n // `fetchData({ rawQuery })` and expect it to execute verbatim.\n if (req.query && typeof req.query.rawQuery === 'string' && req.query.rawQuery.length > 0) {\n assertRawQuerySafe(req.query.rawQuery)\n const rawResult = await client.query(req.query.rawQuery)\n const rawRows = Array.isArray(rawResult?.rows) ? rawResult.rows : []\n const rawPlain = rawRows.map((row) =>\n row && typeof row.toJSON === 'function' ? row.toJSON() : row\n )\n const rawSafe = JSON.parse(JSON.stringify(rawPlain, dateReplacer))\n return res.status(200).json({\n success: true,\n data: rawSafe,\n timestamp: Date.now()\n })\n }\n\n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n \n const conditions = []\n const queryParams = []\n let paramIndex = 1\n \n if (query) {\n let columns = []\n \n if (queryColumns) {\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : [])\n } else {\n try {\n const schemaQuery = `\n SELECT column_name \n FROM information_schema.columns \n WHERE table_name = $1\n ").concat(schema ? "AND table_schema = $2" : '', "\n ORDER BY ordinal_position\n `\n const schemaParams = ").concat(schema
|
|
49
|
+
return "import { Client } from 'pg'\n\nfunction normalizePostgresConnectionString(connectionString) {\n if (!connectionString || typeof connectionString !== 'string') return connectionString;\n if (/^postgresql:\\/(?!\\/)/i.test(connectionString)) {\n return connectionString.replace(/^postgresql:\\//i, 'postgresql://');\n }\n return connectionString;\n}\n\nfunction stripSslQueryParamsFromConnectionString(connectionString) {\n if (!connectionString || typeof connectionString !== 'string') return connectionString;\n try {\n var u = new URL(connectionString.replace(/^postgresql:/i, 'postgres:'));\n u.searchParams.delete('sslmode');\n u.searchParams.delete('ssl');\n u.searchParams.delete('sslrootcert');\n u.searchParams.delete('sslcert');\n u.searchParams.delete('sslkey');\n return u.toString().replace(/^postgres:/i, 'postgresql:');\n } catch (e) {\n return connectionString;\n }\n}\n\nconst getClient = () => {\n var ssl = ".concat(sslCode, ";\n var connStr = process.env.TELEPORT_DB_CONNECTION_STRING;\n if (connStr) {\n connStr = normalizePostgresConnectionString(connStr);\n }\n if (ssl === false && connStr) {\n connStr = stripSslQueryParamsFromConnectionString(connStr);\n }\n if (connStr) {\n return new Client(Object.assign(\n { connectionString: connStr },\n ssl !== undefined ? { ssl: ssl } : {}\n ));\n }\n return new Client(Object.assign(\n {\n host: ").concat(hostRef, ",\n port: parseInt(").concat(portRef, " || '5432', 10),\n user: ").concat(userRef, ",\n password: ").concat(passwordRef, ",\n database: ").concat(databaseRef, ",\n },\n ssl !== undefined ? { ssl: ssl } : {}\n ));\n}\n\n").concat((0, utils_1.generateSafeJSONParseCode)(), "\n\n").concat((0, utils_1.generateSearchEscapeHelpersCode)(), "\n").concat((0, transformations_1.getTransformationCode)(tableName, transformOptions), "\n").concat((0, transformations_1.getTransformWrapperCode)(tableName), "\nconst processFilters = (filters, conditions, queryParams, paramIndex) => {\n if (!filters) return paramIndex\n \n const parsedFilters = safeJSONParse(filters)\n \n if (Array.isArray(parsedFilters)) {\n parsedFilters.forEach((filter) => {\n if (!filter.source || filter.destination === undefined) return\n \n const field = filter.source\n const value = filter.destination\n const operand = filter.operand || '='\n \n if (Array.isArray(value)) {\n if (value.length === 0) return\n if (operand === 'array_overlap') {\n // Row column is a JSON array (e.g. category_ids). Match when it\n // shares any element with the destination set. jsonb ?| (function\n // form) avoids the '?' placeholder-token ambiguity some drivers hit.\n conditions.push(`jsonb_exists_any(NULLIF(${field}, '')::jsonb, $${paramIndex}::text[])`)\n queryParams.push(value.map((entry) => String(entry)))\n paramIndex++\n return\n }\n const placeholders = value.map(() => `$${paramIndex++}`)\n queryParams.push(...value)\n if (operand === '!=') {\n conditions.push(`${field} NOT IN (${placeholders.join(', ')})`)\n } else {\n conditions.push(`${field} IN (${placeholders.join(', ')})`)\n }\n } else {\n if (operand === 'array_overlap') {\n if (value === '' || value === null || value === undefined) return\n // A single comma-joined string (the multi-select Category Filter's\n // ?categoryFilter=a,b,c) expands to multiple ids; one id stays one.\n const overlapValues = String(value).split(',').map((entry) => entry.trim()).filter(Boolean)\n if (overlapValues.length === 0) return\n conditions.push(`jsonb_exists_any(NULLIF(${field}, '')::jsonb, $${paramIndex}::text[])`)\n queryParams.push(overlapValues)\n paramIndex++\n return\n }\n if (value === null) {\n if (operand === '=') {\n conditions.push(`${field} IS NULL`)\n } else if (operand === '!=') {\n conditions.push(`${field} IS NOT NULL`)\n }\n } else {\n const validOps = ['=', '!=', '>', '<', '>=', '<=']\n const sqlOperator = validOps.includes(operand) ? operand : '='\n conditions.push(`${field} ${sqlOperator} $${paramIndex}`)\n queryParams.push(value)\n paramIndex++\n }\n }\n })\n } else {\n Object.entries(parsedFilters).forEach(([key, value]) => {\n if (Array.isArray(value)) {\n const placeholders = value.map(() => `$${paramIndex++}`)\n queryParams.push(...value)\n conditions.push(`${key} IN (${placeholders.join(', ')})`)\n } else {\n conditions.push(`${key} = $${paramIndex}`)\n queryParams.push(value)\n paramIndex++\n }\n })\n }\n \n return paramIndex\n}\n\n").concat((0, utils_1.generateDateFormatterCode)(), "\n\n").concat((0, product_price_sort_1.generateSortFieldSqlHelper)(tableName), "\n\n").concat((0, product_price_sort_1.generateSortFallbackFieldHelper)(tableName), "\n\n// Matches DDL / dangerous statements the raw-query branch should refuse.\n// Keep this list conservative \u2014 anything destructive or schema-changing is\n// out of scope for a client-triggered fetch. SELECT and CTEs (WITH ...) are\n// the only shapes consumers legitimately need.\nconst BLOCKED_RAW_QUERY_PATTERNS = [\n /\\bcreate\\s+(?:temporary\\s+|temp\\s+|unlogged\\s+|global\\s+|local\\s+)?table\\b/i,\n /\\bcreate\\s+(?:unique\\s+)?index\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?(?:materialized\\s+)?view\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?trigger\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?(?:aggregate\\s+)?function\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?procedure\\b/i,\n /\\bcreate\\s+(?:database|schema|sequence|extension|type|role|user)\\b/i,\n /\\bdrop\\s+(?:table|view|index|schema|database|sequence|trigger|function|procedure|role|user|extension|type|materialized)\\b/i,\n /\\balter\\s+(?:table|view|index|schema|database|sequence|role|user|system)\\b/i,\n /\\btruncate\\b/i,\n /\\bgrant\\b/i,\n /\\brevoke\\b/i,\n /\\binsert\\b/i,\n /\\bupdate\\b/i,\n /\\bdelete\\b/i,\n /\\bcopy\\b/i,\n /\\bvacuum\\b/i,\n /\\breindex\\b/i,\n /\\bcluster\\b/i,\n]\n\nfunction assertRawQuerySafe(rawQuery) {\n if (typeof rawQuery !== 'string' || rawQuery.length === 0) {\n throw new Error('rawQuery must be a non-empty string')\n }\n // Reject multi-statement payloads \u2014 only single SELECT / WITH statements.\n // A trailing semicolon is tolerated but any content after it fails.\n var trimmed = rawQuery.trim().replace(/;\\s*$/, '')\n if (trimmed.indexOf(';') !== -1) {\n throw new Error('rawQuery must contain exactly one statement')\n }\n for (var i = 0; i < BLOCKED_RAW_QUERY_PATTERNS.length; i++) {\n if (BLOCKED_RAW_QUERY_PATTERNS[i].test(trimmed)) {\n throw new Error('rawQuery contains a blocked statement')\n }\n }\n}\n\nexport default async function handler(req, res) {\n const client = getClient()\n\n try {\n await client.connect()\n ").concat(schema ? "await client.query('SET search_path TO ".concat(schema, "')") : '', "\n\n // If the caller supplied a rawQuery, run it verbatim (after a safety\n // guard). The schema-driven branch below builds `SELECT * FROM ").concat(tableName, "`\n // with optional filters \u2014 it's the default read path and ignores\n // rawQuery. Page-load workflows and other consumers that need a JOIN\n // or a user-scoped filter pass their fully-rendered SQL here via\n // `fetchData({ rawQuery })` and expect it to execute verbatim.\n if (req.query && typeof req.query.rawQuery === 'string' && req.query.rawQuery.length > 0) {\n assertRawQuerySafe(req.query.rawQuery)\n const rawResult = await client.query(req.query.rawQuery)\n const rawRows = Array.isArray(rawResult?.rows) ? rawResult.rows : []\n const rawPlain = rawRows.map((row) =>\n row && typeof row.toJSON === 'function' ? row.toJSON() : row\n )\n const rawSafe = JSON.parse(JSON.stringify(rawPlain, dateReplacer))\n return res.status(200).json({\n success: true,\n data: rawSafe,\n timestamp: Date.now()\n })\n }\n\n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n \n const conditions = []\n const queryParams = []\n let paramIndex = 1\n \n if (query) {\n let columns = []\n \n if (queryColumns) {\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : [])\n } else {\n try {\n const schemaQuery = `\n SELECT column_name \n FROM information_schema.columns \n WHERE table_name = $1\n ").concat(schema ? "AND table_schema = $2" : '', "\n ORDER BY ordinal_position\n `\n const schemaParams = ").concat(schema
|
|
49
50
|
? "[".concat(JSON.stringify(tableName), ", ").concat(JSON.stringify(schema), "]")
|
|
50
|
-
: "[".concat(JSON.stringify(tableName), "]"), "\n \n const schemaResult = await client.query(schemaQuery, schemaParams)\n columns = schemaResult.rows.map(row => row.column_name)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n }\n }\n \n if (columns.length > 0) {\n const pattern = '%' + escapeLikePattern(query) + '%'\n const placeholder = '$' + paramIndex\n paramIndex++\n queryParams.push(pattern)\n const searchConditions = columns.map(\n (col) => '\"' + sanitizeSearchIdentifier(col) + '\"::text ILIKE ' + placeholder + \" ESCAPE '|'\"\n )\n conditions.push('(' + searchConditions.join(' OR ') + ')')\n }\n }\n \n paramIndex = processFilters(filters, conditions, queryParams, paramIndex)\n \n let sql = `SELECT * FROM ").concat(tableName, "`\n \n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`\n }\n \n if (sorts) {\n const parsedSorts = safeJSONParse(sorts)\n if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {\n const
|
|
51
|
+
: "[".concat(JSON.stringify(tableName), "]"), "\n \n const schemaResult = await client.query(schemaQuery, schemaParams)\n columns = schemaResult.rows.map(row => row.column_name)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n }\n }\n \n if (columns.length > 0) {\n const pattern = '%' + escapeLikePattern(query) + '%'\n const placeholder = '$' + paramIndex\n paramIndex++\n queryParams.push(pattern)\n const searchConditions = columns.map(\n (col) => '\"' + sanitizeSearchIdentifier(col) + '\"::text ILIKE ' + placeholder + \" ESCAPE '|'\"\n )\n conditions.push('(' + searchConditions.join(' OR ') + ')')\n }\n }\n \n paramIndex = processFilters(filters, conditions, queryParams, paramIndex)\n \n let sql = `SELECT * FROM ").concat(tableName, "`\n \n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`\n }\n \n // Handle sorts - new array format. Two ORDER BY clauses are built: the one\n // actually used, and a plain-column twin kept only as the fallback below.\n let orderBySql = ''\n let plainOrderBySql = ''\n if (sorts) {\n const parsedSorts = safeJSONParse(sorts)\n if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {\n const valid = parsedSorts.filter((sort) => sort && sort.field)\n const orderOf = (sort) => (sort.order || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'\n const orderClauses = valid.map((sort) => `${sortFieldSql(sort.field)} ${orderOf(sort)}`)\n const plainClauses = valid.map((sort) => `${sortFallbackField(sort.field)} ${orderOf(sort)}`)\n\n if (orderClauses.length > 0) {\n // A deterministic tiebreaker keeps pagination stable: \"sort by\n // discount\" ties every undiscounted product at zero, and equal rows\n // with no defined order can repeat across pages or vanish between them.\n orderBySql = ` ORDER BY ${orderClauses.join(', ')}").concat((0, product_price_sort_1.generateSortTiebreakSql)(tableName), "`\n plainOrderBySql = ` ORDER BY ${plainClauses.join(', ')}").concat((0, product_price_sort_1.generateSortTiebreakSql)(tableName), "`\n }\n }\n } else if (sortBy) {\n orderBySql = ` ORDER BY ${sortBy} ${(sortOrder || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'}`\n plainOrderBySql = orderBySql\n }\n const usedDiscountAwareSort = orderBySql !== plainOrderBySql\n\n const limitValue = limit || perPage\n const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)\n\n let sqlTail = ''\n if (limitValue) {\n sqlTail += ` LIMIT ${limitValue}`\n }\n \n if (offsetValue !== undefined) {\n sqlTail += ` OFFSET ${offsetValue}`\n }\n\n const baseSql = sql\n sql = baseSql + orderBySql + sqlTail\n\n // The discount-aware price ordering is an inline sub-select over a JSON\n // column. It is written to be unraisable, but it runs inside ORDER BY for\n // the whole table - so if a database ever rejects it, fall back to ordering\n // by the stored list price rather than serving an empty products page.\n let result\n try {\n result = await client.query(sql, queryParams)\n } catch (sortError) {\n if (!usedDiscountAwareSort) throw sortError\n console.warn(\n 'Discount-aware price sort failed; falling back to the list price:',\n sortError && sortError.message\n )\n result = await client.query(baseSql + plainOrderBySql + sqlTail, queryParams)\n }\n const rows = Array.isArray(result?.rows) ? result.rows : []\n const plainRows = rows.map((row) =>\n row && typeof row.toJSON === 'function' ? row.toJSON() : row\n )\n const safeData = JSON.parse(JSON.stringify(plainRows, dateReplacer))\n ").concat((0, transformations_1.getTransformExpression)(tableName)
|
|
51
52
|
? "const transformedData = ".concat((0, transformations_1.getTransformExpression)(tableName))
|
|
52
53
|
: '', "\n\n // Stamp each row with a globally-correct 1-based `__rowNumber` (the page\n // offset is applied server-side above), so list index columns keep counting\n // across pages instead of restarting at 1. Renderer-internal field \u2014 it is\n // filtered from ordinary field bindings and only read by the index column.\n const __finalData = ").concat((0, transformations_1.getTransformExpression)(tableName) ? 'transformedData' : 'safeData', "\n const __rowNumberStart = (typeof offsetValue === 'number' && !isNaN(offsetValue)) ? offsetValue : 0\n const __numberedData = Array.isArray(__finalData)\n ? __finalData.map((__row, __i) =>\n __row && typeof __row === 'object' && !Array.isArray(__row)\n ? Object.assign({}, __row, { __rowNumber: __rowNumberStart + __i + 1 })\n : __row)\n : __finalData\n\n return res.status(200).json({\n success: true,\n data: __numberedData,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('Teleport DB fetch error:', error)\n return res.status(500).json({\n success: false,\n error: error.message || 'Failed to fetch data',\n timestamp: Date.now()\n })\n } finally {\n if (client) {\n try {\n await client.end()\n } catch (error) {\n console.error('Error closing database client:', error)\n }\n }\n }\n}\n");
|
|
53
54
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teleport.js","sourceRoot":"","sources":["../../../src/fetchers/teleport.ts"],"names":[],"mappings":";;;AAAA,kCAKiB;AACjB,sDAK2B;
|
|
1
|
+
{"version":3,"file":"teleport.js","sourceRoot":"","sources":["../../../src/fetchers/teleport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as types from '@babel/types';
|
|
2
|
+
/**
|
|
3
|
+
* Infinite scroll: the list APPENDS pages instead of replacing them.
|
|
4
|
+
*
|
|
5
|
+
* The accumulation lives inside the generated `fetchData` closure rather than
|
|
6
|
+
* around the provider. `DataProvider` renders whatever `fetchData` resolves
|
|
7
|
+
* with, so returning "every page fetched so far, in order" is enough to make it
|
|
8
|
+
* append — no change to the runtime component library, and the existing
|
|
9
|
+
* `persistDataDuringLoading` keeps the rows already on screen while the next
|
|
10
|
+
* page is in flight.
|
|
11
|
+
*
|
|
12
|
+
* A `useRef` holds the pages rather than a state, because writing accumulated
|
|
13
|
+
* rows into state from inside `fetchData` would re-render the provider mid-fetch
|
|
14
|
+
* and re-enter it. The ref is keyed by a SIGNATURE of every fetch param except
|
|
15
|
+
* the page, so a filter, sort or search change starts a fresh list instead of
|
|
16
|
+
* interleaving rows from two different queries.
|
|
17
|
+
*/
|
|
18
|
+
export interface InfiniteScrollVars {
|
|
19
|
+
accumRefVar: string;
|
|
20
|
+
hasMoreFlagVar: string;
|
|
21
|
+
setHasMoreFlagVar: string;
|
|
22
|
+
hasMoreVar: string;
|
|
23
|
+
sentinelRefVar: string;
|
|
24
|
+
}
|
|
25
|
+
export declare const getInfiniteScrollVars: (index: number) => InfiniteScrollVars;
|
|
26
|
+
/**
|
|
27
|
+
* The three declarations behind an appending list:
|
|
28
|
+
*
|
|
29
|
+
* const ds_0_accumRef = useRef({ sig: '', pages: {} })
|
|
30
|
+
* const [ds_0_hasMoreFlag, setDs_0_hasMoreFlag] = useState(true)
|
|
31
|
+
* const ds_0_hasMore = ds_0_hasMoreFlag && (ds_0_maxPages === 0 || <page> < ds_0_maxPages)
|
|
32
|
+
*
|
|
33
|
+
* Two independent signals for "is there more", because either can be missing.
|
|
34
|
+
* `hasMoreFlag` comes from the last response being a SHORT page, which is the
|
|
35
|
+
* only end-of-data signal available from a data source that cannot count.
|
|
36
|
+
* `maxPages` comes from the count endpoint and is the one that stops a request
|
|
37
|
+
* being made at all; it is `0` while unknown, hence the explicit escape.
|
|
38
|
+
*/
|
|
39
|
+
export declare const buildInfiniteScrollDeclarations: (vars: InfiniteScrollVars, maxPagesVar: string, currentPageExpr: types.Expression, includeSentinel: boolean) => types.Statement[];
|
|
40
|
+
/**
|
|
41
|
+
* The `.then(...)` tail that accumulates a page and reports whether more exist.
|
|
42
|
+
*
|
|
43
|
+
* Returned as the resolved value of `fetchData`, so `DataProvider` stores the
|
|
44
|
+
* whole accumulated list as its data.
|
|
45
|
+
*/
|
|
46
|
+
export declare const buildAccumulatingResponseHandler: (vars: InfiniteScrollVars, perPage: number) => types.ArrowFunctionExpression;
|
|
47
|
+
/**
|
|
48
|
+
* The IntersectionObserver that loads the next page when the sentinel scrolls
|
|
49
|
+
* into view. Emitted only in auto mode (no Load More button).
|
|
50
|
+
*
|
|
51
|
+
* `rootMargin` starts the fetch a screenful early so the list rarely shows a
|
|
52
|
+
* gap. The observer is rebuilt whenever `hasMore` or `maxPages` change and
|
|
53
|
+
* disconnected on cleanup; when there is nothing more to load the effect exits
|
|
54
|
+
* before observing, so a finished list costs nothing.
|
|
55
|
+
*/
|
|
56
|
+
export declare const buildSentinelObserverEffect: (vars: InfiniteScrollVars, maxPagesVar: string, buildAdvancePageStatement: () => types.Statement) => types.ExpressionStatement;
|
|
57
|
+
/**
|
|
58
|
+
* `<div ref={ds_N_sentinelRef} aria-hidden="true" style={{ height: '1px' }} />`
|
|
59
|
+
*
|
|
60
|
+
* An invisible one-pixel row after the list. It is generated rather than
|
|
61
|
+
* authored because it is not a control — there is nothing to style or click,
|
|
62
|
+
* only a position in the document for the observer to watch.
|
|
63
|
+
*/
|
|
64
|
+
export declare const buildSentinelElement: (vars: InfiniteScrollVars) => types.JSXElement;
|
|
65
|
+
//# sourceMappingURL=infinite-scroll.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"infinite-scroll.d.ts","sourceRoot":"","sources":["../../src/infinite-scroll.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAErC;;;;;;;;;;;;;;;GAeG;AAIH,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;IACtB,iBAAiB,EAAE,MAAM,CAAA;IACzB,UAAU,EAAE,MAAM,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,qBAAqB,UAAW,MAAM,KAAG,kBAMpD,CAAA;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,+BAA+B,SACpC,kBAAkB,eACX,MAAM,mBACF,MAAM,UAAU,mBAChB,OAAO,KACvB,MAAM,SAAS,EAsDjB,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,SACrC,kBAAkB,WACf,MAAM,KACd,MAAM,uBAoIR,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,SAChC,kBAAkB,eACX,MAAM,6BACQ,MAAM,MAAM,SAAS,KAC/C,MAAM,mBAoGN,CAAA;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,SAAU,kBAAkB,KAAG,MAAM,UAwBnE,CAAA"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.buildSentinelElement = exports.buildSentinelObserverEffect = exports.buildAccumulatingResponseHandler = exports.buildInfiniteScrollDeclarations = exports.getInfiniteScrollVars = void 0;
|
|
27
|
+
var types = __importStar(require("@babel/types"));
|
|
28
|
+
var getInfiniteScrollVars = function (index) { return ({
|
|
29
|
+
accumRefVar: "ds_".concat(index, "_accumRef"),
|
|
30
|
+
hasMoreFlagVar: "ds_".concat(index, "_hasMoreFlag"),
|
|
31
|
+
setHasMoreFlagVar: "setDs_".concat(index, "_hasMoreFlag"),
|
|
32
|
+
hasMoreVar: "ds_".concat(index, "_hasMore"),
|
|
33
|
+
sentinelRefVar: "ds_".concat(index, "_sentinelRef"),
|
|
34
|
+
}); };
|
|
35
|
+
exports.getInfiniteScrollVars = getInfiniteScrollVars;
|
|
36
|
+
/**
|
|
37
|
+
* The three declarations behind an appending list:
|
|
38
|
+
*
|
|
39
|
+
* const ds_0_accumRef = useRef({ sig: '', pages: {} })
|
|
40
|
+
* const [ds_0_hasMoreFlag, setDs_0_hasMoreFlag] = useState(true)
|
|
41
|
+
* const ds_0_hasMore = ds_0_hasMoreFlag && (ds_0_maxPages === 0 || <page> < ds_0_maxPages)
|
|
42
|
+
*
|
|
43
|
+
* Two independent signals for "is there more", because either can be missing.
|
|
44
|
+
* `hasMoreFlag` comes from the last response being a SHORT page, which is the
|
|
45
|
+
* only end-of-data signal available from a data source that cannot count.
|
|
46
|
+
* `maxPages` comes from the count endpoint and is the one that stops a request
|
|
47
|
+
* being made at all; it is `0` while unknown, hence the explicit escape.
|
|
48
|
+
*/
|
|
49
|
+
var buildInfiniteScrollDeclarations = function (vars, maxPagesVar, currentPageExpr, includeSentinel) {
|
|
50
|
+
var declarations = [
|
|
51
|
+
types.variableDeclaration('const', [
|
|
52
|
+
types.variableDeclarator(types.identifier(vars.accumRefVar), types.callExpression(types.identifier('useRef'), [
|
|
53
|
+
types.objectExpression([
|
|
54
|
+
types.objectProperty(types.identifier('sig'), types.stringLiteral('')),
|
|
55
|
+
types.objectProperty(types.identifier('pages'), types.objectExpression([])),
|
|
56
|
+
]),
|
|
57
|
+
])),
|
|
58
|
+
]),
|
|
59
|
+
types.variableDeclaration('const', [
|
|
60
|
+
types.variableDeclarator(types.arrayPattern([
|
|
61
|
+
types.identifier(vars.hasMoreFlagVar),
|
|
62
|
+
types.identifier(vars.setHasMoreFlagVar),
|
|
63
|
+
]), types.callExpression(types.identifier('useState'), [types.booleanLiteral(true)])),
|
|
64
|
+
]),
|
|
65
|
+
types.variableDeclaration('const', [
|
|
66
|
+
types.variableDeclarator(types.identifier(vars.hasMoreVar), types.logicalExpression('&&', types.identifier(vars.hasMoreFlagVar), types.logicalExpression('||', types.binaryExpression('===', types.identifier(maxPagesVar), types.numericLiteral(0)), types.binaryExpression('<', types.cloneNode(currentPageExpr, true), types.identifier(maxPagesVar))))),
|
|
67
|
+
]),
|
|
68
|
+
];
|
|
69
|
+
if (includeSentinel) {
|
|
70
|
+
declarations.push(types.variableDeclaration('const', [
|
|
71
|
+
types.variableDeclarator(types.identifier(vars.sentinelRefVar), types.callExpression(types.identifier('useRef'), [types.nullLiteral()])),
|
|
72
|
+
]));
|
|
73
|
+
}
|
|
74
|
+
return declarations;
|
|
75
|
+
};
|
|
76
|
+
exports.buildInfiniteScrollDeclarations = buildInfiniteScrollDeclarations;
|
|
77
|
+
/**
|
|
78
|
+
* The `.then(...)` tail that accumulates a page and reports whether more exist.
|
|
79
|
+
*
|
|
80
|
+
* Returned as the resolved value of `fetchData`, so `DataProvider` stores the
|
|
81
|
+
* whole accumulated list as its data.
|
|
82
|
+
*/
|
|
83
|
+
var buildAccumulatingResponseHandler = function (vars, perPage) {
|
|
84
|
+
var accumCurrent = function () {
|
|
85
|
+
return types.memberExpression(types.identifier(vars.accumRefVar), types.identifier('current'));
|
|
86
|
+
};
|
|
87
|
+
var accumPages = function () {
|
|
88
|
+
return types.memberExpression(accumCurrent(), types.identifier('pages'));
|
|
89
|
+
};
|
|
90
|
+
return types.arrowFunctionExpression([types.identifier('response')], types.blockStatement([
|
|
91
|
+
types.variableDeclaration('const', [
|
|
92
|
+
types.variableDeclarator(types.identifier('rows'), types.logicalExpression('||', types.optionalMemberExpression(types.identifier('response'), types.identifier('data'), false, true), types.arrayExpression([]))),
|
|
93
|
+
]),
|
|
94
|
+
types.variableDeclaration('const', [
|
|
95
|
+
types.variableDeclarator(types.identifier('page'), types.logicalExpression('||', types.callExpression(types.identifier('Number'), [
|
|
96
|
+
types.memberExpression(types.identifier('params'), types.identifier('page')),
|
|
97
|
+
]), types.numericLiteral(1))),
|
|
98
|
+
]),
|
|
99
|
+
// Everything that identifies the QUERY, with the page zeroed out: two
|
|
100
|
+
// fetches that differ only by page belong to the same accumulated list.
|
|
101
|
+
types.variableDeclaration('const', [
|
|
102
|
+
types.variableDeclarator(types.identifier('sig'), types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [
|
|
103
|
+
types.objectExpression([
|
|
104
|
+
types.spreadElement(types.identifier('params')),
|
|
105
|
+
types.objectProperty(types.identifier('page'), types.numericLiteral(0)),
|
|
106
|
+
]),
|
|
107
|
+
])),
|
|
108
|
+
]),
|
|
109
|
+
types.ifStatement(types.binaryExpression('!==', types.memberExpression(accumCurrent(), types.identifier('sig')), types.identifier('sig')), types.blockStatement([
|
|
110
|
+
types.expressionStatement(types.assignmentExpression('=', accumCurrent(), types.objectExpression([
|
|
111
|
+
types.objectProperty(types.identifier('sig'), types.identifier('sig'), false, true),
|
|
112
|
+
types.objectProperty(types.identifier('pages'), types.objectExpression([])),
|
|
113
|
+
]))),
|
|
114
|
+
])),
|
|
115
|
+
types.expressionStatement(types.assignmentExpression('=', types.memberExpression(accumPages(), types.identifier('page'), true), types.identifier('rows'))),
|
|
116
|
+
// A page that came back short is the last one. This is the only
|
|
117
|
+
// end-of-data signal a data source that cannot count ever gives.
|
|
118
|
+
types.expressionStatement(types.callExpression(types.identifier(vars.setHasMoreFlagVar), [
|
|
119
|
+
types.binaryExpression('>=', types.memberExpression(types.identifier('rows'), types.identifier('length')), types.numericLiteral(perPage)),
|
|
120
|
+
])),
|
|
121
|
+
// Every page held so far, flattened in page order — a numeric sort, so
|
|
122
|
+
// page 10 does not land between 1 and 2. This is what DataProvider stores
|
|
123
|
+
// as its data, and so what the list renders.
|
|
124
|
+
types.returnStatement(types.callExpression(types.memberExpression(types.callExpression(types.memberExpression(types.callExpression(types.memberExpression(types.callExpression(types.memberExpression(types.identifier('Object'), types.identifier('keys')), [accumPages()]), types.identifier('map')), [types.identifier('Number')]), types.identifier('sort')), [
|
|
125
|
+
types.arrowFunctionExpression([types.identifier('a'), types.identifier('b')], types.binaryExpression('-', types.identifier('a'), types.identifier('b'))),
|
|
126
|
+
]), types.identifier('reduce')), [
|
|
127
|
+
types.arrowFunctionExpression([types.identifier('acc'), types.identifier('p')], types.callExpression(types.memberExpression(types.identifier('acc'), types.identifier('concat')), [types.memberExpression(accumPages(), types.identifier('p'), true)])),
|
|
128
|
+
types.arrayExpression([]),
|
|
129
|
+
])),
|
|
130
|
+
]));
|
|
131
|
+
};
|
|
132
|
+
exports.buildAccumulatingResponseHandler = buildAccumulatingResponseHandler;
|
|
133
|
+
/**
|
|
134
|
+
* The IntersectionObserver that loads the next page when the sentinel scrolls
|
|
135
|
+
* into view. Emitted only in auto mode (no Load More button).
|
|
136
|
+
*
|
|
137
|
+
* `rootMargin` starts the fetch a screenful early so the list rarely shows a
|
|
138
|
+
* gap. The observer is rebuilt whenever `hasMore` or `maxPages` change and
|
|
139
|
+
* disconnected on cleanup; when there is nothing more to load the effect exits
|
|
140
|
+
* before observing, so a finished list costs nothing.
|
|
141
|
+
*/
|
|
142
|
+
var buildSentinelObserverEffect = function (vars, maxPagesVar, buildAdvancePageStatement) {
|
|
143
|
+
return types.expressionStatement(types.callExpression(types.identifier('useEffect'), [
|
|
144
|
+
types.arrowFunctionExpression([], types.blockStatement([
|
|
145
|
+
types.variableDeclaration('const', [
|
|
146
|
+
types.variableDeclarator(types.identifier('el'), types.memberExpression(types.identifier(vars.sentinelRefVar), types.identifier('current'))),
|
|
147
|
+
]),
|
|
148
|
+
// No element yet, no observer support (older browsers, jsdom), or
|
|
149
|
+
// nothing left to load.
|
|
150
|
+
types.ifStatement(types.logicalExpression('||', types.logicalExpression('||', types.unaryExpression('!', types.identifier('el'), true), types.binaryExpression('===', types.unaryExpression('typeof', types.identifier('IntersectionObserver')), types.stringLiteral('undefined'))), types.unaryExpression('!', types.identifier(vars.hasMoreVar), true)), types.returnStatement()),
|
|
151
|
+
types.variableDeclaration('const', [
|
|
152
|
+
types.variableDeclarator(types.identifier('observer'), types.newExpression(types.identifier('IntersectionObserver'), [
|
|
153
|
+
types.arrowFunctionExpression([types.identifier('entries')], types.blockStatement([
|
|
154
|
+
types.expressionStatement(types.callExpression(types.memberExpression(types.identifier('entries'), types.identifier('forEach')), [
|
|
155
|
+
types.arrowFunctionExpression([types.identifier('entry')], types.blockStatement([
|
|
156
|
+
types.ifStatement(types.unaryExpression('!', types.memberExpression(types.identifier('entry'), types.identifier('isIntersecting')), true), types.returnStatement()),
|
|
157
|
+
buildAdvancePageStatement(),
|
|
158
|
+
])),
|
|
159
|
+
])),
|
|
160
|
+
])),
|
|
161
|
+
types.objectExpression([
|
|
162
|
+
types.objectProperty(types.identifier('rootMargin'), types.stringLiteral('200px')),
|
|
163
|
+
]),
|
|
164
|
+
])),
|
|
165
|
+
]),
|
|
166
|
+
types.expressionStatement(types.callExpression(types.memberExpression(types.identifier('observer'), types.identifier('observe')), [types.identifier('el')])),
|
|
167
|
+
types.returnStatement(types.arrowFunctionExpression([], types.callExpression(types.memberExpression(types.identifier('observer'), types.identifier('disconnect')), []))),
|
|
168
|
+
])),
|
|
169
|
+
types.arrayExpression([types.identifier(vars.hasMoreVar), types.identifier(maxPagesVar)]),
|
|
170
|
+
]));
|
|
171
|
+
};
|
|
172
|
+
exports.buildSentinelObserverEffect = buildSentinelObserverEffect;
|
|
173
|
+
/**
|
|
174
|
+
* `<div ref={ds_N_sentinelRef} aria-hidden="true" style={{ height: '1px' }} />`
|
|
175
|
+
*
|
|
176
|
+
* An invisible one-pixel row after the list. It is generated rather than
|
|
177
|
+
* authored because it is not a control — there is nothing to style or click,
|
|
178
|
+
* only a position in the document for the observer to watch.
|
|
179
|
+
*/
|
|
180
|
+
var buildSentinelElement = function (vars) {
|
|
181
|
+
return types.jsxElement(types.jsxOpeningElement(types.jsxIdentifier('div'), [
|
|
182
|
+
types.jsxAttribute(types.jsxIdentifier('ref'), types.jsxExpressionContainer(types.identifier(vars.sentinelRefVar))),
|
|
183
|
+
types.jsxAttribute(types.jsxIdentifier('aria-hidden'), types.stringLiteral('true')),
|
|
184
|
+
types.jsxAttribute(types.jsxIdentifier('style'), types.jsxExpressionContainer(types.objectExpression([
|
|
185
|
+
types.objectProperty(types.identifier('height'), types.stringLiteral('1px')),
|
|
186
|
+
]))),
|
|
187
|
+
], true), null, [], true);
|
|
188
|
+
};
|
|
189
|
+
exports.buildSentinelElement = buildSentinelElement;
|
|
190
|
+
//# sourceMappingURL=infinite-scroll.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"infinite-scroll.js","sourceRoot":"","sources":["../../src/infinite-scroll.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as types from '@babel/types';
|
|
2
|
+
/**
|
|
3
|
+
* Numbered pagination: First / Prev / 1 … 4 5 6 … 12 / Next / Last.
|
|
4
|
+
*
|
|
5
|
+
* The builder authors ONE page-number button and a container to repeat it into;
|
|
6
|
+
* this module turns that template into a `.map` over a windowed token list, so
|
|
7
|
+
* every number the visitor sees is the author's own styled node rather than
|
|
8
|
+
* something the generator invented.
|
|
9
|
+
*/
|
|
10
|
+
/** `ds_N_pageTokens` — the windowed list of page numbers and ellipsis markers. */
|
|
11
|
+
export declare const getPageTokensVar: (index: number) => string;
|
|
12
|
+
/**
|
|
13
|
+
* Builds the `useMemo` that decides which page numbers are visible.
|
|
14
|
+
*
|
|
15
|
+
* The window is deliberately small and fixed (first, last, and one neighbour
|
|
16
|
+
* either side of the current page, so at most seven entries): an unbounded
|
|
17
|
+
* strip is unusable at 300 pages and wraps the layout the author styled. The
|
|
18
|
+
* two ellipsis markers are distinct strings because React needs unique keys and
|
|
19
|
+
* a list can legitimately need a gap on both sides.
|
|
20
|
+
*
|
|
21
|
+
* ⛔ Keep this in step with `computePageWindow` in the editor's renderer — the
|
|
22
|
+
* canvas and the published page must agree on which numbers are shown, or the
|
|
23
|
+
* author styles a strip they will never see.
|
|
24
|
+
*/
|
|
25
|
+
export declare const buildPageTokensDeclaration: (index: number, maxPagesVar: string, currentPageExpr: types.Expression) => types.VariableDeclaration;
|
|
26
|
+
export interface NumberedPaginationVars {
|
|
27
|
+
index: number;
|
|
28
|
+
maxPagesVar: string;
|
|
29
|
+
/** How this usage reads its current page (`ds_N_state.page` or `ds_N_page`). */
|
|
30
|
+
currentPageExpr: () => types.Expression;
|
|
31
|
+
/** How it writes a page number back. */
|
|
32
|
+
buildSetPageStatement: (pageExpr: types.Expression) => types.Expression;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Wires the First / Last buttons and expands the page-number template.
|
|
36
|
+
*
|
|
37
|
+
* Prev / Next are left to `wirePaginationButtons`, which already emits them and
|
|
38
|
+
* whose output must stay byte-identical for every project that never switches
|
|
39
|
+
* mode.
|
|
40
|
+
*/
|
|
41
|
+
export declare const wireNumberedPagination: (paginationNode: any, vars: NumberedPaginationVars) => void;
|
|
42
|
+
//# sourceMappingURL=numbered-pagination.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"numbered-pagination.d.ts","sourceRoot":"","sources":["../../src/numbered-pagination.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAOrC;;;;;;;GAOG;AAIH,kFAAkF;AAClF,eAAO,MAAM,gBAAgB,UAAW,MAAM,KAAG,MAAkC,CAAA;AAEnF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,0BAA0B,UAC9B,MAAM,eACA,MAAM,mBACF,MAAM,UAAU,KAChC,MAAM,mBA2GR,CAAA;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,gFAAgF;IAChF,eAAe,EAAE,MAAM,KAAK,CAAC,UAAU,CAAA;IACvC,wCAAwC;IACxC,qBAAqB,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU,CAAA;CACxE;AAQD;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,mBAAoB,GAAG,QAAQ,sBAAsB,KAAG,IA0H1F,CAAA"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.wireNumberedPagination = exports.buildPageTokensDeclaration = exports.getPageTokensVar = void 0;
|
|
27
|
+
var types = __importStar(require("@babel/types"));
|
|
28
|
+
var pagination_controls_1 = require("./pagination-controls");
|
|
29
|
+
/**
|
|
30
|
+
* Numbered pagination: First / Prev / 1 … 4 5 6 … 12 / Next / Last.
|
|
31
|
+
*
|
|
32
|
+
* The builder authors ONE page-number button and a container to repeat it into;
|
|
33
|
+
* this module turns that template into a `.map` over a windowed token list, so
|
|
34
|
+
* every number the visitor sees is the author's own styled node rather than
|
|
35
|
+
* something the generator invented.
|
|
36
|
+
*/
|
|
37
|
+
// tslint:disable:no-any
|
|
38
|
+
/** `ds_N_pageTokens` — the windowed list of page numbers and ellipsis markers. */
|
|
39
|
+
var getPageTokensVar = function (index) { return "ds_".concat(index, "_pageTokens"); };
|
|
40
|
+
exports.getPageTokensVar = getPageTokensVar;
|
|
41
|
+
/**
|
|
42
|
+
* Builds the `useMemo` that decides which page numbers are visible.
|
|
43
|
+
*
|
|
44
|
+
* The window is deliberately small and fixed (first, last, and one neighbour
|
|
45
|
+
* either side of the current page, so at most seven entries): an unbounded
|
|
46
|
+
* strip is unusable at 300 pages and wraps the layout the author styled. The
|
|
47
|
+
* two ellipsis markers are distinct strings because React needs unique keys and
|
|
48
|
+
* a list can legitimately need a gap on both sides.
|
|
49
|
+
*
|
|
50
|
+
* ⛔ Keep this in step with `computePageWindow` in the editor's renderer — the
|
|
51
|
+
* canvas and the published page must agree on which numbers are shown, or the
|
|
52
|
+
* author styles a strip they will never see.
|
|
53
|
+
*/
|
|
54
|
+
var buildPageTokensDeclaration = function (index, maxPagesVar, currentPageExpr) {
|
|
55
|
+
var total = types.identifier('total');
|
|
56
|
+
var current = types.identifier('current');
|
|
57
|
+
var tokens = types.identifier('tokens');
|
|
58
|
+
var start = types.identifier('start');
|
|
59
|
+
var end = types.identifier('end');
|
|
60
|
+
var pageVar = types.identifier('p');
|
|
61
|
+
var body = types.blockStatement([
|
|
62
|
+
types.variableDeclaration('const', [
|
|
63
|
+
types.variableDeclarator(total, types.identifier(maxPagesVar)),
|
|
64
|
+
]),
|
|
65
|
+
types.variableDeclaration('const', [
|
|
66
|
+
types.variableDeclarator(current, types.cloneNode(currentPageExpr, true)),
|
|
67
|
+
]),
|
|
68
|
+
// Nothing matched at all — the empty branch of the list is what renders.
|
|
69
|
+
types.ifStatement(types.binaryExpression('<=', types.cloneNode(total, true), types.numericLiteral(0)), types.returnStatement(types.arrayExpression([]))),
|
|
70
|
+
types.ifStatement(types.binaryExpression('===', types.cloneNode(total, true), types.numericLiteral(1)), types.returnStatement(types.arrayExpression([types.numericLiteral(1)]))),
|
|
71
|
+
types.variableDeclaration('const', [
|
|
72
|
+
types.variableDeclarator(tokens, types.arrayExpression([types.numericLiteral(1)])),
|
|
73
|
+
]),
|
|
74
|
+
types.variableDeclaration('const', [
|
|
75
|
+
types.variableDeclarator(start, types.callExpression(types.memberExpression(types.identifier('Math'), types.identifier('max')), [
|
|
76
|
+
types.numericLiteral(2),
|
|
77
|
+
types.binaryExpression('-', types.cloneNode(current, true), types.numericLiteral(1)),
|
|
78
|
+
])),
|
|
79
|
+
]),
|
|
80
|
+
types.variableDeclaration('const', [
|
|
81
|
+
types.variableDeclarator(end, types.callExpression(types.memberExpression(types.identifier('Math'), types.identifier('min')), [
|
|
82
|
+
types.binaryExpression('-', types.cloneNode(total, true), types.numericLiteral(1)),
|
|
83
|
+
types.binaryExpression('+', types.cloneNode(current, true), types.numericLiteral(1)),
|
|
84
|
+
])),
|
|
85
|
+
]),
|
|
86
|
+
types.ifStatement(types.binaryExpression('>', types.cloneNode(start, true), types.numericLiteral(2)), types.expressionStatement(types.callExpression(types.memberExpression(types.cloneNode(tokens, true), types.identifier('push')), [types.stringLiteral('start-ellipsis')]))),
|
|
87
|
+
types.forStatement(types.variableDeclaration('let', [
|
|
88
|
+
types.variableDeclarator(pageVar, types.cloneNode(start, true)),
|
|
89
|
+
]), types.binaryExpression('<=', types.cloneNode(pageVar, true), types.cloneNode(end, true)), types.assignmentExpression('+=', types.cloneNode(pageVar, true), types.numericLiteral(1)), types.expressionStatement(types.callExpression(types.memberExpression(types.cloneNode(tokens, true), types.identifier('push')), [types.cloneNode(pageVar, true)]))),
|
|
90
|
+
types.ifStatement(types.binaryExpression('<', types.cloneNode(end, true), types.binaryExpression('-', types.cloneNode(total, true), types.numericLiteral(1))), types.expressionStatement(types.callExpression(types.memberExpression(types.cloneNode(tokens, true), types.identifier('push')), [types.stringLiteral('end-ellipsis')]))),
|
|
91
|
+
types.expressionStatement(types.callExpression(types.memberExpression(types.cloneNode(tokens, true), types.identifier('push')), [types.cloneNode(total, true)])),
|
|
92
|
+
types.returnStatement(types.cloneNode(tokens, true)),
|
|
93
|
+
]);
|
|
94
|
+
return types.variableDeclaration('const', [
|
|
95
|
+
types.variableDeclarator(types.identifier((0, exports.getPageTokensVar)(index)), types.callExpression(types.identifier('useMemo'), [
|
|
96
|
+
types.arrowFunctionExpression([], body),
|
|
97
|
+
types.arrayExpression([
|
|
98
|
+
types.identifier(maxPagesVar),
|
|
99
|
+
types.cloneNode(currentPageExpr, true),
|
|
100
|
+
]),
|
|
101
|
+
])),
|
|
102
|
+
]);
|
|
103
|
+
};
|
|
104
|
+
exports.buildPageTokensDeclaration = buildPageTokensDeclaration;
|
|
105
|
+
/** `<setter>(<page>)` wrapped so an already-current page does not re-render. */
|
|
106
|
+
var buildJumpHandler = function (vars, targetExpr) { return types.arrowFunctionExpression([], vars.buildSetPageStatement(targetExpr)); };
|
|
107
|
+
/**
|
|
108
|
+
* Wires the First / Last buttons and expands the page-number template.
|
|
109
|
+
*
|
|
110
|
+
* Prev / Next are left to `wirePaginationButtons`, which already emits them and
|
|
111
|
+
* whose output must stay byte-identical for every project that never switches
|
|
112
|
+
* mode.
|
|
113
|
+
*/
|
|
114
|
+
var wireNumberedPagination = function (paginationNode, vars) {
|
|
115
|
+
var firstButton = (0, pagination_controls_1.findPaginationControl)(paginationNode, 'first');
|
|
116
|
+
var lastButton = (0, pagination_controls_1.findPaginationControl)(paginationNode, 'last');
|
|
117
|
+
var pagesContainer = (0, pagination_controls_1.findPaginationControl)(paginationNode, 'pages');
|
|
118
|
+
if (firstButton) {
|
|
119
|
+
(0, pagination_controls_1.convertControlToButton)(firstButton);
|
|
120
|
+
(0, pagination_controls_1.setJSXExpressionAttribute)(firstButton, 'onClick', buildJumpHandler(vars, types.numericLiteral(1)));
|
|
121
|
+
(0, pagination_controls_1.setJSXExpressionAttribute)(firstButton, 'disabled', types.binaryExpression('<=', vars.currentPageExpr(), types.numericLiteral(1)));
|
|
122
|
+
}
|
|
123
|
+
if (lastButton) {
|
|
124
|
+
(0, pagination_controls_1.convertControlToButton)(lastButton);
|
|
125
|
+
(0, pagination_controls_1.setJSXExpressionAttribute)(lastButton, 'onClick', buildJumpHandler(vars, types.identifier(vars.maxPagesVar)));
|
|
126
|
+
// `maxPages === 0` is "no results", where jumping to the last page would
|
|
127
|
+
// mean jumping to page 0.
|
|
128
|
+
(0, pagination_controls_1.setJSXExpressionAttribute)(lastButton, 'disabled', types.logicalExpression('||', types.binaryExpression('===', types.identifier(vars.maxPagesVar), types.numericLiteral(0)), types.binaryExpression('>=', vars.currentPageExpr(), types.identifier(vars.maxPagesVar))));
|
|
129
|
+
}
|
|
130
|
+
if (!pagesContainer) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
var template = (0, pagination_controls_1.findPaginationControl)(pagesContainer, 'page');
|
|
134
|
+
if (!template) {
|
|
135
|
+
// A container with no template to repeat: leave whatever the author put
|
|
136
|
+
// there alone rather than inventing an unstyled strip.
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
var ellipsisTemplate = (0, pagination_controls_1.findPaginationControl)(pagesContainer, 'ellipsis');
|
|
140
|
+
var tokenId = types.identifier('token');
|
|
141
|
+
// The author's own button, cloned per visible page: their class name (and so
|
|
142
|
+
// every style they applied) rides along.
|
|
143
|
+
var numberButton = types.cloneNode(template, true);
|
|
144
|
+
(0, pagination_controls_1.convertControlToButton)(numberButton);
|
|
145
|
+
(0, pagination_controls_1.setJSXExpressionAttribute)(numberButton, 'onClick', buildJumpHandler(vars, types.cloneNode(tokenId, true)));
|
|
146
|
+
// The current page is marked, not disabled: `[aria-current='page']` is
|
|
147
|
+
// styleable and keeps the button reachable for screen readers.
|
|
148
|
+
(0, pagination_controls_1.setJSXExpressionAttribute)(numberButton, 'aria-current', types.conditionalExpression(types.binaryExpression('===', vars.currentPageExpr(), types.cloneNode(tokenId, true)), types.stringLiteral('page'), types.identifier('undefined')));
|
|
149
|
+
(0, pagination_controls_1.setJSXExpressionAttribute)(numberButton, 'key', types.templateLiteral([
|
|
150
|
+
types.templateElement({ raw: 'page-', cooked: 'page-' }),
|
|
151
|
+
types.templateElement({ raw: '', cooked: '' }, true),
|
|
152
|
+
], [types.cloneNode(tokenId, true)]));
|
|
153
|
+
// Whatever label the template carried ('1') is replaced by the real number.
|
|
154
|
+
numberButton.children = [types.jsxExpressionContainer(types.cloneNode(tokenId, true))];
|
|
155
|
+
var gapNode = ellipsisTemplate
|
|
156
|
+
? types.cloneNode(ellipsisTemplate, true)
|
|
157
|
+
: types.jsxElement(types.jsxOpeningElement(types.jsxIdentifier('span'), [], false), types.jsxClosingElement(types.jsxIdentifier('span')), [types.jsxText('…')], false);
|
|
158
|
+
(0, pagination_controls_1.setJSXExpressionAttribute)(gapNode, 'key', types.cloneNode(tokenId, true));
|
|
159
|
+
// {ds_N_pageTokens.map(token => typeof token === 'number' ? <button…> : <span…>)}
|
|
160
|
+
pagesContainer.children = [
|
|
161
|
+
types.jsxExpressionContainer(types.callExpression(types.memberExpression(types.identifier((0, exports.getPageTokensVar)(vars.index)), types.identifier('map')), [
|
|
162
|
+
types.arrowFunctionExpression([types.cloneNode(tokenId, true)], types.conditionalExpression(types.binaryExpression('===', types.unaryExpression('typeof', types.cloneNode(tokenId, true)), types.stringLiteral('number')), numberButton, gapNode)),
|
|
163
|
+
])),
|
|
164
|
+
];
|
|
165
|
+
};
|
|
166
|
+
exports.wireNumberedPagination = wireNumberedPagination;
|
|
167
|
+
//# sourceMappingURL=numbered-pagination.js.map
|