From 9ad76b4948de93a4207e2eb8499d8fe940097dfc Mon Sep 17 00:00:00 2001 From: Inhji Date: Tue, 21 Mar 2023 07:19:45 +0100 Subject: [PATCH] first tries with kbar --- assets/js/app.js | 23 ++-- assets/js/kbar.js | 283 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 296 insertions(+), 10 deletions(-) create mode 100644 assets/js/kbar.js diff --git a/assets/js/app.js b/assets/js/app.js index 6ec18af..b876f92 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -18,18 +18,21 @@ // 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 { Socket } from "phoenix" +import { LiveSocket } from "phoenix_live_view" import topbar from "../vendor/topbar" import lolight from "../vendor/lolight" +import React from "react" +import { createRoot } from 'react-dom/client' +import KBar from "./kbar" lolight("pre code") let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") -let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}}) +let liveSocket = new LiveSocket("/live", Socket, { params: { _csrf_token: csrfToken } }) // Show progress bar on live navigation and form submits -topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"}) +topbar.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" }) window.addEventListener("phx:page-loading-start", _info => topbar.show(300)) window.addEventListener("phx:page-loading-stop", _info => topbar.hide()) @@ -42,22 +45,22 @@ liveSocket.connect() // >> liveSocket.disableLatencySim() window.liveSocket = liveSocket +const reactRoot = document.querySelector('#react-root') +if (reactRoot) { + const root = createRoot(reactRoot); + root.render(); +} + 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") - } }) - -console.log("boot complete!") \ No newline at end of file diff --git a/assets/js/kbar.js b/assets/js/kbar.js new file mode 100644 index 0000000..96a716a --- /dev/null +++ b/assets/js/kbar.js @@ -0,0 +1,283 @@ +import React from 'react' +import { + KBarProvider, + KBarPortal, + KBarPositioner, + KBarAnimator, + KBarSearch, + KBarResults, + useMatches, + useRegisterActions, + useKBar +} from "kbar"; +import classNames from 'classnames'; + +const searchStyle = { + padding: "12px 16px", + fontSize: "16px", + width: "100%", + boxSizing: "border-box", + outline: "none", + border: "none" +}; + +const animatorStyle = { + maxWidth: "600px", + width: "100%", + borderRadius: "8px", + overflow: "hidden" +}; + +const groupNameStyle = { + padding: "8px 16px", + fontSize: "10px", + textTransform: "uppercase", + opacity: 0.5, +}; + + +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 actions = [ + { + 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"), + } +] + +const dynamicActionsList = { + id: "public.home", + name: "Go Home", + shortcut: ["x"], + keywords: "public", + perform: () => (window.location.pathname = "/"), +} + +function DynamicResultsProvider({children}) { + //const [search, setSearch] = React.useState(""); + //useKBar(state => console.log("state")) + /* + const dynamicActions = React.useMemo(() => { + const searchQuery = search + //const results = await getResults(search); + //return results.map(r => createAction(...)); + console.log(searchQuery) + return dynamicActionsInner + }, [search]) + */ + // const {query, search, options} = useKBar((state) => ({ search: state.searchQuery })) + // const dynamicActions = React.useMemo(() =>{ + // return dynamicActionsInner + // }, [search]) + + const { query } = useKBar(state => ({ query: state.query })) + const [dynamicActions, setDynamicActions] = React.useState([]) + + React.useEffect(() => { + console.log(query) + //fetchUsers(query).then(setUsers) + setDynamicActions(dynamicActionsList) + }, [query]) + + console.log("mount") + + useRegisterActions(dynamicActionsList, [dynamicActions]) + + return ([children]) +} + +export default function KBar() { + return ( + + + + + + + + + + + + + ) +} \ No newline at end of file