diff --git a/assets/css/app.css b/assets/css/app.css index 2ba3d84..06a928b 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -13,13 +13,21 @@ html { font-family: 'Inter', sans-serif; font-feature-settings: "case", "cpsp", "frac", "salt", "ccmp", "cv01", "cv02", "cv03", "cv04", "cv05", "cv06", "cv07", "cv09", "cv10", "cv11"; - @apply text-slate-800; + } + + body { + @apply dark:bg-slate-800; } .stack > * + * { margin-block-start: var(--flow-space, 1em); } + :root[data-mode=dark] .prose { + @apply prose-invert; + } + + /* * ============= SITE LAYOUT ============= */ @@ -47,7 +55,7 @@ @apply flex md:flex-row lg:flex-col mb-6 lg:mb-0; } - & ul.menu { + & h3 { @apply m-0; } } @@ -57,14 +65,20 @@ #site-content { @apply grid grid-cols-1 lg:grid-cols-5 gap-0 lg:gap-12; @apply px-3 sm:px-0; + + #content-wrapper { + @apply col-span-4; + } + + #secondary-sidebar { + @apply col-span-1; + } } - #content-wrapper { - @apply col-span-4; - } + /* === SITE FOOTER === */ - #secondary-sidebar { - @apply col-span-1; + #site-footer { + @apply mt-8 prose max-w-none; } /* diff --git a/assets/js/app.js b/assets/js/app.js index 8506a13..f667226 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1,29 +1,10 @@ -// If you want to use Phoenix channels, run `mix help phx.gen.channel` -// to get started and then uncomment the line below. -// import "./user_socket.js" - -// You can include dependencies in two ways. -// -// The simplest option is to put them in assets/vendor and -// import them using relative paths: -// -// import "../vendor/some-package.js" -// -// Alternatively, you can `npm install some-package --prefix assets` and import -// them using a path starting with the package name: -// -// import "some-package" -// - // Include phoenix_html to handle method=PUT/DELETE in forms and buttons. import "phoenix_html" // Establish Phoenix Socket and LiveView configuration. import { Socket } from "phoenix" import { LiveSocket } from "phoenix_live_view" import topbar from "../vendor/topbar" -import React from "react" -import { createRoot } from 'react-dom' -import KBar from "./kbar" +import darkmode from "./darkmode" let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") let liveSocket = new LiveSocket("/live", Socket, { params: { _csrf_token: csrfToken } }) @@ -42,41 +23,24 @@ liveSocket.connect() // >> liveSocket.disableLatencySim() window.liveSocket = liveSocket -const reactRoot = document.querySelector('#react-root') -if (reactRoot) { - const root = createRoot(reactRoot); - root.render(); -} +document.addEventListener("DOMContentLoaded", function() { + darkmode() -document - .querySelector("#dark-mode-toggle") - .addEventListener("click", (e) => { - e.preventDefault() - const data = document.documentElement.dataset - if (data["mode"] && data["mode"] == "dark") { - delete data["mode"] - window.localStorage.removeItem("theme") - } else { - data["mode"] = "dark" - window.localStorage.setItem("theme", "dark") - } - }) + document + .querySelectorAll('textarea') + .forEach(e => e.addEventListener('keydown', function(e) { + if (e.key == 'Tab') { + e.preventDefault(); + var start = this.selectionStart; + var end = this.selectionEnd; -document - .querySelectorAll('textarea') - .forEach(e => e.addEventListener('keydown', function(e) { - if (e.key == 'Tab') { - e.preventDefault(); - var start = this.selectionStart; - var end = this.selectionEnd; + // set textarea value to: text before caret + tab + text after caret + this.value = this.value.substring(0, start) + + "\t" + this.value.substring(end); - // set textarea value to: text before caret + tab + text after caret - this.value = this.value.substring(0, start) + - "\t" + this.value.substring(end); - - // put caret at right position again - this.selectionStart = - this.selectionEnd = start + 1; - } - })) - + // put caret at right position again + this.selectionStart = + this.selectionEnd = start + 1; + } + })) +}) diff --git a/assets/js/darkmode.js b/assets/js/darkmode.js new file mode 100644 index 0000000..6205910 --- /dev/null +++ b/assets/js/darkmode.js @@ -0,0 +1,15 @@ +export default function() { + document + .querySelector('#dark-mode-toggle') + .addEventListener('click', (e) => { + e.preventDefault() + const data = document.documentElement.dataset + if (data['mode'] && data['mode'] == 'dark') { + delete data['mode'] + window.localStorage.removeItem('theme') + } else { + data['mode'] = 'dark' + window.localStorage.setItem('theme', 'dark') + } + }) +} \ No newline at end of file diff --git a/assets/js/kbar.js b/assets/js/kbar.js deleted file mode 100644 index d3e265c..0000000 --- a/assets/js/kbar.js +++ /dev/null @@ -1,267 +0,0 @@ -import React, {useState, useEffect, useMemo} from 'react' -import { - KBarProvider, - KBarPortal, - KBarPositioner, - KBarAnimator, - KBarSearch, - KBarResults, - useMatches, - useRegisterActions, - useKBar, - createAction -} from "kbar"; -import classNames from 'classnames'; - -const searchStyle = { - padding: "12px 16px", - fontSize: "16px", - width: "100%", - boxSizing: "border-box", - outline: "none" -}; - -const animatorStyle = { - maxWidth: "600px", - width: "100%", - borderRadius: "8px", - overflow: "hidden" -}; - -const groupNameStyle = { - padding: "8px 16px", - fontSize: "10px", - textTransform: "uppercase", - opacity: 0.75, -}; - - -function RenderResults() { - const { results, rootActionId } = useMatches(); - - return ( - - typeof item === "string" ? ( -
{item}
- ) : ( - - ) - } - /> - ); -} - -const ResultItem = React.forwardRef( - ( - { - action, - active, - currentRootActionId, - }, - ref - ) => { - const ancestors = React.useMemo(() => { - if (!currentRootActionId) return action.ancestors; - const index = action.ancestors.findIndex( - (ancestor) => ancestor.id === currentRootActionId - ); - // +1 removes the currentRootAction; e.g. - // if we are on the "Set theme" parent action, - // the UI should not display "Set theme… > Dark" - // but rather just "Dark" - return action.ancestors.slice(index + 1); - }, [action.ancestors, currentRootActionId]); - - return ( -
-
- {action.icon && action.icon} -
-
- {ancestors.length > 0 && - ancestors.map((ancestor) => ( - - - {ancestor.name} - - - › - - - ))} - {action.name} -
- {action.subtitle && ( - {action.subtitle} - )} -
-
- {action.shortcut?.length ? ( -
- {action.shortcut.map((sc) => ( - - {sc} - - ))} -
- ) : null} -
- ); - } -); - -const staticActions = [ - { - id: "user", - name: "User", - shortcut: ["u"], - keywords: "profile", - perform: () => (window.location.pathname = "user"), - }, - { - id: "user.edit", - name: "Edit User", - shortcut: ["u e"], - keywords: "profile edit settings", - perform: () => (window.location.pathname = "user/settings"), - }, - { - id: "admin", - name: "Admin", - shortcut: ["a"], - keywords: "home", - perform: () => (window.location.pathname = "admin"), - }, - { - id: "notes", - name: "Notes", - shortcut: ["n"], - keywords: "posts", - perform: () => (window.location.pathname = "admin/notes"), - }, - { - id: "notes.new", - name: "New Note", - shortcut: ["n n"], - keywords: "create new", - perform: () => (window.location.pathname = "admin/notes/new"), - }, - { - id: "channels", - name: "Channels", - shortcut: ["c"], - keywords: "channels", - perform: () => (window.location.pathname = "admin/channels"), - }, - { - id: "channels.new", - name: "New Channel", - shortcut: ["n c"], - keywords: "create new", - perform: () => (window.location.pathname = "admin/channels/new"), - }, - { - id: "identities", - name: "Identities", - shortcut: ["i"], - keywords: "identities", - perform: () => (window.location.pathname = "admin/identities"), - }, - { - id: "identities.new", - name: "New Identity", - shortcut: ["n i"], - keywords: "create new", - perform: () => (window.location.pathname = "admin/identities/new"), - }, - { - id: "settings", - name: "Settings", - shortcut: ["s"], - keywords: "settings", - perform: () => (window.location.pathname = "admin/settings"), - }, - { - id: "settings.edit", - name: "Edit Settings", - shortcut: ["s e"], - keywords: "settings edit", - perform: () => (window.location.pathname = "admin/settings/edit"), - } -].map(function(a) { - return {...a, section: "Pages"} -}) - -function DynamicResultsProvider() { - const [actions, setActions] = useState([]) - const [notes, setNotes] = useState([]) - const [rerender, setRerender] = useState(true) - - useEffect(() => { - fetch("/api/admin/notes") - .then(resp => resp.json()) - .then(json => setNotes(json.notes)) - }, [rerender]) - - - const noteActions = useMemo(() => notes.map(note => createAction({ - id: note.slug, - name: note.name, - subtitle: note.channels.map(c => c.name).join(", "), - section: "Notes", - keywords: note.channels.map(c => c.name), - perform: () => (window.location.pathname = `/admin/notes/${note.id}`), - })), [notes]) - - useRegisterActions([...staticActions, ...noteActions], [noteActions]) -} - -export default function KBar() { - return ( - - - - - - - - - - - - ) -} \ No newline at end of file diff --git a/assets/js/public.js b/assets/js/public.js index bd10512..c84f378 100644 --- a/assets/js/public.js +++ b/assets/js/public.js @@ -4,6 +4,7 @@ import 'phoenix_html' import hljs from 'highlight.js' import GLightbox from 'glightbox' import Tablesort from 'tablesort' +import darkmode from "./darkmode" document.addEventListener('DOMContentLoaded', (event) => { document.querySelectorAll('.prose pre code').forEach((el) => @@ -11,22 +12,8 @@ document.addEventListener('DOMContentLoaded', (event) => { document.querySelectorAll('.prose table').forEach(el => new Tablesort(el)) -}); -document - .querySelector('#dark-mode-toggle') - .addEventListener('click', (e) => { - e.preventDefault() - const data = document.documentElement.dataset - if (data['mode'] && data['mode'] == 'dark') { - delete data['mode'] - window.localStorage.removeItem('theme') - } else { - data['mode'] = 'dark' - window.localStorage.setItem('theme', 'dark') - } - }) + darkmode() -GLightbox({ selector: '.lightbox' }) - -window.hljs = hljs \ No newline at end of file + GLightbox({ selector: '.lightbox' }) +}); \ No newline at end of file diff --git a/assets/package-lock.json b/assets/package-lock.json index 899f557..43756c1 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -8,16 +8,13 @@ "classnames": "^2.3.2", "glightbox": "^3.2.0", "highlight.js": "^11.8.0", - "kbar": "^0.1.0-beta.43", "phoenix": "file:../deps/phoenix", "phoenix_html": "file:../deps/phoenix_html", "phoenix_live_view": "file:../deps/phoenix_live_view", "tablesort": "^5.3.0" }, "devDependencies": { - "esbuild": "^0.19.2", - "react": "^17.0.2", - "react-dom": "^17.0.2" + "esbuild": "^0.19.2" } }, "../deps/phoenix": { @@ -31,17 +28,6 @@ "version": "0.19.5", "license": "MIT" }, - "node_modules/@babel/runtime": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.15.tgz", - "integrity": "sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@esbuild/android-arm": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.2.tgz", @@ -394,92 +380,6 @@ "node": ">=12" } }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", - "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-portal": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.3.tgz", - "integrity": "sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", - "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-slot": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", - "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@reach/observe-rect": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@reach/observe-rect/-/observe-rect-1.2.0.tgz", - "integrity": "sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==" - }, "node_modules/classnames": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", @@ -522,19 +422,6 @@ "@esbuild/win32-x64": "0.19.2" } }, - "node_modules/fast-equals": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-2.0.4.tgz", - "integrity": "sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w==" - }, - "node_modules/fuse.js": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz", - "integrity": "sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==", - "engines": { - "node": ">=10" - } - }, "node_modules/glightbox": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/glightbox/-/glightbox-3.2.0.tgz", @@ -548,60 +435,6 @@ "node": ">=12.0.0" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/kbar": { - "version": "0.1.0-beta.43", - "resolved": "https://registry.npmjs.org/kbar/-/kbar-0.1.0-beta.43.tgz", - "integrity": "sha512-MmhhvGuZfmA616X9wuy/iaWCPFmlEi6kGkvce/7GlatWmCSkHZhD8glxUruFUpxSN0HZvW/6e/jvSgpRGnC76w==", - "dependencies": { - "@radix-ui/react-portal": "^1.0.1", - "fast-equals": "^2.0.3", - "fuse.js": "^6.6.2", - "react-virtual": "^2.8.2", - "tiny-invariant": "^1.2.0" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/kbar/node_modules/react-virtual": { - "version": "2.10.4", - "resolved": "https://registry.npmjs.org/react-virtual/-/react-virtual-2.10.4.tgz", - "integrity": "sha512-Ir6+oPQZTVHfa6+JL9M7cvMILstFZH/H3jqeYeKI4MSUX+rIruVwFC6nGVXw9wqAw8L0Kg2KvfXxI85OvYQdpQ==", - "funding": [ - "https://github.com/sponsors/tannerlinsley" - ], - "dependencies": { - "@reach/observe-rect": "^1.1.0" - }, - "peerDependencies": { - "react": "^16.6.3 || ^17.0.0" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/phoenix": { "resolved": "../deps/phoenix", "link": true @@ -614,54 +447,10 @@ "resolved": "../deps/phoenix_live_view", "link": true }, - "node_modules/react": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", - "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", - "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "scheduler": "^0.20.2" - }, - "peerDependencies": { - "react": "17.0.2" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" - }, - "node_modules/scheduler": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", - "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, "node_modules/tablesort": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/tablesort/-/tablesort-5.3.0.tgz", "integrity": "sha512-WkfcZBHsp47gVH9CBHG0ZXopriG01IA87arGrchvIe868d4RiXVvoYPS1zMq9IdW05kBs5iGsqxTABqLyWonbg==" - }, - "node_modules/tiny-invariant": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", - "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" } } } diff --git a/assets/package.json b/assets/package.json index 7a26156..0525b0f 100644 --- a/assets/package.json +++ b/assets/package.json @@ -1,14 +1,11 @@ { "devDependencies": { - "esbuild": "^0.19.2", - "react": "^17.0.2", - "react-dom": "^17.0.2" + "esbuild": "^0.19.2" }, "dependencies": { "classnames": "^2.3.2", "glightbox": "^3.2.0", "highlight.js": "^11.8.0", - "kbar": "^0.1.0-beta.43", "phoenix": "file:../deps/phoenix", "phoenix_html": "file:../deps/phoenix_html", "phoenix_live_view": "file:../deps/phoenix_live_view", diff --git a/lib/chiya/flop.ex b/lib/chiya/flop.ex new file mode 100644 index 0000000..7b68b23 --- /dev/null +++ b/lib/chiya/flop.ex @@ -0,0 +1,3 @@ +defmodule Chiya.Flop do + use Flop, repo: Chiya.Repo, default_limit: 10 +end diff --git a/lib/chiya/notes.ex b/lib/chiya/notes.ex index f8b9b70..240f90c 100644 --- a/lib/chiya/notes.ex +++ b/lib/chiya/notes.ex @@ -39,6 +39,14 @@ defmodule Chiya.Notes do |> Repo.preload(@preloads) end + def list_admin_notes(params) do + q = + Note + |> order_by([n], desc: n.updated_at, desc: n.published_at) + + Chiya.Flop.validate_and_run(q, params, for: Chiya.Notes.Note) + end + def list_home_notes(channel, params) do q = list_notes_by_channel_query(channel) @@ -46,10 +54,7 @@ defmodule Chiya.Notes do |> order_by([n], desc: n.updated_at, desc: n.published_at) |> preload(^@preloads) - Flop.validate_and_run(q, params, - for: Chiya.Notes.Note, - repo: Chiya.Repo - ) + Chiya.Flop.validate_and_run(q, params, for: Chiya.Notes.Note) end def list_notes_by_channel(%Chiya.Channels.Channel{} = channel) do diff --git a/lib/chiya_web/components/core_components.ex b/lib/chiya_web/components/core_components.ex index 60634d1..6bb9aab 100644 --- a/lib/chiya_web/components/core_components.ex +++ b/lib/chiya_web/components/core_components.ex @@ -17,8 +17,8 @@ defmodule ChiyaWeb.CoreComponents do alias Phoenix.LiveView.JS import ChiyaWeb.Gettext - import ChiyaWeb.DarkModeToggle + import Flop.Phoenix def favicon(assigns) do ~H""" @@ -738,6 +738,26 @@ defmodule ChiyaWeb.CoreComponents do """ end + attr :meta, Flop.Meta, required: true + attr :id, :string, default: nil + attr :on_change, :string, default: "update-filter" + attr :target, :string, default: nil + attr :fields, :list, default: [] + + def filter_form(%{meta: meta} = assigns) do + assigns = assign(assigns, form: Phoenix.Component.to_form(meta), meta: nil) + + ~H""" + <.form for={@form} id={@id} phx-target={@target} phx-change={@on_change} phx-submit={@on_change}> + <.filter_fields :let={i} form={@form} fields={@fields}> + <.input field={i.field} label={i.label} type={i.type} phx-debounce={120} {i.rest} /> + + + <.button class="button" name="reset">reset + + """ + end + ## JS Commands def show(js \\ %JS{}, selector) do diff --git a/lib/chiya_web/components/layouts/app.html.heex b/lib/chiya_web/components/layouts/app.html.heex index b50a571..2ee54a9 100644 --- a/lib/chiya_web/components/layouts/app.html.heex +++ b/lib/chiya_web/components/layouts/app.html.heex @@ -40,9 +40,7 @@ -
-
- <.flash_group flash={@flash} /> - <%= @inner_content %> -
+
+ <.flash_group flash={@flash} /> + <%= @inner_content %>
diff --git a/lib/chiya_web/components/layouts/root_public.html.heex b/lib/chiya_web/components/layouts/root_public.html.heex index 6344eeb..b16cf6b 100644 --- a/lib/chiya_web/components/layouts/root_public.html.heex +++ b/lib/chiya_web/components/layouts/root_public.html.heex @@ -66,6 +66,12 @@ <% end %> +
  • + + + ☀️ + +
  • @@ -73,23 +79,27 @@
    @@ -98,7 +108,7 @@
    -