Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: ['8.3', '8.4', '8.5']
php-version: ['8.4', '8.5']

steps:
- name: Checkout code
Expand Down
31 changes: 18 additions & 13 deletions app/Http/Controllers/FontsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,30 @@

namespace App\Http\Controllers;

use App\Models\Font;
use App\Services\FontService;
use Inertia\Inertia;
use Illuminate\Support\Str;

class FontsController extends Controller
{
public function index()
public function index(FontService $fontService)
{
$fonts = collect($fontService->getFonts())->map(fn ($f) => [
'id' => $f['id'],
'name' => "font-{$f['id']}",
'title' => $f['family'],
'fontFamily' => "'{$f['family']}', {$f['category']}",
'fontProvider' => $f['type'],
'fontImport' => Str::studly($f['family']),
'fontVariable' => "--font-{$f['id']}",
'fontWeight' => $f['weights'],
'fontSubsets' => $f['subsets'],
'fontDependency' => ($f['variable'] ?? false) ? "@fontsource-variable/{$f['id']}" : "@fontsource/{$f['id']}",
'category' => $f['category'],
])->values();

return Inertia::render('fonts/index', [
'fonts' => Font::query()->orderBy('title')->get()->map(fn ($f) => [
'name' => $f->name,
'title' => $f->title,
'fontFamily' => $f->font_family,
'fontProvider' => $f->font_provider,
'fontImport' => $f->font_import,
'fontVariable' => $f->font_variable,
'fontWeight' => $f->font_weight,
'fontSubsets' => $f->font_subsets,
'fontDependency' => $f->font_dependency,
]),
'fonts' => $fonts,
]);
}
}
27 changes: 24 additions & 3 deletions app/Http/Controllers/RegistriesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
use App\Models\Font;
use App\Models\Registry;
use App\Models\Theme;
use App\Services\FontService;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Str;

class RegistriesController extends Controller
{
public function show(string $type, string $name): JsonResponse
public function show(string $type, string $name, FontService $fontService): JsonResponse
{
$model = match ($type) {
'fonts' => Font::class,
Expand All @@ -20,16 +22,35 @@ public function show(string $type, string $name): JsonResponse
};

if ($name === 'registry') {
$items = $model::all()->map->toRegistry()->values();
$items = $model::all()->map->toRegistry();

if ($type === 'fonts') {
$fonts = collect($fontService->getFonts())->map(fn ($f) => $fontService->toRegistry($f));
$items = $items->merge($fonts);
}

return response()->json([
'$schema' => 'https://ui.shadcn.com/schema/registry.json',
'name' => 'designbycode',
'homepage' => 'https://ui.designbycode.co.za',
'items' => $items->all(),
'items' => $items->values()->all(),
]);
}

if ($type === 'fonts') {
$font = Font::where('name', $name)->first();
if ($font) {
return response()->json($font->toRegistry());
}

$fontId = Str::after($name, 'font-');
$fontData = $fontService->getFont($fontId);

if ($fontData) {
return response()->json($fontService->toRegistry($fontData));
}
}

return response()->json(
$model::where('name', $name)->firstOrFail()->toRegistry()
);
Expand Down
6 changes: 5 additions & 1 deletion app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ protected function configureDefaults(): void

protected function shareThemes(): void
{
if (! Schema::hasTable('themes')) {
try {
if (! Schema::hasTable('themes')) {
return;
}
} catch (\Exception) {
return;
}

Expand Down
63 changes: 63 additions & 0 deletions app/Services/FontService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace App\Services;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;

class FontService
{
protected string $baseUrl = 'https://api.fontsource.org/v1';

public function getFonts(): array
{
return Cache::remember('fonts_list', 86400, function () {
$response = Http::get("{$this->baseUrl}/fonts");
if ($response->failed()) {
return [];
}
return $response->json();
});
}

public function getFont(string $id): ?array
{
return Cache::remember("font_details_{$id}", 86400, function () use ($id) {
$response = Http::get("{$this->baseUrl}/fonts/{$id}");
if ($response->failed()) {
return null;
}
return $response->json();
});
}

public function toRegistry(array $fontData): array
{
$id = $fontData['id'];
$family = $fontData['family'];
$importName = Str::studly($family);
$variableName = "--font-{$id}";
$isVariable = $fontData['variable'] ?? false;
$dependency = $isVariable ? "@fontsource-variable/{$id}" : "@fontsource/{$id}";

return [
'name' => "font-{$id}",
'title' => $family,
'type' => 'registry:font',
'meta' => [
'version' => '1.0.0',
'category' => 'fonts'
],
'author' => 'designbycode',
'font' => [
'family' => "'{$family}', sans-serif",
'provider' => 'google',
'import' => $importName,
'variable' => $variableName,
'subsets' => $fontData['subsets'] ?? ['latin'],
'dependency' => $dependency
]
];
}
}
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
],
"license": "MIT",
"require": {
"php": "^8.3",
"php": "^8.4",
"inertiajs/inertia-laravel": "^3.0",
"laravel/cashier-paddle": "^2.8",
"laravel/fortify": "^1.34",
Expand Down
36 changes: 36 additions & 0 deletions resources/js/hooks/use-in-view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useEffect, useState, useRef } from 'react';

export interface UseInViewOptions extends IntersectionObserverInit {
triggerOnce?: boolean;
}

export function useInView(options?: UseInViewOptions) {
const [inView, setInView] = useState(false);
const ref = useRef<any>(null);

useEffect(() => {
const currentRef = ref.current;
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
setInView(true);
if (options?.triggerOnce) {
observer.unobserve(entry.target);
}
} else if (!options?.triggerOnce) {
setInView(false);
}
}, options);

if (currentRef) {
observer.observe(currentRef);
}

return () => {
if (currentRef) {
observer.unobserve(currentRef);
}
};
}, [options]);

return { ref, inView };
}
2 changes: 0 additions & 2 deletions resources/js/layouts/main-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { usePage } from '@inertiajs/react';
import { PlaceholderPattern } from '@/components/ui/placeholder-pattern';
import MainFooter from '@/layouts/main/main-footer';
import MainNavigation from '@/layouts/main/main-navigation';
import { GlowStack } from '@/registry/new-york/components/ui/glow/glow-stack';
Expand Down
11 changes: 5 additions & 6 deletions resources/js/layouts/main/theme/main-editor-block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export default function MainEditorBlock({
// ── Mount ────────────────────────────────────────────────────────────────

useEffect(() => {
// eslint-disable-next-line react-hooks/set-state-in-effect
setMounted(true);
}, []);

Expand Down Expand Up @@ -255,9 +256,9 @@ export default function MainEditorBlock({
);
}

// ── Controls ──────────────────────────────────────────────────────────────
// ── Render ────────────────────────────────────────────────────────────────

const Controls = () => (
const controls = (
<div className="flex items-center gap-1">
{showCopyButton && (
<div className="flex items-center gap-1">
Expand Down Expand Up @@ -308,8 +309,6 @@ export default function MainEditorBlock({
</div>
);

// ── Render ────────────────────────────────────────────────────────────────

return (
<div
ref={containerRef}
Expand All @@ -326,13 +325,13 @@ export default function MainEditorBlock({
<span className="font-mono text-sm font-bold text-muted-foreground">
{normalizedLanguage}
</span>
<Controls />
{controls}
</div>
)}

{variant === 'minimal' && (
<div className="absolute top-2 right-2 z-10 opacity-0 transition-opacity group-hover/editor-block:opacity-100">
<Controls />
{controls}
</div>
)}

Expand Down
11 changes: 9 additions & 2 deletions resources/js/layouts/main/theme/main-package-manager-search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,20 @@ export function MainPackageManagerSearch({
const initialName = defaultRegistry ?? extractRegistryName(initialCodes);
const registryName = selectedRegistry || initialName;

useEffect(() => {
const [lastQuery, setLastQuery] = useState('');
if (searchQuery !== lastQuery) {
setLastQuery(searchQuery);
if (!searchQuery || searchQuery.length < 2) {
setResults((prev) => (prev.length > 0 ? [] : prev));
setResults([]);
}
}

useEffect(() => {
if (!searchQuery || searchQuery.length < 2) {
return;
}

// eslint-disable-next-line react-hooks/set-state-in-effect
setLoading(true);
const params = new URLSearchParams({ q: searchQuery });
fetch(`/api/registries/search?${params}`)
Expand Down
12 changes: 8 additions & 4 deletions resources/js/layouts/main/theme/main-registry-installer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@ function MainRegistryInstaller({
}) {
const { url } = usePage().props;

const installerCode = code.startsWith('fonts/font-')
? `@designbycode/${code.replace('fonts/', '')}`
: `${url}/r/${code}.json`;

return (
<PackageManagerCode
className={cn(``, className)}
codes={{
bun: `bunx --bun shadcn@latest add ${url}/r/${code}.json`,
npm: `npx shadcn@latest add ${url}/r/${code}.json`,
pnpm: `pnpm dlx shadcn@latest add ${url}/r/${code}.json`,
yarn: `yarn dlx shadcn@latest add ${url}/r/${code}.json`,
bun: `bunx --bun shadcn@latest add ${installerCode}`,
npm: `npx shadcn@latest add ${installerCode}`,
pnpm: `pnpm dlx shadcn@latest add ${installerCode}`,
yarn: `yarn dlx shadcn@latest add ${installerCode}`,
}}
/>
);
Expand Down
7 changes: 5 additions & 2 deletions resources/js/layouts/main/theme/main-theme-switcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,21 @@ function MainThemeSwitcher() {
[],
);

useEffect(() => {
const [lastOpen, setLastOpen] = useState(false);
if (open !== lastOpen) {
setLastOpen(open);
if (!open) {
setSearchInput('');
setActiveSearch('');
setThemes([]);
setPage(1);
setHasMore(true);
}
}, [open]);
}

useEffect(() => {
if (open) {
// eslint-disable-next-line react-hooks/set-state-in-effect
fetchPage(1, '', false);
}
}, [open, fetchPage]);
Expand Down
Loading