mirror of
https://github.com/projectsend/projectsend.git
synced 2026-10-03 12:54:18 +00:00
Merge pull request #1807 from projectsend/feat/package-styling-hooks
Let an installed package restyle the staff area and supply its own browser icons
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
import '../css/app.css';
|
||||
|
||||
// Stylesheets an installed package ships under resources/css, the styling
|
||||
// counterpart of the package pages resolved below. Imported after app.css so
|
||||
// a package can restyle what core draws; core names no package and no style.
|
||||
import.meta.glob('../../vendor/*/*/resources/css/*.css', { eager: true });
|
||||
|
||||
import { createInertiaApp, router } from '@inertiajs/react';
|
||||
import axios from 'axios';
|
||||
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
|
||||
|
||||
@@ -7,7 +7,9 @@ export default function AppLogoIcon(props: SVGAttributes<SVGElement>) {
|
||||
const gradientId = useId();
|
||||
|
||||
return (
|
||||
<svg {...props} viewBox="0 0 234.26482 252.25172" xmlns="http://www.w3.org/2000/svg">
|
||||
// Marked so an installed package can restyle the default mark; only
|
||||
// drawn when no logo was uploaded in Branding, which always wins.
|
||||
<svg data-slot="app-logo-default" {...props} viewBox="0 0 234.26482 252.25172" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id={gradientId} gradientUnits="userSpaceOnUse" x1="31.5263" y1="211.0108" x2="197.4637" y2="50.766899">
|
||||
<stop offset="0.1675" stopColor="#5219B3" />
|
||||
|
||||
@@ -18,11 +18,15 @@ export function AppShell({ children, variant = 'header' }: AppShellProps) {
|
||||
};
|
||||
|
||||
if (variant === 'header') {
|
||||
return <div className="flex min-h-screen w-full flex-col">{children}</div>;
|
||||
return (
|
||||
<div data-surface="staff" className="flex min-h-screen w-full flex-col">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SidebarProvider defaultOpen={isOpen} open={isOpen} onOpenChange={handleSidebarChange}>
|
||||
<SidebarProvider data-surface="staff" defaultOpen={isOpen} open={isOpen} onOpenChange={handleSidebarChange}>
|
||||
{children}
|
||||
</SidebarProvider>
|
||||
);
|
||||
|
||||
@@ -9,7 +9,10 @@ import { type BreadcrumbItem as BreadcrumbItemType } from '@/types';
|
||||
|
||||
export function AppSidebarHeader({ breadcrumbs = [] }: { breadcrumbs?: BreadcrumbItemType[] }) {
|
||||
return (
|
||||
<header className="border-sidebar-border/50 flex h-16 shrink-0 items-center gap-2 border-b px-6 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12 md:px-4">
|
||||
<header
|
||||
data-slot="app-header"
|
||||
className="border-sidebar-border/50 flex h-16 shrink-0 items-center gap-2 border-b px-6 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12 md:px-4"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<SidebarTrigger className="-ml-1" />
|
||||
<Breadcrumbs breadcrumbs={breadcrumbs} />
|
||||
|
||||
@@ -23,6 +23,7 @@ export function WidgetBox({ id, title, headerExtra, children }: { id: string; ti
|
||||
<div
|
||||
ref={setNodeRef}
|
||||
style={{ transform: CSS.Transform.toString(transform), transition }}
|
||||
data-slot="card"
|
||||
className={`bg-card rounded-lg border p-4 ${isDragging ? 'z-10 opacity-50' : ''}`}
|
||||
>
|
||||
<div className="mb-3 flex flex-wrap items-center justify-between gap-3">
|
||||
|
||||
@@ -12,7 +12,7 @@ export function ListToolbar({ children, showClear, onClear }: { children: ReactN
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="mb-4 flex flex-wrap items-end gap-3 rounded-lg border p-4">
|
||||
<div data-slot="list-toolbar" className="mb-4 flex flex-wrap items-end gap-3 rounded-lg border p-4">
|
||||
{children}
|
||||
{showClear && (
|
||||
<Button type="button" variant="ghost" onClick={onClear}>
|
||||
|
||||
@@ -9,7 +9,9 @@ export default function ProjectSendLogo(props: SVGAttributes<SVGElement>) {
|
||||
const gradientId = useId();
|
||||
|
||||
return (
|
||||
<svg {...props} viewBox="0 0 1046.5 257" xmlns="http://www.w3.org/2000/svg">
|
||||
// Marked so an installed package can restyle the default wordmark;
|
||||
// only drawn when no logo was uploaded in Branding.
|
||||
<svg data-slot="app-wordmark-default" {...props} viewBox="0 0 1046.5 257" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id={gradientId} gradientUnits="userSpaceOnUse" x1="31.5263" y1="211.0108" x2="197.4637" y2="50.766899">
|
||||
<stop offset="0.1675" stopColor="#5219B3" />
|
||||
|
||||
@@ -24,7 +24,7 @@ interface TableShellProps {
|
||||
*/
|
||||
export function TableShell({ columns, emptyMessage, isEmpty, children }: TableShellProps) {
|
||||
return (
|
||||
<div className="overflow-x-auto rounded-lg border">
|
||||
<div data-slot="table-shell" className="overflow-x-auto rounded-lg border">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="bg-muted/50 border-b text-left">
|
||||
|
||||
@@ -36,7 +36,7 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : 'button';
|
||||
return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
|
||||
return <Comp data-slot="button" data-variant={variant ?? 'default'} className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
|
||||
});
|
||||
Button.displayName = 'Button';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn('rounded-lg border bg-card text-card-foreground shadow-xs', className)} {...props} />
|
||||
<div ref={ref} data-slot="card" className={cn('rounded-lg border bg-card text-card-foreground shadow-xs', className)} {...props} />
|
||||
));
|
||||
Card.displayName = 'Card';
|
||||
|
||||
|
||||
@@ -29,9 +29,16 @@
|
||||
there is nothing on the client that could work the name out. --}}
|
||||
<meta name="xsrf-cookie" content="{{ \App\Http\Middleware\ValidateCsrfToken::cookieName() }}">
|
||||
|
||||
<link rel="icon" href="/favicon.ico" sizes="48x48">
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||
{{-- An installed package may name its own icons in
|
||||
projectsend.icons; they then replace these as a set, so a
|
||||
stray default never outranks one of them in some browser. --}}
|
||||
@forelse (config('projectsend.icons', []) as $icon)
|
||||
<link rel="{{ $icon['rel'] }}" href="{{ $icon['href'] }}"@isset($icon['type']) type="{{ $icon['type'] }}"@endisset @isset($icon['sizes']) sizes="{{ $icon['sizes'] }}"@endisset>
|
||||
@empty
|
||||
<link rel="icon" href="/favicon.ico" sizes="48x48">
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||
@endforelse
|
||||
|
||||
@routes
|
||||
@viteReactRefresh
|
||||
|
||||
Reference in New Issue
Block a user