chore: update packages to latest version. feat: replace hashicon lib with vanilla component
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { Avatar } from "@components/UI/Avatar";
|
||||
import {
|
||||
CommandDialog,
|
||||
CommandEmpty,
|
||||
@@ -8,7 +9,6 @@ import {
|
||||
} from "@components/UI/Command.tsx";
|
||||
import { useAppStore } from "@core/stores/appStore.ts";
|
||||
import { useDevice, useDeviceStore } from "@core/stores/deviceStore.ts";
|
||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||
import { useCommandState } from "cmdk";
|
||||
import {
|
||||
ArrowLeftRightIcon,
|
||||
@@ -51,11 +51,11 @@ export interface Command {
|
||||
|
||||
export interface SubItem {
|
||||
label: string;
|
||||
icon: JSX.Element;
|
||||
icon: React.ReactNode;
|
||||
action: () => void;
|
||||
}
|
||||
|
||||
export const CommandPalette = (): JSX.Element => {
|
||||
export const CommandPalette = () => {
|
||||
const {
|
||||
commandPaletteOpen,
|
||||
setCommandPaletteOpen,
|
||||
@@ -125,9 +125,11 @@ export const CommandPalette = (): JSX.Element => {
|
||||
device.nodes.get(device.hardware.myNodeNum)?.user?.longName ??
|
||||
device.hardware.myNodeNum.toString(),
|
||||
icon: (
|
||||
<Hashicon
|
||||
size={16}
|
||||
value={device.hardware.myNodeNum.toString()}
|
||||
<Avatar
|
||||
text={
|
||||
device.nodes.get(device.hardware.myNodeNum)?.user
|
||||
?.shortName ?? device.hardware.myNodeNum.toString()
|
||||
}
|
||||
/>
|
||||
),
|
||||
action() {
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Separator } from "@components/UI/Seperator.tsx";
|
||||
import { Code } from "@components/UI/Typography/Code.tsx";
|
||||
import { useAppStore } from "@core/stores/appStore.ts";
|
||||
import { useDeviceStore } from "@core/stores/deviceStore.ts";
|
||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||
import {
|
||||
HomeIcon,
|
||||
LanguagesIcon,
|
||||
@@ -12,6 +11,8 @@ import {
|
||||
SearchIcon,
|
||||
SunIcon,
|
||||
} from "lucide-react";
|
||||
import type { JSX } from "react";
|
||||
import { Avatar } from "./UI/Avatar";
|
||||
|
||||
export const DeviceSelector = (): JSX.Element => {
|
||||
const { getDevices } = useDeviceStore();
|
||||
@@ -44,9 +45,12 @@ export const DeviceSelector = (): JSX.Element => {
|
||||
}}
|
||||
active={selectedDevice === device.id}
|
||||
>
|
||||
<Hashicon
|
||||
size={24}
|
||||
value={device.hardware.myNodeNum.toString()}
|
||||
<Avatar
|
||||
text={
|
||||
device.nodes
|
||||
.get(device.hardware.myNodeNum)
|
||||
?.user?.shortName.toString() ?? "UNK"
|
||||
}
|
||||
/>
|
||||
</DeviceSelectorButton>
|
||||
))}
|
||||
|
||||
@@ -8,15 +8,15 @@ export const DeviceSelectorButton = ({
|
||||
active,
|
||||
onClick,
|
||||
children,
|
||||
}: DeviceSelectorButtonProps): JSX.Element => (
|
||||
}: DeviceSelectorButtonProps) => (
|
||||
<li
|
||||
className="aspect-w-1 aspect-h-1 relative w-full"
|
||||
onClick={onClick}
|
||||
onKeyDown={onClick}
|
||||
>
|
||||
{active && (
|
||||
{/* {active && (
|
||||
<div className="absolute -left-2 h-10 w-1.5 rounded-full bg-accent" />
|
||||
)}
|
||||
)} */}
|
||||
<div className="flex aspect-square cursor-pointer flex-col items-center justify-center">
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -51,7 +51,7 @@ export function SelectInput<T extends FieldValues>({
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{optionsEnumValues.map(([name, value]) => (
|
||||
<SelectItem key={name} value={value.toString()}>
|
||||
<SelectItem key={name + value} value={value.toString()}>
|
||||
{formatEnumName
|
||||
? name
|
||||
.replace(/_/g, " ")
|
||||
|
||||
@@ -15,9 +15,9 @@ export const FieldWrapper = ({
|
||||
children,
|
||||
valid,
|
||||
validationText,
|
||||
}: FieldWrapperProps): JSX.Element => (
|
||||
}: FieldWrapperProps) => (
|
||||
<div className="pt-6 sm:pt-5">
|
||||
<div role="group" aria-labelledby="label-notifications">
|
||||
<fieldset aria-labelledby="label-notifications">
|
||||
<div className="sm:grid sm:grid-cols-3 sm:items-baseline sm:gap-4">
|
||||
<Label>{label}</Label>
|
||||
<div className="sm:col-span-2">
|
||||
@@ -32,6 +32,6 @@ export const FieldWrapper = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Separator } from "@app/components/UI/Seperator";
|
||||
import { H5 } from "@app/components/UI/Typography/H5.tsx";
|
||||
import { Subtle } from "@app/components/UI/Typography/Subtle.tsx";
|
||||
import { Avatar } from "@components/UI/Avatar";
|
||||
import { Mono } from "@components/generic/Mono.tsx";
|
||||
import { TimeAgo } from "@components/generic/Table/tmp/TimeAgo.tsx";
|
||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||
import { Protobuf } from "@meshtastic/js";
|
||||
import type { Protobuf as ProtobufType } from "@meshtastic/js";
|
||||
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
||||
@@ -23,7 +23,7 @@ export interface NodeDetailProps {
|
||||
node: ProtobufType.Mesh.NodeInfo;
|
||||
}
|
||||
|
||||
export const NodeDetail = ({ node }: NodeDetailProps): JSX.Element => {
|
||||
export const NodeDetail = ({ node }: NodeDetailProps) => {
|
||||
const name = node.user?.longName || `!${numberToHexUnpadded(node.num)}`;
|
||||
const hardwareType = Protobuf.Mesh.HardwareModel[
|
||||
node.user?.hwModel ?? 0
|
||||
@@ -33,7 +33,7 @@ export const NodeDetail = ({ node }: NodeDetailProps): JSX.Element => {
|
||||
<div className="dark:text-black">
|
||||
<div className="flex gap-2">
|
||||
<div className="flex flex-col items-center gap-2 min-w-6 pt-1">
|
||||
<Hashicon value={node.num.toString()} size={22} />
|
||||
<Avatar text={node.user?.shortName} />
|
||||
|
||||
<div>
|
||||
{node.user?.publicKey && node.user?.publicKey.length > 0 ? (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { MessageWithState } from "@app/core/stores/deviceStore.ts";
|
||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||
import { Avatar } from "@components/UI/Avatar";
|
||||
import type { Protobuf } from "@meshtastic/js";
|
||||
import {
|
||||
AlertCircleIcon,
|
||||
@@ -13,11 +13,7 @@ export interface MessageProps {
|
||||
sender?: Protobuf.Mesh.NodeInfo;
|
||||
}
|
||||
|
||||
export const Message = ({
|
||||
lastMsgSameUser,
|
||||
message,
|
||||
sender,
|
||||
}: MessageProps): JSX.Element => {
|
||||
export const Message = ({ lastMsgSameUser, message, sender }: MessageProps) => {
|
||||
return lastMsgSameUser ? (
|
||||
<div className="ml-5 flex">
|
||||
{message.state === "ack" ? (
|
||||
@@ -39,7 +35,7 @@ export const Message = ({
|
||||
<div className="mx-4 mt-2 gap-2">
|
||||
<div className="flex gap-2">
|
||||
<div className="w-6 cursor-pointer">
|
||||
<Hashicon value={(sender?.num ?? 0).toString()} size={32} />
|
||||
<Avatar text={sender?.user?.shortName ?? "UNK"} />
|
||||
</div>
|
||||
<span className="cursor-pointer font-medium text-textPrimary">
|
||||
{sender?.user?.longName ?? "UNK"}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import { cn } from "@app/core/utils/cn";
|
||||
import type React from "react";
|
||||
|
||||
type RGBColor = {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
};
|
||||
|
||||
interface AvatarProps {
|
||||
text: string;
|
||||
size?: "sm" | "lg";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
// biome-ignore lint/complexity/noStaticOnlyClass: stop being annoying Biome
|
||||
class ColorUtils {
|
||||
static hexToRgb(hex: number): RGBColor {
|
||||
return {
|
||||
r: (hex & 0xff0000) >> 16,
|
||||
g: (hex & 0x00ff00) >> 8,
|
||||
b: hex & 0x0000ff,
|
||||
a: 255,
|
||||
};
|
||||
}
|
||||
|
||||
static rgbToHex(color: RGBColor): number {
|
||||
return (
|
||||
(Math.round(color.a) << 24) |
|
||||
(Math.round(color.r) << 16) |
|
||||
(Math.round(color.g) << 8) |
|
||||
Math.round(color.b)
|
||||
);
|
||||
}
|
||||
|
||||
static isLight(color: RGBColor): boolean {
|
||||
const brightness = (color.r * 299 + color.g * 587 + color.b * 114) / 1000;
|
||||
return brightness > 127.5;
|
||||
}
|
||||
}
|
||||
|
||||
export const Avatar: React.FC<AvatarProps> = ({
|
||||
text,
|
||||
size = "sm",
|
||||
className,
|
||||
}) => {
|
||||
const sizes = {
|
||||
sm: "size-11 text-xs",
|
||||
lg: "size-16 text-lg",
|
||||
};
|
||||
|
||||
// Pick a color based on the text provided to function
|
||||
const getColorFromText = (text: string): RGBColor => {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
hash = text.charCodeAt(i) + ((hash << 5) - hash);
|
||||
}
|
||||
|
||||
return {
|
||||
r: (hash & 0xff0000) >> 16,
|
||||
g: (hash & 0x00ff00) >> 8,
|
||||
b: hash & 0x0000ff,
|
||||
a: 255,
|
||||
};
|
||||
};
|
||||
|
||||
const bgColor = getColorFromText(text ?? "UNK");
|
||||
const isLight = ColorUtils.isLight(bgColor);
|
||||
const textColor = isLight ? "#000000" : "#FFFFFF";
|
||||
const initials = text?.toUpperCase().slice(0, 4) ?? "UNK";
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
`
|
||||
rounded-full
|
||||
flex
|
||||
items-center
|
||||
justify-center
|
||||
size-11
|
||||
font-semibold`,
|
||||
sizes[size],
|
||||
className,
|
||||
)}
|
||||
style={{
|
||||
backgroundColor: `rgb(${bgColor.r}, ${bgColor.g}, ${bgColor.b})`,
|
||||
color: textColor,
|
||||
}}
|
||||
>
|
||||
<p className="p-1">{initials}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -9,9 +9,9 @@ export interface SidebarSectionProps {
|
||||
export const SidebarSection = ({
|
||||
label: title,
|
||||
children,
|
||||
}: SidebarSectionProps): JSX.Element => (
|
||||
}: SidebarSectionProps) => (
|
||||
<div className="px-4 py-2">
|
||||
<H4 className="mb-2 ml-2">{title}</H4>
|
||||
<H4 className="mb-3 ml-2">{title}</H4>
|
||||
<div className="space-y-1">{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -74,15 +74,12 @@ export const Table = ({ headings, rows }: TableProps): JSX.Element => {
|
||||
>
|
||||
<div className="flex gap-2">
|
||||
{heading.title}
|
||||
{sortColumn === heading.title && (
|
||||
<>
|
||||
{sortOrder === "asc" ? (
|
||||
<ChevronUpIcon size={16} />
|
||||
) : (
|
||||
<ChevronDownIcon size={16} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{sortColumn === heading.title &&
|
||||
(sortOrder === "asc" ? (
|
||||
<ChevronUpIcon size={16} />
|
||||
) : (
|
||||
<ChevronDownIcon size={16} />
|
||||
))}
|
||||
</div>
|
||||
</th>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user