From 6e4156968e4071e9847d5e78f2a7d69b8265b736 Mon Sep 17 00:00:00 2001 From: D063222 Date: Mon, 5 Dec 2022 15:03:42 +0100 Subject: [PATCH 1/8] [exampleapp] playing with the select --- apps/exampleapp/apiserver.config.js | 4 + apps/exampleapp/public/secretOptions.json | 222 ++++++++++++++++++ apps/exampleapp/src/AppContent.js | 2 + apps/exampleapp/src/actions.js | 17 +- .../src/components/SmartSelectInput.js | 106 +++++++++ apps/exampleapp/src/components/TabPanelTwo.js | 32 +++ 6 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 apps/exampleapp/public/secretOptions.json create mode 100644 apps/exampleapp/src/components/SmartSelectInput.js create mode 100644 apps/exampleapp/src/components/TabPanelTwo.js diff --git a/apps/exampleapp/apiserver.config.js b/apps/exampleapp/apiserver.config.js index ebb65bc3b..9a6b5d69a 100644 --- a/apps/exampleapp/apiserver.config.js +++ b/apps/exampleapp/apiserver.config.js @@ -12,11 +12,15 @@ const apiServer = require("./apiserver.config") */ let peaksData = require("./public/peaks.json") +let secretOptions = require("./public/secretOptions.json") const bodyParser = require("body-parser") module.exports = { getHandlers: () => { return (devServer) => { + devServer.app.get("/secrets", function (req, res) { + res.json(secretOptions) + }) devServer.app.get("/peaks", function (req, res) { res.json(peaksData) }) diff --git a/apps/exampleapp/public/secretOptions.json b/apps/exampleapp/public/secretOptions.json new file mode 100644 index 000000000..13ca7de45 --- /dev/null +++ b/apps/exampleapp/public/secretOptions.json @@ -0,0 +1,222 @@ +[ + { + "label": "HN_TEST_1 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/7a03c517-df7e-48c1-aca2-5c700d5382b6)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/7a03c517-df7e-48c1-aca2-5c700d5382b6" + }, + { + "label": "HN_TEST_4 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/da2c2f70-e11a-4e8a-ba90-7ecf02858342)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/da2c2f70-e11a-4e8a-ba90-7ecf02858342" + }, + { + "label": "HN_TEST_2 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e876b43f-987f-40ee-82c9-e024e55c34e5)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e876b43f-987f-40ee-82c9-e024e55c34e5" + }, + { + "label": "HN_TEST_1 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d26555aa-7f7a-4c22-ad2d-edd8d10b9b28)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d26555aa-7f7a-4c22-ad2d-edd8d10b9b28" + }, + { + "label": "i331795-testserver.crt (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/fe558d24-db7e-455a-8ad4-819eb5a45c9e)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/fe558d24-db7e-455a-8ad4-819eb5a45c9e" + }, + { + "label": "tls_secret1 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e094d766-fd20-461c-8a95-23174d4ed412)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e094d766-fd20-461c-8a95-23174d4ed412" + }, + { + "label": "testSecret_9997 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/ec31b126-f2d3-47a5-8cdc-3eb316f22afd)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/ec31b126-f2d3-47a5-8cdc-3eb316f22afd" + }, + { + "label": "testSecret_9992 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d3a4e12e-c231-4edb-8ba2-51ed1e25e027)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d3a4e12e-c231-4edb-8ba2-51ed1e25e027" + }, + { + "label": "testSecret_9989 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d1ae4f8b-d0b0-4347-a6a0-e06d1d62ad1b)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d1ae4f8b-d0b0-4347-a6a0-e06d1d62ad1b" + }, + { + "label": "testSecret_9987 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f32a151d-7d98-4cec-915e-f7287d67b133)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f32a151d-7d98-4cec-915e-f7287d67b133" + }, + { + "label": "testSecret_9976 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/cb8ee9d1-8f75-4fab-be12-136d18cc5e61)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/cb8ee9d1-8f75-4fab-be12-136d18cc5e61" + }, + { + "label": "testSecret_9972 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f359d462-89d2-47e3-b7a5-42b412d287ae)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f359d462-89d2-47e3-b7a5-42b412d287ae" + }, + { + "label": "testSecret_9967 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e0d9f463-926f-41ba-ab37-8c40a8eb8f05)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e0d9f463-926f-41ba-ab37-8c40a8eb8f05" + }, + { + "label": "testSecret_9966 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/cc8bf2d7-8bc1-4197-8ec0-b29a5135cb6a)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/cc8bf2d7-8bc1-4197-8ec0-b29a5135cb6a" + }, + { + "label": "testSecret_9964 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e0a07c84-eaa4-418c-ab0b-5eaf066937fa)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e0a07c84-eaa4-418c-ab0b-5eaf066937fa" + }, + { + "label": "testSecret_9961 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f78af18f-7ddc-4510-9cbc-073867adce21)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f78af18f-7ddc-4510-9cbc-073867adce21" + }, + { + "label": "testSecret_9960 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f5b872b9-b57b-4629-a582-2c9bd9c83dc0)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f5b872b9-b57b-4629-a582-2c9bd9c83dc0" + }, + { + "label": "testSecret_9958 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f4c7d455-bd3b-4fb8-8e45-5226e3fce5bd)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f4c7d455-bd3b-4fb8-8e45-5226e3fce5bd" + }, + { + "label": "testSecret_9956 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f9640ac6-a206-4c0d-9831-068a91c91028)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f9640ac6-a206-4c0d-9831-068a91c91028" + }, + { + "label": "testSecret_9949 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e2ff0cf2-be5b-4bca-891b-e7c76e67614c)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e2ff0cf2-be5b-4bca-891b-e7c76e67614c" + }, + { + "label": "testSecret_9947 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d7fc6d91-9e21-4dac-90db-3310230286a4)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d7fc6d91-9e21-4dac-90db-3310230286a4" + }, + { + "label": "testSecret_9944 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d4fc4e89-1cb2-4295-933b-101dacc9025a)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d4fc4e89-1cb2-4295-933b-101dacc9025a" + }, + { + "label": "testSecret_9933 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e845a090-fe3f-45a2-938d-e4cb01120674)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e845a090-fe3f-45a2-938d-e4cb01120674" + }, + { + "label": "testSecret_9925 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/ebea20d7-d1ab-4f9f-9519-bf76c568f834)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/ebea20d7-d1ab-4f9f-9519-bf76c568f834" + }, + { + "label": "testSecret_9924 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f43d1f8c-d621-4b5f-8a60-e7871d59ee49)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f43d1f8c-d621-4b5f-8a60-e7871d59ee49" + }, + { + "label": "testSecret_9919 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/feb40d4f-f93f-42be-8d6c-bb28fbd292ef)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/feb40d4f-f93f-42be-8d6c-bb28fbd292ef" + }, + { + "label": "testSecret_9917 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/cd65e6c9-e28a-45c1-9f33-ad8b2d1ce2a5)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/cd65e6c9-e28a-45c1-9f33-ad8b2d1ce2a5" + }, + { + "label": "testSecret_9912 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d9e0e87d-774d-4f87-8246-3bfba84d57cb)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d9e0e87d-774d-4f87-8246-3bfba84d57cb" + }, + { + "label": "testSecret_9910 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e0d52bbe-60db-4f80-b11d-38c0c1b72f80)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e0d52bbe-60db-4f80-b11d-38c0c1b72f80" + }, + { + "label": "testSecret_9906 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f5421539-7942-4e0c-939a-510e1f8e47e9)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f5421539-7942-4e0c-939a-510e1f8e47e9" + }, + { + "label": "testSecret_9904 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e84852cf-fa78-40fd-b0ac-4d4b3dcb29a6)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e84852cf-fa78-40fd-b0ac-4d4b3dcb29a6" + }, + { + "label": "testSecret_9899 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/fc4c3f04-8862-4c80-974c-37d088202975)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/fc4c3f04-8862-4c80-974c-37d088202975" + }, + { + "label": "testSecret_9897 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/c90eb4dc-654e-44e4-bfbb-bb4446361ac9)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/c90eb4dc-654e-44e4-bfbb-bb4446361ac9" + }, + { + "label": "testSecret_9888 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f1674486-c6e7-4545-9b8f-bb78b255b147)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f1674486-c6e7-4545-9b8f-bb78b255b147" + }, + { + "label": "testSecret_9887 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e7f1e72a-74f8-4452-a6df-6abf39f7d619)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e7f1e72a-74f8-4452-a6df-6abf39f7d619" + }, + { + "label": "testSecret_9881 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/cf7745fb-e41f-422a-a982-154def92ccf3)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/cf7745fb-e41f-422a-a982-154def92ccf3" + }, + { + "label": "testSecret_9879 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d3d872b4-ed0b-430e-aa64-c47aaddbe75b)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d3d872b4-ed0b-430e-aa64-c47aaddbe75b" + }, + { + "label": "testSecret_9877 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e9042ce0-dc0b-47c9-a5f0-a083730d2526)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e9042ce0-dc0b-47c9-a5f0-a083730d2526" + }, + { + "label": "testSecret_9874 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e0315199-a824-471b-aed9-ffb4ca95095b)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e0315199-a824-471b-aed9-ffb4ca95095b" + }, + { + "label": "testSecret_9870 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/c9d570fc-a274-4919-ba68-bac93cfa2d72)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/c9d570fc-a274-4919-ba68-bac93cfa2d72" + }, + { + "label": "testSecret_9867 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d0a62685-c9dc-49bb-8c40-98b9ea7a5d22)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d0a62685-c9dc-49bb-8c40-98b9ea7a5d22" + }, + { + "label": "testSecret_9866 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d67c6ef4-040e-495e-8a90-32aee1c3b380)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d67c6ef4-040e-495e-8a90-32aee1c3b380" + }, + { + "label": "testSecret_9862 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/cbccb5ff-e771-48b6-8f25-ca871a9dea80)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/cbccb5ff-e771-48b6-8f25-ca871a9dea80" + }, + { + "label": "testSecret_9857 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/eb47b161-6f2b-4b1e-97ec-3f5721f392eb)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/eb47b161-6f2b-4b1e-97ec-3f5721f392eb" + }, + { + "label": "testSecret_9847 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f5202eb2-9548-424e-8b6c-af60e68bfcaa)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f5202eb2-9548-424e-8b6c-af60e68bfcaa" + }, + { + "label": "testSecret_9840 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/efd2b227-e106-456e-8ced-28d6f1bcaead)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/efd2b227-e106-456e-8ced-28d6f1bcaead" + }, + { + "label": "testSecret_9837 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e099b4ed-d92e-4408-a4fe-dc8be3edd101)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e099b4ed-d92e-4408-a4fe-dc8be3edd101" + }, + { + "label": "testSecret_9834 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/ed86278b-b614-4830-81ed-3ea8cb17df42)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/ed86278b-b614-4830-81ed-3ea8cb17df42" + }, + { + "label": "testSecret_9824 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f9c62b2f-8b17-443c-b77c-d9633f45a81d)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/f9c62b2f-8b17-443c-b77c-d9633f45a81d" + }, + { + "label": "testSecret_9823 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/cef7e105-4663-40bb-885a-b200ab55a946)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/cef7e105-4663-40bb-885a-b200ab55a946" + }, + { + "label": "testSecret_9821 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/eb6a3bad-e01b-46e5-84bb-419df7b06bd3)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/eb6a3bad-e01b-46e5-84bb-419df7b06bd3" + }, + { + "label": "testSecret_9818 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e53ab67a-c55a-4d99-8e1e-a9c429bea420)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/e53ab67a-c55a-4d99-8e1e-a9c429bea420" + }, + { + "label": "testSecret_9814 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/df3569c9-84df-4024-acee-885af99d6698)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/df3569c9-84df-4024-acee-885af99d6698" + }, + { + "label": "testSecret_9810 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d7cb0080-77c1-4fee-b132-ef196ae0c640)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d7cb0080-77c1-4fee-b132-ef196ae0c640" + }, + { + "label": "testSecret_9809 (https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d04eecd0-b4fb-4196-90d9-d103b27f99d1)", + "value": "https://keymanager-3.qa-de-1.cloud.sap:443/v1/secrets/d04eecd0-b4fb-4196-90d9-d103b27f99d1" + } +] diff --git a/apps/exampleapp/src/AppContent.js b/apps/exampleapp/src/AppContent.js index b22909c07..8b42994e4 100644 --- a/apps/exampleapp/src/AppContent.js +++ b/apps/exampleapp/src/AppContent.js @@ -17,6 +17,7 @@ import PanelManager from "./components/PanelManager" import { useQuery } from "react-query" import { fetchPeaks } from "./actions" import PeaksList from "./components/PeaksList/PeaksList" +import TabPanelTwo from "./components/TabPanelTwo" const AppContent = (props) => { const endpoint = useStore((state) => state.endpoint) @@ -99,6 +100,7 @@ const AppContent = (props) => { Content Panel two. Normally you will probably want to put the TabPanel content into separate components. + diff --git a/apps/exampleapp/src/actions.js b/apps/exampleapp/src/actions.js index 4e1d7f980..1df7ed1ad 100644 --- a/apps/exampleapp/src/actions.js +++ b/apps/exampleapp/src/actions.js @@ -29,7 +29,22 @@ const checkStatus = (response) => { } } -// Example fetch call. Adjust as needed for your API +export const fetchSecretOptions = ({ queryKey }) => { + const [_key, endpoint, options] = queryKey + const query = encodeUrlParamsFromObject(options) + return fetch(`${endpoint}/secrets?${query}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + }) + .then(checkStatus) + .then((response) => { + return response.json() + }) +} + export const fetchPeaks = ({ queryKey }) => { const [_key, endpoint, options] = queryKey const query = encodeUrlParamsFromObject(options) diff --git a/apps/exampleapp/src/components/SmartSelectInput.js b/apps/exampleapp/src/components/SmartSelectInput.js new file mode 100644 index 000000000..2f2031a54 --- /dev/null +++ b/apps/exampleapp/src/components/SmartSelectInput.js @@ -0,0 +1,106 @@ +import React, { useMemo, useState, useRef, useEffect } from "react" +import { + SelectRow, + SelectOption, + TextInputRow, + DataGrid, + DataGridRow, + DataGridCell, +} from "juno-ui-components" + +const useOnClickOutside = (ref, handler) => { + useEffect(() => { + const listener = (event) => { + // Do nothing if clicking ref's element or descendent elements + // if (!ref.current || ref.current.contains(event.target)) { + // return + // } + + console.log("ref: ", ref.current, event) + + handler(event) + } + window.addEventListener("click", listener) + return () => { + window.removeEventListener("click", listener) + } + }, [ref, handler]) +} + +const optionsContainer = (isOpen) => { + return ` + max-h-64 + overflow-y-scroll + outline-none + ring-2 + ring-theme-focus + opacity-0 + transition + ease-out + duration-300 + ${isOpen && `opacity-100`} + ` + .replace(/\n/g, " ") + .replace(/\s+/g, " ") +} + +const SmartSelectInput = ({ options }) => { + const [displayOptions, setDisplayOptions] = useState(false) + const ref = useRef() + + useOnClickOutside(ref, () => { + console.log("useOnClickOutside") + // setDisplayOptions(false) + }) + + options = useMemo(() => { + if (!options) return [] + return options + }, [options]) + + const onFocus = (event) => { + console.log("onfocus", event) + setDisplayOptions(true) + } + const onBlur = (event) => { + console.log("onBlur", event) + // setDisplayOptions(false) + event.currentTarget.focus() + event.preventDefault() + } + + return ( + <> + + + + + + +
+
+ + {options.length > 0 && ( + <> + {options.map((option, i) => ( + + {option.value} + + ))} + + )} + +
+
+ + ) +} + +export default SmartSelectInput diff --git a/apps/exampleapp/src/components/TabPanelTwo.js b/apps/exampleapp/src/components/TabPanelTwo.js new file mode 100644 index 000000000..d16ded335 --- /dev/null +++ b/apps/exampleapp/src/components/TabPanelTwo.js @@ -0,0 +1,32 @@ +import React from "react" + +import useStore from "../store" +import { useQuery } from "react-query" +import { fetchSecretOptions } from "../actions" +import SmartSelectInput from "./SmartSelectInput" + +const TabPanelTwo = ({}) => { + const endpoint = useStore((state) => state.endpoint) + const { isLoading, isError, data, error } = useQuery( + ["defaultSecrets", endpoint, {}], + fetchSecretOptions, + { + // enable the query also if the endpoint is set. For fetching local + // data is not necessary since it should be empty + // enabled: !!endpoint, + // If set to Infinity, the data will never be considered stale + // until a browser reload is triggered + staleTime: Infinity, + // refer to this documentation to see more options + // https://tanstack.com/query/v4/docs/guides/queries + } + ) + + return ( + <> + + + ) +} + +export default TabPanelTwo From c0150b206bd668989cd786c1eb9ef87526681ad3 Mon Sep 17 00:00:00 2001 From: D063222 Date: Tue, 20 Dec 2022 14:18:11 +0100 Subject: [PATCH 2/8] [example] working basic select --- apps/exampleapp/package.json | 1 + .../src/components/SmartSelectInput.js | 216 ++++++++++++------ apps/exampleapp/src/components/TabPanelTwo.js | 76 ++++++ yarn.lock | 65 ++++++ 4 files changed, 289 insertions(+), 69 deletions(-) diff --git a/apps/exampleapp/package.json b/apps/exampleapp/package.json index 737ebef39..6e258c78c 100644 --- a/apps/exampleapp/package.json +++ b/apps/exampleapp/package.json @@ -8,6 +8,7 @@ "@babel/core": "^7.20.2", "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", + "@floating-ui/react": "^0.14.0", "@svgr/webpack": "^6.2.1", "@testing-library/dom": "^8.19.0", "@testing-library/jest-dom": "^5.16.5", diff --git a/apps/exampleapp/src/components/SmartSelectInput.js b/apps/exampleapp/src/components/SmartSelectInput.js index 2f2031a54..f21a3d523 100644 --- a/apps/exampleapp/src/components/SmartSelectInput.js +++ b/apps/exampleapp/src/components/SmartSelectInput.js @@ -1,32 +1,25 @@ -import React, { useMemo, useState, useRef, useEffect } from "react" +import React, { useMemo, useState, useEffect } from "react" +import { + useFloating, + autoUpdate, + offset, + flip, + shift, + useDismiss, + useRole, + useClick, + useInteractions, + FloatingFocusManager, + useId, +} from "@floating-ui/react" import { - SelectRow, - SelectOption, TextInputRow, DataGrid, DataGridRow, DataGridCell, + Badge, } from "juno-ui-components" -const useOnClickOutside = (ref, handler) => { - useEffect(() => { - const listener = (event) => { - // Do nothing if clicking ref's element or descendent elements - // if (!ref.current || ref.current.contains(event.target)) { - // return - // } - - console.log("ref: ", ref.current, event) - - handler(event) - } - window.addEventListener("click", listener) - return () => { - window.removeEventListener("click", listener) - } - }, [ref, handler]) -} - const optionsContainer = (isOpen) => { return ` max-h-64 @@ -34,72 +27,157 @@ const optionsContainer = (isOpen) => { outline-none ring-2 ring-theme-focus - opacity-0 - transition - ease-out - duration-300 - ${isOpen && `opacity-100`} + bg-theme-textinput ` .replace(/\n/g, " ") .replace(/\s+/g, " ") } +const optionsRow = ` + hover:text-theme-accent +` + +const fakeInputText = ` + text-theme-textinput + bg-theme-textinput + min-h-[2.5rem] + rounded-3px + p-2 +` + +const fakeInputTextPlaceholder = ` + opacity-50 +` + +const fakeInputTextOptions = ` + mr-1 +` + const SmartSelectInput = ({ options }) => { + const [open, setOpen] = useState(false) + const [selectedOptions, setSelectedOptions] = useState([]) const [displayOptions, setDisplayOptions] = useState(false) - const ref = useRef() - - useOnClickOutside(ref, () => { - console.log("useOnClickOutside") - // setDisplayOptions(false) - }) options = useMemo(() => { if (!options) return [] return options }, [options]) - const onFocus = (event) => { - console.log("onfocus", event) - setDisplayOptions(true) + useEffect(() => { + if (options) { + const difference = options.filter( + ({ value: id1 }) => + !selectedOptions.some(({ value: id2 }) => id2 === id1) + ) + setDisplayOptions(difference) + } + }, [selectedOptions, options]) + + const { x, y, reference, floating, strategy, context } = useFloating({ + open, + placement: "bottom-start", + onOpenChange: setOpen, + middleware: [offset(5), shift()], + whileElementsMounted: autoUpdate, + }) + + const click = useClick(context) + const dismiss = useDismiss(context) + const role = useRole(context) + + const { getReferenceProps, getFloatingProps } = useInteractions([ + click, + dismiss, + role, + ]) + + const headingId = useId() + + // =============== + + const onChange = (event) => { + setOpen(true) } - const onBlur = (event) => { - console.log("onBlur", event) - // setDisplayOptions(false) - event.currentTarget.focus() - event.preventDefault() + + const onOptionClicked = (option) => { + console.log("onOptionClicked: ", option) + setSelectedOptions([...selectedOptions, option]) } + const onOptionDeselected = (option) => { + const index = selectedOptions.findIndex( + (item) => item.value == option.value + ) + if (index < 0) { + return + } + let newOptions = selectedOptions.slice() + newOptions.splice(index, 1) + setSelectedOptions(newOptions) + } + + console.log("selectedOptions: ", selectedOptions) + return ( - <> - - - - - - -
-
- - {options.length > 0 && ( - <> - {options.map((option, i) => ( - - {option.value} - - ))} - - )} - +
+
+ {/* */} +
+ {selectedOptions.length > 0 ? ( + <> + {selectedOptions.map((item, index) => ( + onOptionDeselected(item)} + /> + ))} + + ) : ( +
Select...
+ )}
- + {open && ( + +
+ + {displayOptions.length > 0 && ( + <> + {displayOptions.map((option, i) => ( + onOptionClicked(option)} + className={optionsRow} + > + {option.value} + + ))} + + )} + +
+
+ )} +
) } diff --git a/apps/exampleapp/src/components/TabPanelTwo.js b/apps/exampleapp/src/components/TabPanelTwo.js index d16ded335..8771bb481 100644 --- a/apps/exampleapp/src/components/TabPanelTwo.js +++ b/apps/exampleapp/src/components/TabPanelTwo.js @@ -24,7 +24,83 @@ const TabPanelTwo = ({}) => { return ( <> + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo + ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis + dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies + nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. + Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In + enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum + felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus + elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo + ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem + ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla + ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies + nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam + rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper + libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit + vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante + tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam + quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed + fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed + consequat, leo eget bibendum sodales, augue velit cursus nunc, Lorem ipsum + dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget + dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient + montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, + pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec + pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim + justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum + felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus + elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo + ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem + ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla + ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies + nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam + rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper + libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit + vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante + tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam + quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed + fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed + consequat, leo eget bibendum sodales, augue velit cursus nunc, + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo + ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis + dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies + nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. + Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In + enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum + felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus + elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo + ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem + ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla + ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies + nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam + rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper + libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit + vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante + tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam + quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed + fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed + consequat, leo eget bibendum sodales, augue velit cursus nunc, Lorem ipsum + dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget + dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient + montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, + pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec + pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim + justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum + felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus + elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo + ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem + ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla + ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies + nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam + rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper + libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit + vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante + tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam + quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed + fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed + consequat, leo eget bibendum sodales, augue velit cursus nunc, ) } diff --git a/yarn.lock b/yarn.lock index 30c0c2f5f..d16821ef6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3113,6 +3113,48 @@ __metadata: languageName: node linkType: hard +"@floating-ui/core@npm:^1.0.4": + version: 1.0.4 + resolution: "@floating-ui/core@npm:1.0.4" + checksum: 6362a625ad0dba7cfd197fc4b6f620eed95147d38684d7347287569897098862c6ba1173f3758d76d22e7739112c57dc836a569c582e78f5807c26ef41df4989 + languageName: node + linkType: hard + +"@floating-ui/dom@npm:^1.0.5": + version: 1.0.12 + resolution: "@floating-ui/dom@npm:1.0.12" + dependencies: + "@floating-ui/core": ^1.0.4 + checksum: fbcdec5fb8d096a65da346e76fc6c8f2e4db3c9c70b06dea17321c775bc125ae0b02450a8b195a224b8edf9acba205b607f6d638bda78a2ca1b2ec92fa15b784 + languageName: node + linkType: hard + +"@floating-ui/react-dom@npm:^1.0.1": + version: 1.0.1 + resolution: "@floating-ui/react-dom@npm:1.0.1" + dependencies: + "@floating-ui/dom": ^1.0.5 + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 74b80fd46f2f301f444af458e5bbdb0ef0321786aca15dd892c45ce4077a500dfc84c117cb1669360ecc1378346eff77886f1b0dd9b1adbb30e72f894fe27dfb + languageName: node + linkType: hard + +"@floating-ui/react@npm:^0.14.0": + version: 0.14.0 + resolution: "@floating-ui/react@npm:0.14.0" + dependencies: + "@floating-ui/react-dom": ^1.0.1 + aria-hidden: ^1.1.3 + tabbable: ^6.0.1 + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: b6d7f575059a4cf3aad593335dac0a6dc4f19d8a06390454b5a87933205c195310c10f50b7c33a31ccb0b617250b0d238dfa370749c93e2e31c8fba2a94cd54f + languageName: node + linkType: hard + "@gar/promisify@npm:^1.0.1": version: 1.1.2 resolution: "@gar/promisify@npm:1.1.2" @@ -7918,6 +7960,21 @@ __metadata: languageName: node linkType: hard +"aria-hidden@npm:^1.1.3": + version: 1.2.2 + resolution: "aria-hidden@npm:1.2.2" + dependencies: + tslib: ^2.0.0 + peerDependencies: + "@types/react": ^16.9.0 || ^17.0.0 || ^18.0.0 + react: ^16.9.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: ee1a3688db5491eeb87b73eea624614f24ef62a74cf9e47bc8229dde1ff7457f7e4a26425cadc0d3efd89380305e6fb4a4e505bccdee16beaa4686014861d7b1 + languageName: node + linkType: hard + "aria-query@npm:^5.0.0": version: 5.0.0 resolution: "aria-query@npm:5.0.0" @@ -12226,6 +12283,7 @@ __metadata: "@babel/core": ^7.20.2 "@babel/preset-env": ^7.20.2 "@babel/preset-react": ^7.18.6 + "@floating-ui/react": ^0.14.0 "@svgr/webpack": ^6.2.1 "@testing-library/dom": ^8.19.0 "@testing-library/jest-dom": ^5.16.5 @@ -23596,6 +23654,13 @@ __metadata: languageName: node linkType: hard +"tabbable@npm:^6.0.1": + version: 6.0.1 + resolution: "tabbable@npm:6.0.1" + checksum: 65e378ad69a97416f2fdce34ade11b8ff68b33d9b2d978920a9d285c77e1bb88cb35113a8f00af8c4f0163d788d451a48840a216fa918d6a3f0c554951deb984 + languageName: node + linkType: hard + "tailwindcss@npm:*": version: 3.0.18 resolution: "tailwindcss@npm:3.0.18" From 3185ba912aee6646dc99c44b18a93069484e022d Mon Sep 17 00:00:00 2001 From: D063222 Date: Tue, 20 Dec 2022 18:57:53 +0100 Subject: [PATCH 3/8] [example]: added filter to smart select --- .../src/components/SmartSelectInput.js | 104 +++++++++++------- 1 file changed, 65 insertions(+), 39 deletions(-) diff --git a/apps/exampleapp/src/components/SmartSelectInput.js b/apps/exampleapp/src/components/SmartSelectInput.js index f21a3d523..d1d9fb02a 100644 --- a/apps/exampleapp/src/components/SmartSelectInput.js +++ b/apps/exampleapp/src/components/SmartSelectInput.js @@ -3,7 +3,6 @@ import { useFloating, autoUpdate, offset, - flip, shift, useDismiss, useRole, @@ -14,49 +13,66 @@ import { } from "@floating-ui/react" import { TextInputRow, + TextInput, DataGrid, DataGridRow, DataGridCell, Badge, } from "juno-ui-components" -const optionsContainer = (isOpen) => { - return ` - max-h-64 - overflow-y-scroll - outline-none - ring-2 - ring-theme-focus - bg-theme-textinput - ` - .replace(/\n/g, " ") - .replace(/\s+/g, " ") -} +const optionsContainer = ` + smart-select-options-container + max-h-64 + overflow-y-scroll + outline-none + rounded-3px + ring-2 + ring-theme-focus + bg-theme-textinput + ` + +const optionFilter = ` + smart-select-options-filter + p-3 + bg-theme-background-lvl-2 +` const optionsRow = ` + smart-select-options-row hover:text-theme-accent ` -const fakeInputText = ` - text-theme-textinput - bg-theme-textinput - min-h-[2.5rem] - rounded-3px - p-2 -` +const fakeInputText = (isOpen) => { + return ` + smart-select-input + text-theme-textinput + bg-theme-textinput + min-h-[2.5rem] + rounded-3px + p-2 + ${isOpen && `ring-2 ring-theme-focus`} + ` + .replace(/\n/g, " ") + .replace(/\s+/g, " ") +} const fakeInputTextPlaceholder = ` + smart-select-input-placeholder opacity-50 ` const fakeInputTextOptions = ` + smart-select-input-selected-option mr-1 ` +const regexString = (string) => string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&") + const SmartSelectInput = ({ options }) => { const [open, setOpen] = useState(false) const [selectedOptions, setSelectedOptions] = useState([]) const [displayOptions, setDisplayOptions] = useState(false) + const [searchTerm, setSearchTerm] = useState("") options = useMemo(() => { if (!options) return [] @@ -65,19 +81,28 @@ const SmartSelectInput = ({ options }) => { useEffect(() => { if (options) { + // remove choosen options const difference = options.filter( ({ value: id1 }) => !selectedOptions.some(({ value: id2 }) => id2 === id1) ) - setDisplayOptions(difference) + // filter + const regex = new RegExp(regexString(searchTerm.trim()), "i") + const filteredOptions = difference.filter( + (i) => `${i.label}`.search(regex) >= 0 + ) + + console.log("filteredOptions ", filteredOptions) + + setDisplayOptions(filteredOptions) } - }, [selectedOptions, options]) + }, [selectedOptions, options, searchTerm]) const { x, y, reference, floating, strategy, context } = useFloating({ open, placement: "bottom-start", onOpenChange: setOpen, - middleware: [offset(5), shift()], + middleware: [offset(8), shift()], whileElementsMounted: autoUpdate, }) @@ -93,14 +118,7 @@ const SmartSelectInput = ({ options }) => { const headingId = useId() - // =============== - - const onChange = (event) => { - setOpen(true) - } - const onOptionClicked = (option) => { - console.log("onOptionClicked: ", option) setSelectedOptions([...selectedOptions, option]) } @@ -116,18 +134,14 @@ const SmartSelectInput = ({ options }) => { setSelectedOptions(newOptions) } - console.log("selectedOptions: ", selectedOptions) + const onSearchTermChanges = (event) => { + setSearchTerm(event.target.value) + } return (
- {/* */} -
+
{selectedOptions.length > 0 ? ( <> {selectedOptions.map((item, index) => ( @@ -150,7 +164,7 @@ const SmartSelectInput = ({ options }) => {
{ aria-labelledby={headingId} {...getFloatingProps()} > +
+ +
{displayOptions.length > 0 && ( <> @@ -173,6 +194,11 @@ const SmartSelectInput = ({ options }) => { ))} )} + {displayOptions.length === 0 && searchTerm && ( + + No options found + + )}
From 5bde27da46d7bd2bbc9ab04945ea7aab7a9bd371 Mon Sep 17 00:00:00 2001 From: D063222 Date: Tue, 20 Dec 2022 19:05:05 +0100 Subject: [PATCH 4/8] [exampleapp]: smart select options container width --- .../exampleapp/src/components/SmartSelectInput.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/exampleapp/src/components/SmartSelectInput.js b/apps/exampleapp/src/components/SmartSelectInput.js index d1d9fb02a..fa7b7b90e 100644 --- a/apps/exampleapp/src/components/SmartSelectInput.js +++ b/apps/exampleapp/src/components/SmartSelectInput.js @@ -10,6 +10,7 @@ import { useInteractions, FloatingFocusManager, useId, + size, } from "@floating-ui/react" import { TextInputRow, @@ -102,7 +103,19 @@ const SmartSelectInput = ({ options }) => { open, placement: "bottom-start", onOpenChange: setOpen, - middleware: [offset(8), shift()], + middleware: [ + offset(8), + shift(), + size({ + apply({ rects, elements, availableHeight }) { + Object.assign(elements.floating.style, { + maxHeight: `${availableHeight}px`, + width: `${rects.reference.width}px`, + }) + }, + padding: 10, + }), + ], whileElementsMounted: autoUpdate, }) From d7f0300b75af793f8c33a66302c9864cde81250f Mon Sep 17 00:00:00 2001 From: D063222 Date: Tue, 20 Dec 2022 19:31:59 +0100 Subject: [PATCH 5/8] [exampleapp]: sticky filter --- .../src/components/SmartSelectInput.js | 58 +++++++------- apps/exampleapp/src/components/TabPanelTwo.js | 76 +++++++++++++++++++ 2 files changed, 107 insertions(+), 27 deletions(-) diff --git a/apps/exampleapp/src/components/SmartSelectInput.js b/apps/exampleapp/src/components/SmartSelectInput.js index fa7b7b90e..3656c5ee4 100644 --- a/apps/exampleapp/src/components/SmartSelectInput.js +++ b/apps/exampleapp/src/components/SmartSelectInput.js @@ -23,9 +23,7 @@ import { const optionsContainer = ` smart-select-options-container - max-h-64 overflow-y-scroll - outline-none rounded-3px ring-2 ring-theme-focus @@ -36,6 +34,12 @@ const optionFilter = ` smart-select-options-filter p-3 bg-theme-background-lvl-2 + sticky + top-0 +` + +const optionsGrid = ` + ` const optionsRow = ` @@ -82,19 +86,17 @@ const SmartSelectInput = ({ options }) => { useEffect(() => { if (options) { - // remove choosen options + // compute difference between the given options and the selected so + // the same option can't be selected more then one time const difference = options.filter( ({ value: id1 }) => !selectedOptions.some(({ value: id2 }) => id2 === id1) ) - // filter + // filter the difference with the filter string given by the user const regex = new RegExp(regexString(searchTerm.trim()), "i") const filteredOptions = difference.filter( (i) => `${i.label}`.search(regex) >= 0 ) - - console.log("filteredOptions ", filteredOptions) - setDisplayOptions(filteredOptions) } }, [selectedOptions, options, searchTerm]) @@ -193,26 +195,28 @@ const SmartSelectInput = ({ options }) => { onChange={onSearchTermChanges} />
- - {displayOptions.length > 0 && ( - <> - {displayOptions.map((option, i) => ( - onOptionClicked(option)} - className={optionsRow} - > - {option.value} - - ))} - - )} - {displayOptions.length === 0 && searchTerm && ( - - No options found - - )} - +
+ + {displayOptions.length > 0 && ( + <> + {displayOptions.map((option, i) => ( + onOptionClicked(option)} + className={optionsRow} + > + {option.value} + + ))} + + )} + {displayOptions.length === 0 && searchTerm && ( + + No options found + + )} + +
)} diff --git a/apps/exampleapp/src/components/TabPanelTwo.js b/apps/exampleapp/src/components/TabPanelTwo.js index 8771bb481..efeef463e 100644 --- a/apps/exampleapp/src/components/TabPanelTwo.js +++ b/apps/exampleapp/src/components/TabPanelTwo.js @@ -100,6 +100,82 @@ const TabPanelTwo = ({}) => { tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed + consequat, leo eget bibendum sodales, augue velit cursus nunc, Lorem ipsum + dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget + dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient + montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, + pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec + pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim + justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum + felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus + elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo + ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem + ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla + ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies + nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam + rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper + libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit + vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante + tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam + quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed + fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed + consequat, leo eget bibendum sodales, augue velit cursus nunc, Lorem ipsum + dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget + dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient + montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, + pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec + pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim + justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum + felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus + elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo + ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem + ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla + ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies + nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam + rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper + libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit + vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante + tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam + quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed + fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed + consequat, leo eget bibendum sodales, augue velit cursus nunc, Lorem ipsum + dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget + dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient + montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, + pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec + pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim + justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum + felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus + elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo + ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem + ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla + ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies + nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam + rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper + libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit + vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante + tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam + quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed + fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed + consequat, leo eget bibendum sodales, augue velit cursus nunc, Lorem ipsum + dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget + dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient + montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, + pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec + pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim + justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum + felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus + elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo + ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem + ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla + ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies + nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam + rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper + libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit + vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante + tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam + quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed + fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, ) From 5648617a36ff61f5e8dd9fe1b5aae7fb9f791fa4 Mon Sep 17 00:00:00 2001 From: D063222 Date: Wed, 21 Dec 2022 13:13:35 +0100 Subject: [PATCH 6/8] [exampleapp] prevent close dropdown on click reference --- .../src/components/SmartSelectInput.js | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/apps/exampleapp/src/components/SmartSelectInput.js b/apps/exampleapp/src/components/SmartSelectInput.js index 3656c5ee4..9390c3b4a 100644 --- a/apps/exampleapp/src/components/SmartSelectInput.js +++ b/apps/exampleapp/src/components/SmartSelectInput.js @@ -121,8 +121,13 @@ const SmartSelectInput = ({ options }) => { whileElementsMounted: autoUpdate, }) - const click = useClick(context) - const dismiss = useDismiss(context) + const click = useClick(context, { + toggle: true, + event: "mousedown", + }) + const dismiss = useDismiss(context, { + referencePress: true, + }) const role = useRole(context) const { getReferenceProps, getFloatingProps } = useInteractions([ @@ -155,26 +160,29 @@ const SmartSelectInput = ({ options }) => { return (
-
-
- {selectedOptions.length > 0 ? ( - <> - {selectedOptions.map((item, index) => ( - onOptionDeselected(item)} - /> - ))} - - ) : ( -
Select...
- )} -
+
+ {selectedOptions.length > 0 ? ( + <> + {selectedOptions.map((item, index) => ( + onOptionDeselected(item)} + /> + ))} + + ) : ( +
Select...
+ )}
+ {open && (
Date: Wed, 11 Jan 2023 14:57:39 +0100 Subject: [PATCH 7/8] [example-app] reinstall floating ui --- apps/exampleapp/package.json | 2 +- yarn.lock | 44 +++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/apps/exampleapp/package.json b/apps/exampleapp/package.json index a1cd470b7..b0104ddc6 100644 --- a/apps/exampleapp/package.json +++ b/apps/exampleapp/package.json @@ -8,7 +8,7 @@ "@babel/core": "^7.20.2", "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", - "@floating-ui/react": "^0.14.0", + "@floating-ui/react": "^0.17.0", "@svgr/webpack": "^6.2.1", "@testing-library/dom": "^8.19.0", "@testing-library/jest-dom": "^5.16.5", diff --git a/yarn.lock b/yarn.lock index 4854f099e..383266ed1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3120,6 +3120,13 @@ __metadata: languageName: node linkType: hard +"@floating-ui/core@npm:^1.0.5": + version: 1.1.0 + resolution: "@floating-ui/core@npm:1.1.0" + checksum: ac48969915247320e52d173480c224e2ded94d557ba4cc504547bb314d126348dcc0aeef05686673e1b289596e6ce15118edc84900dd310c613d805f83b4e27d + languageName: node + linkType: hard + "@floating-ui/dom@npm:^1.0.5": version: 1.0.12 resolution: "@floating-ui/dom@npm:1.0.12" @@ -3129,6 +3136,15 @@ __metadata: languageName: node linkType: hard +"@floating-ui/dom@npm:^1.1.0": + version: 1.1.0 + resolution: "@floating-ui/dom@npm:1.1.0" + dependencies: + "@floating-ui/core": ^1.0.5 + checksum: 717551da6f470101cd1de0edc449b229fade7f94c2ff98d09e14ced041e27092aac94bd78756c4247a42b57129f187292f145f0001a81ece399a89b20b4be60b + languageName: node + linkType: hard + "@floating-ui/react-dom-interactions@npm:^0.10.1": version: 0.10.3 resolution: "@floating-ui/react-dom-interactions@npm:0.10.3" @@ -3154,6 +3170,32 @@ __metadata: languageName: node linkType: hard +"@floating-ui/react-dom@npm:^1.2.0": + version: 1.2.0 + resolution: "@floating-ui/react-dom@npm:1.2.0" + dependencies: + "@floating-ui/dom": ^1.1.0 + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 7c88eb27d33b910ad90b2aea66f90dcf3857f826ef9b403772693a0e3c91a02e56db123e48ecc2be502c3dc436e5d3bc7c974373515f38d4776fb1a838191990 + languageName: node + linkType: hard + +"@floating-ui/react@npm:^0.17.0": + version: 0.17.0 + resolution: "@floating-ui/react@npm:0.17.0" + dependencies: + "@floating-ui/react-dom": ^1.2.0 + aria-hidden: ^1.1.3 + tabbable: ^6.0.1 + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: e9729a7825a612df88f588a2299375d4ce7f64a810b32e9f38be4c5f5332c164e24c346e101321ca33c3171afbeced554f7cdbaea19bf67cf31b4af811d433eb + languageName: node + linkType: hard + "@gar/promisify@npm:^1.0.1": version: 1.1.2 resolution: "@gar/promisify@npm:1.1.2" @@ -12373,7 +12415,7 @@ __metadata: "@babel/core": ^7.20.2 "@babel/preset-env": ^7.20.2 "@babel/preset-react": ^7.18.6 - "@floating-ui/react": ^0.14.0 + "@floating-ui/react": ^0.17.0 "@svgr/webpack": ^6.2.1 "@testing-library/dom": ^8.19.0 "@testing-library/jest-dom": ^5.16.5 From 672e037453a147265c2fead26c0d817dc036a8e1 Mon Sep 17 00:00:00 2001 From: D063222 Date: Thu, 12 Jan 2023 15:03:52 +0100 Subject: [PATCH 8/8] synch with elektra implementation --- .../src/components/SmartSelectInput.js | 166 +++++++++++------- 1 file changed, 102 insertions(+), 64 deletions(-) diff --git a/apps/exampleapp/src/components/SmartSelectInput.js b/apps/exampleapp/src/components/SmartSelectInput.js index 9390c3b4a..f09224c46 100644 --- a/apps/exampleapp/src/components/SmartSelectInput.js +++ b/apps/exampleapp/src/components/SmartSelectInput.js @@ -14,11 +14,15 @@ import { } from "@floating-ui/react" import { TextInputRow, - TextInput, DataGrid, DataGridRow, DataGridCell, Badge, + LoadingIndicator, + Stack, + Message, + Button, + Spinner, } from "juno-ui-components" const optionsContainer = ` @@ -37,9 +41,8 @@ const optionFilter = ` sticky top-0 ` - -const optionsGrid = ` - +const optionFilterInput = ` + w-full ` const optionsRow = ` @@ -73,34 +76,43 @@ const fakeInputTextOptions = ` const regexString = (string) => string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&") -const SmartSelectInput = ({ options }) => { +const SmartSelectInput = ({ + options, + isLoading, + error, + fetchPromise, + isFetching, + fetchStatus, + fetchButton, + onOptionClick, + onOptionRemove, + selectedOptions, + searchTerm, + onSearchTermChange, +}) => { const [open, setOpen] = useState(false) - const [selectedOptions, setSelectedOptions] = useState([]) - const [displayOptions, setDisplayOptions] = useState(false) - const [searchTerm, setSearchTerm] = useState("") + // set default to empty string if not given options = useMemo(() => { if (!options) return [] return options }, [options]) - useEffect(() => { - if (options) { - // compute difference between the given options and the selected so - // the same option can't be selected more then one time - const difference = options.filter( - ({ value: id1 }) => - !selectedOptions.some(({ value: id2 }) => id2 === id1) - ) - // filter the difference with the filter string given by the user - const regex = new RegExp(regexString(searchTerm.trim()), "i") - const filteredOptions = difference.filter( - (i) => `${i.label}`.search(regex) >= 0 - ) - setDisplayOptions(filteredOptions) - } - }, [selectedOptions, options, searchTerm]) + // set default to empty string if not given + searchTerm = useMemo(() => { + if (!searchTerm) return "" + return searchTerm + }, [searchTerm]) + + // set default to empty array if not given + selectedOptions = useMemo(() => { + if (!selectedOptions) return [] + return selectedOptions + }, [selectedOptions]) + // + // Set up Floating ui + // const { x, y, reference, floating, strategy, context } = useFloating({ open, placement: "bottom-start", @@ -138,24 +150,15 @@ const SmartSelectInput = ({ options }) => { const headingId = useId() + // + // Callbacks + // const onOptionClicked = (option) => { - setSelectedOptions([...selectedOptions, option]) + if (onOptionClick) onOptionClick(option) } const onOptionDeselected = (option) => { - const index = selectedOptions.findIndex( - (item) => item.value == option.value - ) - if (index < 0) { - return - } - let newOptions = selectedOptions.slice() - newOptions.splice(index, 1) - setSelectedOptions(newOptions) - } - - const onSearchTermChanges = (event) => { - setSearchTerm(event.target.value) + if (onOptionRemove) onOptionRemove(option) } return ( @@ -197,34 +200,69 @@ const SmartSelectInput = ({ options }) => { {...getFloatingProps()} >
- -
-
- - {displayOptions.length > 0 && ( - <> - {displayOptions.map((option, i) => ( - onOptionClicked(option)} - className={optionsRow} - > - {option.value} - - ))} - - )} - {displayOptions.length === 0 && searchTerm && ( - - No options found - + + + {searchTerm && fetchPromise && ( + + {fetchStatus && ( + + {fetchStatus} + + )} + {isFetching && } + {fetchButton && fetchButton} + )} - +
+ + {error && ( + + + + + + )} + + {isLoading && ( + + + + + Loading Options... + + + + )} + + {options.length > 0 && ( + <> + {options.map((option, i) => ( + onOptionClicked(option)} + className={optionsRow} + > + {option.label} + + ))} + + )} + + {(!options || options.length === 0) && ( + + + No options available + + + )} +
)}