diff --git a/src/components/FileExplorer.tsx b/src/components/FileExplorer.tsx index 17d926b44..13d36fc85 100644 --- a/src/components/FileExplorer.tsx +++ b/src/components/FileExplorer.tsx @@ -7,6 +7,7 @@ import jsonIconUrl from '~/images/file-icons/json.svg?url' import svelteIconUrl from '~/images/file-icons/svelte.svg?url' import vueIconUrl from '~/images/file-icons/vue.svg?url' import markoIconUrl from '~/images/file-icons/marko.svg?url' +import emberIconUrl from '~/images/ember-logo.svg?url' import textIconUrl from '~/images/file-icons/txt.svg?url' import type { GitHubFileNode } from '~/utils/documents.server' import { twMerge } from 'tailwind-merge' @@ -33,6 +34,9 @@ const getFileIconPath = (filename: string) => { return vueIconUrl case 'marko': return markoIconUrl + case 'gjs': + case 'gts': + return emberIconUrl default: return textIconUrl } diff --git a/src/components/markdown/codeBlock.shared.tsx b/src/components/markdown/codeBlock.shared.tsx index 2befa457e..ffbbdcc30 100644 --- a/src/components/markdown/codeBlock.shared.tsx +++ b/src/components/markdown/codeBlock.shared.tsx @@ -83,6 +83,8 @@ export function getCodeBlockLanguageFromFilePath(filePath: string) { if (['cts', 'mts'].includes(ext)) return 'ts' if (['cjs', 'mjs'].includes(ext)) return 'js' + if (ext === 'gts') return 'ts' + if (ext === 'gjs') return 'js' if (['prettierrc', 'babelrc', 'webmanifest'].includes(ext)) return 'json' if (['env', 'example'].includes(ext)) return 'sh' diff --git a/src/images/ember-logo.svg b/src/images/ember-logo.svg new file mode 100644 index 000000000..e0a795691 --- /dev/null +++ b/src/images/ember-logo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/libraries/frameworks.tsx b/src/libraries/frameworks.tsx index 683a864e6..c4214b9a8 100644 --- a/src/libraries/frameworks.tsx +++ b/src/libraries/frameworks.tsx @@ -1,5 +1,6 @@ import alpineLogo from '../images/alpine-logo.svg' import angularLogo from '../images/angular-logo.svg' +import emberLogo from '../images/ember-logo.svg' import jsLogo from '../images/js-logo.svg' import litLogo from '../images/lit-logo.svg' import markoLogo from '../images/marko-logo.svg' @@ -40,6 +41,13 @@ export const frameworkOptions = [ color: 'bg-red-500', fontColor: 'text-fuchsia-500', }, + { + label: 'Ember', + value: 'ember', + logo: emberLogo, + color: 'bg-orange-600', + fontColor: 'text-orange-600', + }, { label: 'Solid', value: 'solid', diff --git a/src/libraries/libraries.ts b/src/libraries/libraries.ts index 4e2f763e3..f15ea4216 100644 --- a/src/libraries/libraries.ts +++ b/src/libraries/libraries.ts @@ -314,7 +314,7 @@ export const table: LibrarySlim = { to: '/table', tagline: 'Headless UI for building powerful tables & datagrids', description: - 'Supercharge your tables or build a datagrid from scratch for TS/JS, React, Vue, Solid, Svelte, Qwik, Angular, and Lit while retaining 100% control over markup and styles.', + 'Supercharge your tables or build a datagrid from scratch in any framework while retaining 100% control over markup and styles.', bgStyle: 'bg-blue-500', borderStyle: 'border-blue-500/50', textStyle: 'text-blue-500', @@ -326,6 +326,7 @@ export const table: LibrarySlim = { repo: 'tanstack/table', frameworks: [ 'angular', + 'ember', 'react', 'preact', 'solid', diff --git a/src/libraries/types.ts b/src/libraries/types.ts index 8f452652c..5cec2be84 100644 --- a/src/libraries/types.ts +++ b/src/libraries/types.ts @@ -6,6 +6,7 @@ export type { LibraryId } from './ids' export type Framework = | 'angular' | 'alpine' + | 'ember' | 'lit' | 'marko' | 'preact' diff --git a/src/routes/-libraries-utils.ts b/src/routes/-libraries-utils.ts index 1cb17260e..ef6848c89 100644 --- a/src/routes/-libraries-utils.ts +++ b/src/routes/-libraries-utils.ts @@ -41,3 +41,17 @@ export function getFrameworkLibraryCounts(allLibraries: Array) { return counts } + +export function orderFrameworksForBrowse< + TFramework extends { value: Framework }, +>( + frameworks: ReadonlyArray, + counts: Partial>, +) { + return [...frameworks].sort((a, b) => { + if (a.value === 'vanilla') return 1 + if (b.value === 'vanilla') return -1 + + return (counts[b.value] ?? 0) - (counts[a.value] ?? 0) + }) +} diff --git a/src/routes/libraries.tsx b/src/routes/libraries.tsx index 13c606ab5..802098262 100644 --- a/src/routes/libraries.tsx +++ b/src/routes/libraries.tsx @@ -7,6 +7,7 @@ import LibraryCard from '~/components/LibraryCard' import { getFrameworkLibraryCounts, getVisibleLibraries, + orderFrameworksForBrowse, orderLibrariesForBrowse, } from './-libraries-utils' @@ -30,8 +31,11 @@ function LibrariesPage() { const allLibraries = getVisibleLibraries() const ordered = orderLibrariesForBrowse(allLibraries) const frameworkCounts = getFrameworkLibraryCounts(allLibraries) - const frameworksWithLibraries = frameworkOptions.filter( - (framework) => (frameworkCounts[framework.value] ?? 0) > 0, + const frameworksWithLibraries = orderFrameworksForBrowse( + frameworkOptions.filter( + (framework) => (frameworkCounts[framework.value] ?? 0) > 0, + ), + frameworkCounts, ) const deprecatedLibraries = [reactChartsProject] diff --git a/src/routes/libraries_.$framework.tsx b/src/routes/libraries_.$framework.tsx index 17bf9d67e..917f61166 100644 --- a/src/routes/libraries_.$framework.tsx +++ b/src/routes/libraries_.$framework.tsx @@ -6,6 +6,7 @@ import LibraryCard from '~/components/LibraryCard' import { getFrameworkLibraryCounts, getVisibleLibraries, + orderFrameworksForBrowse, orderLibrariesForBrowse, } from './-libraries-utils' @@ -45,8 +46,11 @@ function LibrariesFrameworkPage() { const allLibraries = getVisibleLibraries() const frameworkCounts = getFrameworkLibraryCounts(allLibraries) - const frameworksWithLibraries = frameworkOptions.filter( - (option) => (frameworkCounts[option.value] ?? 0) > 0, + const frameworksWithLibraries = orderFrameworksForBrowse( + frameworkOptions.filter( + (option) => (frameworkCounts[option.value] ?? 0) > 0, + ), + frameworkCounts, ) const filteredLibraries = orderLibrariesForBrowse( allLibraries.filter((library) => diff --git a/src/utils/llms.ts b/src/utils/llms.ts index 987f9cc0a..066389521 100644 --- a/src/utils/llms.ts +++ b/src/utils/llms.ts @@ -41,6 +41,7 @@ const librarySections = [ const frameworkLabels: Record = { angular: 'Angular', alpine: 'Alpine', + ember: 'Ember', lit: 'Lit', marko: 'Marko', preact: 'Preact', diff --git a/src/utils/npm-packages.ts b/src/utils/npm-packages.ts index f37c4e023..546d329a7 100644 --- a/src/utils/npm-packages.ts +++ b/src/utils/npm-packages.ts @@ -26,10 +26,11 @@ export const frameworkMeta: Record = solid: { name: 'Solid', color: '#2C4F7C' }, vue: { name: 'Vue', color: '#42B883' }, svelte: { name: 'Svelte', color: '#FF3E00' }, - marko: { name: 'Marko', color: '#44bfef' }, angular: { name: 'Angular', color: '#DD0031' }, - alpine: { name: 'Alpine', color: '#77C1D2' }, lit: { name: 'Lit', color: '#325CFF' }, + alpine: { name: 'Alpine', color: '#77C1D2' }, + marko: { name: 'Marko', color: '#44bfef' }, + ember: { name: 'Ember', color: '#E04E39' }, qwik: { name: 'Qwik', color: '#18B6F6' }, vanilla: { name: 'Vanilla', color: '#F7DF1E' }, } diff --git a/tests/libraries-utils.test.ts b/tests/libraries-utils.test.ts new file mode 100644 index 000000000..129c09266 --- /dev/null +++ b/tests/libraries-utils.test.ts @@ -0,0 +1,33 @@ +import assert from 'node:assert/strict' +import { orderFrameworksForBrowse } from '../src/routes/-libraries-utils' +import type { Framework } from '../src/libraries/types' + +const frameworks: Array<{ value: Framework }> = [ + { value: 'react' }, + { value: 'vue' }, + { value: 'solid' }, + { value: 'vanilla' }, + { value: 'ember' }, +] + +const ordered = orderFrameworksForBrowse(frameworks, { + react: 8, + vue: 5, + solid: 5, + vanilla: 10, + ember: 1, +}) + +assert.deepEqual( + ordered.map((framework) => framework.value), + ['react', 'vue', 'solid', 'ember', 'vanilla'], + 'frameworks sort by library count, preserve ties, and keep Vanilla last', +) + +assert.deepEqual( + frameworks.map((framework) => framework.value), + ['react', 'vue', 'solid', 'vanilla', 'ember'], + 'framework sorting does not mutate the shared framework options', +) + +console.log('libraries utils tests passed')