Created
June 21, 2024 11:14
-
-
Save derekmcloughlin/8b6bc96c109de5ca098edd23e0646715 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{"name":"Active Search","method":"url","files":[{"location":"./playgrounds/activesearch/server.js","filename":"server.js","builtin":true,"contents":"import { faker } from 'https://cdn.skypack.dev/@faker-js/[email protected]';\nimport Fuse from 'https://cdn.jsdelivr.net/npm/[email protected]/dist/fuse.mjs'\n\nfaker.seed(42)\n\nlet contacts = []\n\nfor (let i = 0; i < 300; i++) {\n contacts.push({\n firstName: faker.name.firstName(),\n lastName: faker.name.lastName(),\n })\n}\n\nconst fuse = new Fuse(contacts, {\n keys: ['firstName', 'lastName'],\n includeScore: true\n})\n\non.get(\"/\", (request) => {\n return render(request, 'index.html', {\n results: []\n })\n})\n\non.post(\"/search\", async (request) => {\n let formData = await request.formData()\n let query = formData.get('search')\n\n let results = fuse.search(query)\n\n await sleep(500);\n\n return render(request, 'results.html', {\n results: results.slice(0, 8)\n })\n})\n"},{"location":"./playgrounds/activesearch/.playground.json","filename":".playground.json","builtin":true,"contents":"{\n \"name\": \"Active Search\",\n \"method\": \"url\",\n \"files\": [\n {\n \"location\": \"./playgrounds/activesearch/server.js\",\n \"filename\": \"server.js\",\n \"builtin\": true\n },\n {\n \"location\": \"./playgrounds/activesearch/.playground.json\",\n \"filename\": \".playground.json\",\n \"builtin\": true\n },\n {\n \"location\": \"./playgrounds/.loader.html\",\n \"filename\": \".loader.html\",\n \"builtin\": true\n },\n {\n \"location\": \"./playgrounds/activesearch/index.html\",\n \"filename\": \"index.html\"\n },\n {\n \"location\": \"./playgrounds/activesearch/results.html\",\n \"filename\": \"results.html\"\n }\n ]\n}"},{"location":"./playgrounds/.loader.html","filename":".loader.html","builtin":true,"contents":"<script src=\"./js/nunjucks.js\"></script>\n<script src=\"./js/pollyjs-core.js\"></script>\n<script src=\"./js/pollyjs-adapter-fetch.js\"></script>\n<script src=\"./js/pollyjs-adapter-xhr.js\"></script>\n<script>\n\n document.addEventListener('htmx:configRequest', function(evt) {\n evt.detail.path = window.location.href + evt.detail.path;\n });\n\n const fileContents = {};\n files.forEach(file => fileContents[file.filename] = file.contents);\n\n function readFile(filename) {\n return fileContents[filename]\n }\n\n let TemplateLoader = {\n getSource: (name) => ({\n src: readFile(name),\n path: name\n })\n }\n var templates = new nunjucks.Environment(TemplateLoader, {\n autoescape: false\n })\n\n function render(request, template, context) {\n let out = templates.render(template, {...context, request});\n \n return new Response(out, {\n headers: {\n 'content-type': 'text/html'\n }\n })\n }\n\n const { Polly } = window['@pollyjs/core'];\n\n Polly.register(window['@pollyjs/adapter-fetch']);\n Polly.register(window['@pollyjs/adapter-xhr']);\n\n const polly = new Polly('sandbox-server', {\n adapters: ['fetch', 'xhr'],\n mode: \"passthrough\",\n logging: true,\n });\n\n const server = polly.server;\n \n const on = {};\n \n ['get','put','post','patch','delete','merge','head','options'].forEach((method) => {\n on[method] = (route, handler) => {\n \n let parsedRoute = window.location.href + route.replaceAll(/\\/<([^>]+)>/gm, (_, m) => \"/:\"+m)\n\n server[method](parsedRoute).intercept(async (req, res) => {\n\n var requestHeaders = Object.assign({}, req.headers)\n var request = new Request(req.url, {\n body: req.body,\n headers: requestHeaders,\n method: req.method,\n })\n\n var response = await handler(request, ...Object.values(req.params), ...new URL(req.url).searchParams.values())\n\n if (!response) throw new Error(\"Handler returned no response!\") // Return 404?\n\n let responseHeaders = {}\n\n for (const [k,v] of response.headers.entries()) {\n res.setHeader(k,v);\n responseHeaders[k] = v\n }\n\n let text = await response.text()\n\n \n res.status(response.status).send(text);\n \n window.parent.postMessage({\n type: \"network_log\",\n request: {\n url: req.url.replace(window.location.href, ''),\n body: req.body,\n method: req.method,\n headers: requestHeaders,\n },\n response: {\n headers: responseHeaders,\n status: response.status,\n body: text,\n },\n }, \"*\")\n });\n }\n })\n \n const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay))\n</script>\n<script type=\"module\">\n ///server.js\n</script>"},{"location":"./playgrounds/activesearch/index.html","filename":"index.html","contents":"<html>\n<head>\n <link href=\"https://unpkg.com/water.css@2/out/dark.css\" rel=\"stylesheet\">\n <script src=\"https://unpkg.com/htmx.org@<2/dist/htmx.js\" defer></script>\n</head>\n<body>\n \n <h1>Active Search <span class=\"htmx-indicator\">Searching...</span></h1>\n\n <input type=\"search\" \n name=\"search\" placeholder=\"Type to search...\" \n hx-post=\"/search\" \n hx-trigger=\"input changed delay:500ms, search\" \n hx-target=\"#search-results\"\n hx-indicator=\".htmx-indicator\">\n \n <table>\n <thead>\n <tr>\n <th>Distance</th>\n <th>First Name</th>\n <th>Last Name</th>\n </tr>\n </thead>\n <tbody id=\"search-results\"></tbody>\n </table>\n\n</body>\n</html>"},{"location":"./playgrounds/activesearch/results.html","filename":"results.html","contents":"{% for result in results %}\n <tr>\n <td>{{ result.score|round(2) }}</td>\n <td>{{ result.item.firstName }}</td>\n <td>{{ result.item.lastName }}</td>\n </tr>\n{% endfor %}"}]} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment