diff --git a/packages/browser-sdk/example/typescript/app.ts b/packages/browser-sdk/example/typescript/app.ts index e0bb6d9a..718d9782 100644 --- a/packages/browser-sdk/example/typescript/app.ts +++ b/packages/browser-sdk/example/typescript/app.ts @@ -1,6 +1,6 @@ import { ReflagClient, CheckEvent, RawFlags } from "../../src"; -const urlParams = new URLSearchParams(window.location.search); +const urlParams = new URLSearchParams(window?.location?.search); const publishableKey = urlParams.get("publishableKey"); const flagKey = urlParams.get("flagKey") ?? "huddles"; diff --git a/packages/browser-sdk/index.html b/packages/browser-sdk/index.html index 2a61be03..4cb973bc 100644 --- a/packages/browser-sdk/index.html +++ b/packages/browser-sdk/index.html @@ -7,7 +7,7 @@ Reflag Browser SDK - +
Loading... diff --git a/packages/browser-sdk/package.json b/packages/browser-sdk/package.json index af694b28..35807d7e 100644 --- a/packages/browser-sdk/package.json +++ b/packages/browser-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@reflag/browser-sdk", - "version": "1.0.0", + "version": "1.1.0", "packageManager": "yarn@4.1.1", "license": "MIT", "repository": { diff --git a/packages/browser-sdk/src/toolbar/Flags.css b/packages/browser-sdk/src/toolbar/Flags.css index a1a7bcf5..83ec4aeb 100644 --- a/packages/browser-sdk/src/toolbar/Flags.css +++ b/packages/browser-sdk/src/toolbar/Flags.css @@ -4,6 +4,7 @@ color: white; width: 100%; font-size: var(--text-size); + height: 28px; &::placeholder { color: var(--gray500); @@ -44,29 +45,19 @@ } .flag-row { - opacity: 0; - transform: translateY(-7px); - transition-property: opacity, transform; - transition-duration: 0.075s; - transition-timing-function: cubic-bezier(0.75, -0.015, 0.565, 1.055); - - &.show-on-open { - opacity: 1; - transform: translateY(0); - /* stagger effect where first item (i=0) has no delay, - and delay is based on item count (n) so total animation time always is 509ms */ - transition-delay: calc(0.05s * var(--i) / max(var(--n) - 1, 1)); - } - &.not-visible { visibility: hidden; } } -.flag-empty-cell { - width: 100%; +.flags-table-empty { + position: absolute; + top: 0; + left: 0; + right: 0; color: var(--gray500); - padding: 6px 0; + padding: 12px 12px; + line-height: 1.5; } .flag-name-cell { diff --git a/packages/browser-sdk/src/toolbar/Flags.tsx b/packages/browser-sdk/src/toolbar/Flags.tsx index da25650d..e8753c49 100644 --- a/packages/browser-sdk/src/toolbar/Flags.tsx +++ b/packages/browser-sdk/src/toolbar/Flags.tsx @@ -1,27 +1,26 @@ -import { h } from "preact"; -import { useEffect, useState } from "preact/hooks"; +import { Fragment, h } from "preact"; import { Switch } from "./Switch"; import { FlagItem } from "./Toolbar"; +const isFound = (flagKey: string, searchQuery: string | null) => { + return flagKey.toLocaleLowerCase().includes(searchQuery ?? ""); +}; + export function FlagsTable({ flags, searchQuery, appBaseUrl, - isOpen, setIsEnabledOverride, }: { flags: FlagItem[]; searchQuery: string | null; appBaseUrl: string; - isOpen: boolean; setIsEnabledOverride: (key: string, isEnabled: boolean | null) => void; }) { const hasFlags = flags.length > 0; const hasShownFlags = flags.some((flag) => - flag.flagKey - .toLocaleLowerCase() - .includes(searchQuery?.toLocaleLowerCase() ?? ""), + isFound(flag.flagKey, searchQuery), ); // List flags that match the search query first then alphabetically @@ -29,11 +28,23 @@ export function FlagsTable({ searchQuery === null ? flags : [...flags].sort((a, b) => { - const aMatches = a.flagKey.includes(searchQuery); - const bMatches = b.flagKey.includes(searchQuery); + const aMatches = isFound(a.flagKey, searchQuery); + const bMatches = isFound(b.flagKey, searchQuery); // If both match or both don't match, sort alphabetically if (aMatches === bMatches) { + const aStartsWith = a.flagKey + .toLocaleLowerCase() + .startsWith(searchQuery); + const bStartsWith = b.flagKey + .toLocaleLowerCase() + .startsWith(searchQuery); + + // If one starts with search query and the other doesn't, prioritize the one that starts with it + if (aStartsWith && !bStartsWith) return -1; + if (bStartsWith && !aStartsWith) return 1; + + // Otherwise sort alphabetically return a.flagKey.localeCompare(b.flagKey); } @@ -42,36 +53,31 @@ export function FlagsTable({ }); return ( - - - {(!hasFlags || !hasShownFlags) && ( - - - - )} - {searchedFlags.map((flag, index) => ( - - setIsEnabledOverride(flag.flagKey, override) - } - /> - ))} - -
- No flags {!hasShownFlags ? `matching "${searchQuery} "` : ""} - found -
+ + {(!hasFlags || !hasShownFlags) && ( +
+ No flags {hasFlags ? `matching "${searchQuery}"` : "found"} +
+ )} + + + {searchedFlags.map((flag, index) => ( + + setIsEnabledOverride(flag.flagKey, override) + } + /> + ))} + +
+
); } @@ -79,30 +85,19 @@ function FlagRow({ setEnabledOverride, appBaseUrl, flag, - isOpen, index, isNotVisible, }: { flag: FlagItem; appBaseUrl: string; setEnabledOverride: (isEnabled: boolean | null) => void; - isOpen: boolean; index: number; isNotVisible: boolean; }) { - const [showOnOpen, setShowOnOpen] = useState(isOpen); - useEffect(() => { - setShowOnOpen(isOpen); - }, [isOpen]); return ( (null); const [flags, setFlags] = useState([]); + const wasHidden = + window?.sessionStorage?.getItem(TOOLBAR_HIDE_KEY) === "true"; + const [isHidden, setIsHidden] = useState(wasHidden); + const updateFlags = useCallback(() => { const rawFlags = reflagClient.getFlags(); setFlags( @@ -79,6 +85,16 @@ export default function Toolbar({ const { isOpen, close, toggle } = useDialog(); + const hideToolbar = useCallback(() => { + window?.sessionStorage?.setItem(TOOLBAR_HIDE_KEY, "true"); + setIsHidden(true); + close(); + }, [close]); + + if (isHidden) { + return null; + } + return (