move to svelte kit

This commit is contained in:
cupcakearmy 2021-08-02 09:53:08 +02:00
parent d8e58997e4
commit b1540a7600
No known key found for this signature in database
GPG Key ID: D28129AE5654D9D9
64 changed files with 1485 additions and 2895 deletions

1
.env Normal file
View File

@ -0,0 +1 @@
VITE_API_URL=https://api.nicco.io/graphql

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.afphoto filter=lfs diff=lfs merge=lfs -text

15
.gitignore vendored
View File

@ -1,10 +1,5 @@
# Node
/node_modules/
# Sapper
/src/node_modules/@sapper/
/__sapper__/
.vercel
data
backups
.DS_Store
node_modules
/.svelte-kit
/package
/build

1
.graphqlrc.yml Normal file
View File

@ -0,0 +1 @@
schema: "https://api.nicco.io/graphql"

1
.npmrc Normal file
View File

@ -0,0 +1 @@
engine-strict=true

View File

@ -1,7 +0,0 @@
{
"tabWidth": 2,
"useTabs": false,
"semi": false,
"trailingComma": "es5",
"singleQuote": true
}

View File

@ -1,10 +0,0 @@
# Node
/node_modules/
# Sapper
/src/node_modules/@sapper/
/__sapper__/
.vercel
data
backups

View File

@ -1,13 +1,38 @@
# [nicco.io](https://nicco.io)
# create-svelte
My personal space on the interwebs
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte);
## Stack
## Creating a project
- Wordpress as Headless CMS
- Advanced Custom Fields
- Custom Post Type UI
- ACF to REST API
- WP Webhooks
- Sapper Frontend (Svelte)
- Vercel for static hosting
If you're seeing this, you've probably already done this step. Congrats!
```bash
# create a new project in the current directory
npm init svelte@next
# create a new project in my-app
npm init svelte@next my-app
```
> Note: the `@next` is temporary
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Building
Before creating a production version of your app, install an [adapter](https://kit.svelte.dev/docs#adapters) for your target environment. Then:
```bash
npm run build
```
> You can preview the built app with `npm run preview`, regardless of whether you installed an adapter. This should _not_ be used to serve your app in production.

BIN
design/About.afphoto (Stored with Git LFS) Normal file

Binary file not shown.

BIN
design/About.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

BIN
design/Home.afphoto (Stored with Git LFS) Normal file

Binary file not shown.

BIN
design/Home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 MiB

BIN
design/Signature.afphoto (Stored with Git LFS) Normal file

Binary file not shown.

BIN
design/Signature.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

3074
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +1,27 @@
{
"private": true,
"scripts": {
"dev": "sapper dev",
"export": "sapper export",
"start": "serve __sapper__/export"
},
"dependencies": {
"axios": "^0.21.1",
"compression": "^1.7.1",
"dayjs": "^1.9.7",
"highlight.js": "^10.4.1",
"lunr": "^2.3.9",
"polka": "next",
"sirv": "^1.0.9",
"svelte-cloudinary": "^0.2"
"dev": "svelte-kit dev",
"build": "svelte-kit build",
"preview": "svelte-kit preview",
"check": "svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^14.0.0",
"@rollup/plugin-node-resolve": "^8.0.0",
"@rollup/plugin-replace": "^2.2.0",
"npm-run-all": "^4.1.5",
"rollup": "^2.34.2",
"rollup-plugin-svelte": "^6.1.1",
"rollup-plugin-terser": "^7.0.0",
"sapper": "^0.29.0",
"sapper-environment": "^1.0.1",
"svelte": "^3.35.0"
"@sveltejs/adapter-static": "^1.0.0-next.13",
"@sveltejs/kit": "^1.0.0-next.139",
"@types/highlight.js": "^10.1.0",
"svelte": "^3.34.0",
"svelte-check": "^2.0.0",
"svelte-preprocess": "^4.0.0",
"tslib": "^2.0.0",
"typescript": "^4.0.0"
},
"type": "module",
"dependencies": {
"axios": "^0.21.1",
"dayjs": "^1.10.5",
"highlight.js": "^11.0.0",
"svelte-cloudinary": "^0.2.3"
}
}

View File

@ -1,75 +0,0 @@
import resolve from '@rollup/plugin-node-resolve'
import replace from '@rollup/plugin-replace'
import commonjs from '@rollup/plugin-commonjs'
import svelte from 'rollup-plugin-svelte'
import { terser } from 'rollup-plugin-terser'
import config from 'sapper/config/rollup.js'
import pkg from './package.json'
const mode = process.env.NODE_ENV
const dev = mode === 'development'
const onwarn = (warning, onwarn) =>
(warning.code === 'MISSING_EXPORT' && /'preload'/.test(warning.message)) ||
(warning.code === 'CIRCULAR_DEPENDENCY' &&
/[/\\]@sapper[/\\]/.test(warning.message)) ||
onwarn(warning)
export default {
client: {
input: config.client.input(),
output: config.client.output(),
plugins: [
replace({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode),
preventAssignment: true,
}),
svelte({
dev,
hydratable: true,
emitCss: true,
}),
resolve({
browser: true,
dedupe: ['svelte'],
}),
commonjs(),
!dev &&
terser({
module: true,
}),
],
preserveEntrySignatures: false,
onwarn,
},
server: {
input: config.server.input(),
output: config.server.output(),
plugins: [
replace({
'process.browser': false,
'process.env.NODE_ENV': JSON.stringify(mode),
preventAssignment: true,
}),
svelte({
generate: 'ssr',
hydratable: true,
dev,
}),
resolve({
dedupe: ['svelte'],
}),
commonjs(),
],
external: Object.keys(pkg.dependencies).concat(
require('module').builtinModules
),
preserveEntrySignatures: 'strict',
onwarn,
},
}

View File

@ -7,8 +7,8 @@
}
:root {
--ff: 'Jost', Roboto, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
--ff: 'Jost', Roboto, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans,
Helvetica Neue, sans-serif;
--ff-alt: 'Playfair Display', serif;
--clr-light: #ffffff;
--clr-dark: #010101;

View File

@ -4,35 +4,14 @@
<meta charset="utf-8" />
<meta name="description" content="Designer & Developer" />
<meta name="keywords" content="Web Agency Blog Articles" />
<meta
name="viewport"
content="width=device-width,initial-scale=1.0,viewport-fit=cover"
/>
<meta name="theme-color" content="#333333" />
<meta name="viewport" content="width=device-width,initial-scale=1.0,viewport-fit=cover" />
%sapper.base%
<link rel="stylesheet" href="global.css" />
<link rel="icon" type="image/png" href="/images/monogramm.png" />
<!-- Sapper generates a <style> tag containing critical CSS
for the current page. CSS for the rest of the app is
lazily loaded when it precaches secondary pages -->
%sapper.styles%
<!-- This contains the contents of the <svelte:head> component, if
the current page has one -->
%sapper.head%
%svelte.head%
</head>
<body>
<!-- The application will be rendered inside this element,
because `src/client.js` references it -->
<div id="sapper">%sapper.html%</div>
<!-- Sapper creates a <script> tag containing `src/client.js`
and anything else it needs to hydrate the app and
initialise the router -->
%sapper.scripts%
<div id="svelte">%svelte.body%</div>
<script type="text/javascript">
var _paq = (window._paq = window._paq || [])
@ -56,11 +35,7 @@
</script>
<noscript
><p>
<img
src="//stats.nicco.io/rainbow?idsite=1&amp;rec=1"
style="border: 0"
alt=""
/></p
<img src="//stats.nicco.io/rainbow?idsite=1&amp;rec=1" style="border: 0" alt="" /></p
></noscript>
</body>
</html>

View File

@ -1,5 +0,0 @@
import * as sapper from '@sapper/app';
sapper.start({
target: document.querySelector('#sapper')
});

View File

@ -1,61 +0,0 @@
<script>
import dj from 'dayjs'
import ImageFrame from '../components/ImageFrame.svelte'
import Icon from '../components/Icon.svelte'
export let work
</script>
<a href={work.link} target="_blank" rel="noopener">
<div class="horizontal">
<div class="title regular">{work.title}</div>
<div>
<Icon icon="link-outline" />
<span>{work.link.replace(/https?:\/\//, '')}</span>
</div>
</div>
<ImageFrame
src={work.image.sizes.medium_large}
alt={work.image.description}
/>
</a>
<div class="horizontal regular">
<div>{work.role}</div>
<div>{dj(work.date * 1000).format('MMM YY')}</div>
</div>
<p>
{@html work.content}
</p>
<style>
.title {
font-size: 2em;
line-height: 1;
}
.horizontal {
display: flex;
justify-content: space-between;
align-items: end;
}
.regular {
font-weight: 400;
}
p {
margin-bottom: 6em;
}
a {
font-family: monospace;
}
@media (max-width: 30em) {
.horizontal {
flex-direction: column;
align-items: flex-start;
}
}
</style>

1
src/global.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="@sveltejs/kit" />

52
src/lib/api/index.ts Normal file
View File

@ -0,0 +1,52 @@
import axios from 'axios'
export const API = axios.create({
baseURL: import.meta.env.VITE_API_URL as string,
})
export function gql(s: TemplateStringsArray) {
return s.join('')
}
export async function Call<T>(query: string, variables: Record<string, any> = {}): Promise<T> {
const { data } = await API({
url: '/graphql',
method: 'post',
data: {
query,
variables,
},
})
return data.data as T
}
export type Page = {
title: string
content: string | null
slug: string
id: string
status: string
}
export interface Work extends Page {
work: {
date: string
image: MediaItem
link: string
role: string
}
}
export interface Project extends Page {
project: {
date: string
link: string
description: string
}
}
export type MediaItem = {
srcSet: string
altText: string
sourceUrl: string
}

View File

@ -1,13 +1,12 @@
<script>
<script lang="ts">
import { onMount } from 'svelte'
export let icon
export let icon: string
$: src = `/icons/${icon}.svg`
let html = null
let html: string | null = null
onMount(async () => {
console.log(src)
html = await fetch(src).then((res) => res.text())
})
</script>
@ -27,7 +26,6 @@
width: 1em;
height: 1em;
contain: strict;
/* fill: currentcolor; */
box-sizing: content-box;
transform: translateY(0.2em);
}

View File

@ -1,9 +1,15 @@
<script>
<script lang="ts">
import Icon from './Icon.svelte'
export let links = []
type Link = {
href: string
name: string
icon: string
}
function isExternal(link) {
export let links: Link[] = []
function isExternal(link: string) {
return /^https?\:\/\//.test(link)
}
@ -15,11 +21,7 @@
<ul>
{#each list as { href, name, icon, external }}
<a
rel={external ? 'noopener noreferrer' : ''}
{href}
target={external ? '_blank' : ''}
>
<a rel={external ? 'noopener noreferrer' : ''} {href} target={external ? '_blank' : ''}>
<li>
<Icon class="icon" {icon} />
{name}

View File

@ -1,18 +1,22 @@
<script context="module">
<script lang="ts" context="module">
import { initialize } from 'svelte-cloudinary'
initialize({ cloud_name: 'cupcakearmy' })
</script>
<script>
<script lang="ts">
import { image } from 'svelte-cloudinary'
export let src
export let alt
export let src: string
// export let srcset: string
export let alt: string
$: cleaned = src.replace('https://api.nicco.io', '/nicco')
</script>
<img use:image={{ src: cleaned, bind: { width: true }, lazy: true }} {alt} />
<!-- <img {srcset} {alt} /> -->
<style>
img {
width: calc(100% - 0.25em);
@ -29,5 +33,3 @@
margin: 1em 0;
}
</style>
<img use:image={{ src: cleaned, bind: { width: true }, lazy: true }} {alt} />

View File

@ -1,7 +1,7 @@
<script>
import Icon from './Icon.svelte'
<script lang="ts">
import { page } from '$app/stores'
export let segment
import Icon from './Icon.svelte'
const routes = [
{ name: 'About', href: '/about' },
@ -11,12 +11,12 @@
{ name: 'Contact', href: '/contact' },
]
let nav
let nav: HTMLDivElement
</script>
<nav bind:this={nav}>
<a href="/">
<h1 class:active={segment === undefined}>NB</h1>
<h1 class:active={$page.path === '/'}>NB</h1>
</a>
<ul>
<li>
@ -28,7 +28,7 @@
<li>
<a {href}>
<span>{name}</span>
<div class:active={href.slice(1) === segment} />
<div class:active={$page.path.startsWith(href)} />
</a>
</li>
{/each}

View File

@ -1,19 +1,19 @@
<script>
<script lang="ts">
import SpacedLetters from './SpacedLetters.svelte'
export let title = ''
export let readable = false
</script>
<div>
<h1>
<SpacedLetters letters={title} {readable} />
</h1>
</div>
<style>
div {
margin-top: calc(28vh - 3em);
margin-bottom: 3em;
}
</style>
<div>
<h1>
<SpacedLetters letters={title} {readable} />
</h1>
</div>

View File

@ -1,7 +1,7 @@
<script>
<script lang="ts">
import { spring } from 'svelte/motion'
import { scroll } from '../lib/scroll'
import { scroll } from '$lib/stores'
let el
const springed = spring(
@ -21,9 +21,7 @@
const x = R + R * Math.cos(a) * 2
const y = R - R * Math.sin(a) * 2
const center = alpha > 180 ? 1 : 0
const path = `M${R},${-50} A${R * 2},${
R * 2
},0,${center},1,${x},${y} L${R},${R} L${R},${-R}`
const path = `M${R},${-50} A${R * 2},${R * 2},0,${center},1,${x},${y} L${R},${R} L${R},${-R}`
if (el) el.setAttribute('d', path)
}

View File

@ -1,18 +1,18 @@
<script>
import dj from 'dayjs'
<script lang="ts">
import dayjs from 'dayjs'
import Icon from '../components/Icon.svelte'
import Icon from './Icon.svelte'
export let project
export let project: import('$lib/api').Project
</script>
<section>
<a href={project.link} target="_blank" rel="noopener">
<a href={project.project.link} target="_blank" rel="noopener">
<h2>{project.title}</h2>
</a>
<div class="subtitle">
<b>{project.description}</b>
<b class="date">{dj(project.date * 1000).format('MMM YY')}</b>
<b>{project.project.description}</b>
<b class="date">{dayjs(project.project.date, 'X').format('MMM YY')}</b>
</div>
<p>
@ -21,8 +21,8 @@
<div class="link">
<Icon icon="link-outline" />
<a rel="noopener noreferrer" target="_blank" href={project.link}
>{project.link.replace(/https?:\/\//, '')}</a
<a rel="noopener noreferrer" target="_blank" href={project.project.link}
>{project.project.link.replace(/https?:\/\//, '')}</a
>
</div>
</section>

View File

@ -1,4 +1,4 @@
<script>
<script lang="ts">
import PageTitle from './PageTitle.svelte'
export let title = ''
@ -6,6 +6,12 @@
export let readable = false
</script>
<PageTitle {title} {readable} />
<section class:expanded>
<slot />
</section>
<style>
section {
max-width: 30em;
@ -16,9 +22,3 @@
margin-top: 5em;
}
</style>
<PageTitle {title} {readable} />
<section class:expanded>
<slot />
</section>

View File

@ -1,9 +1,15 @@
<script>
export let letters = []
<script lang="ts">
export let letters: string = ''
export let even = false
export let readable = false
</script>
<div class:even class:readable>
{#if even}
{#each letters as letter}<span>{letter}</span>{/each}
{:else}{letters}{/if}
</div>
<style>
span {
width: 1em;
@ -28,9 +34,3 @@
font-size: 2.25rem;
}
</style>
<div class:even class:readable>
{#if even}
{#each letters as letter}<span>{letter}</span>{/each}
{:else}{letters}{/if}
</div>

View File

@ -1,4 +1,4 @@
<script>
<script lang="ts">
import 'highlight.js/styles/github.css'
import hljs from 'highlight.js/lib/core'
import javascript from 'highlight.js/lib/languages/javascript'
@ -23,9 +23,9 @@
import { onMount } from 'svelte'
export let content
export let content: string
function encodeTextToUrl(text) {
function encodeTextToUrl(text: string): string {
return text
.replace(/[^A-Za-z ]/, '')
.replace('/ +/', ' ')
@ -35,14 +35,14 @@
onMount(() => {
hljs.highlightAll()
const selector = [1, 2, 3, 4, 5, 6]
.map((i) => `div.adapter h${i}`)
.join(', ')
const selector = [1, 2, 3, 4, 5, 6].map((i) => `div.adapter h${i}`).join(', ')
const elements = window.document.querySelectorAll(selector)
for (const el of elements) {
const hash = encodeTextToUrl(el.textContent)
el.innerHTML = `<a class="target-link" name="${hash}" href="${window.location.pathname}#${hash}">${el.innerHTML}</a>`
}
elements.forEach((el) => {
if (el.textContent) {
const hash = encodeTextToUrl(el.textContent)
el.innerHTML = `<a class="target-link" name="${hash}" href="${window.location.pathname}#${hash}">${el.innerHTML}</a>`
}
})
})
</script>

View File

@ -0,0 +1,65 @@
<script lang="ts">
import type { Work } from '$lib/api'
import dayjs from 'dayjs'
import ImageFrame from '$lib/components/ImageFrame.svelte'
import Icon from '$lib/components/Icon.svelte'
export let work: Work
</script>
<section>
<a href={work.work.link} target="_blank" rel="noopener">
<div class="horizontal">
<div class="title regular">{work.title}</div>
<div>
<Icon icon="link-outline" />
<span>{work.work.link.replace(/https?:\/\//, '')}</span>
</div>
</div>
<!-- <ImageFrame src={work.work.image.sizes.medium_large} alt={work.image.description} /> -->
<!-- <ImageFrame srcset={work.work.image.srcSet} alt={work.work.image.altText} /> -->
<ImageFrame src={work.work.image.sourceUrl} alt={work.work.image.altText} />
</a>
<div class="horizontal regular">
<div>{work.work.role}</div>
<div>{dayjs(work.work.date, 'X').format('MMM YY')}</div>
</div>
{#if work.content}
<p>
{@html work.content}
</p>
{/if}
</section>
<style>
.title {
font-size: 2em;
line-height: 1;
}
.horizontal {
display: flex;
justify-content: space-between;
align-items: end;
}
.regular {
font-weight: 400;
}
section {
margin-bottom: 6em;
}
a {
font-family: monospace;
}
@media (max-width: 30em) {
.horizontal {
flex-direction: column;
align-items: flex-start;
}
}
</style>

View File

@ -1,6 +0,0 @@
export function readingTimeInMinutes(text, options = {}) {
options = Object.assign({ wpm: 200 }, options)
const cleaned = text.replace(/(<.*?>)|(\\n)|(&#\d*?;)/g, '')
const words = cleaned.split(' ').length
return Math.round(words / options.wpm)
}

View File

@ -1,50 +0,0 @@
import axios from 'axios'
const isDev = process.env.NODE_ENV !== 'production' && false
axios.defaults.baseURL = `${isDev ? 'http://localhost' : 'https://api.nicco.io'}/wp-json/wp/v2`
function normalize(post) {
return {
...post,
...post.acf,
id: post.id,
title: post.title.rendered,
content: post.content.rendered,
}
}
function combineUrlAndParams(url, params) {
const p = new URLSearchParams({
per_page: 100,
...params,
}).toString()
return `${url}?${p}`
}
export async function getOne(url, params = {}) {
const { data } = await axios(combineUrlAndParams(url, params))
if (!data.length) return null
else return normalize(data[0])
}
export async function getAll(url, params = {}) {
const { data, headers } = await axios(combineUrlAndParams(url, params))
const totalPages = parseInt(headers['x-wp-totalpages'])
const results = [...data]
if (totalPages > 1) {
for (let page = 2; page <= totalPages; page++) {
const { data } = await axios(combineUrlAndParams(url, { ...params, page }))
results.push(...data)
}
}
return results.map(normalize)
}
export function sortByDate(data) {
return data.sort((a, b) => parseInt(b.date) - parseInt(a.date))
}
export function respond(res, body) {
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify(body))
}

View File

@ -1,22 +1,25 @@
<script>
<script lang="ts">
import { onMount } from 'svelte'
import { stores } from '@sapper/app'
import { page } from '$app/stores'
import { scroll } from '../lib/scroll'
import Nav from '../components/Nav.svelte'
import Progress from '../components/Progress.svelte'
import '../app.css'
export let segment
let wrapper
let main
import dayjs from 'dayjs'
import customParseFormat from 'dayjs/plugin/customParseFormat.js'
dayjs.extend(customParseFormat)
import { scroll } from '$lib/stores'
import Nav from '$lib/components/Nav.svelte'
import Progress from '$lib/components/Progress.svelte'
let wrapper: HTMLDivElement
let main: HTMLDivElement
function resize() {
wrapper.style.height = `${window.innerHeight}px`
}
const { page } = stores()
let last = ''
$: {
const { host, path } = $page
const full = host + path
@ -26,25 +29,25 @@
}
}
function updateScroll(e) {
function updateScroll(e: any) {
const el = e.target
const percentage = el.scrollTop / (el.scrollHeight - el.offsetHeight)
scroll.set(isNaN(percentage) ? 0 : percentage)
}
onMount(() => {
const resizeFN = window.addEventListener('resize', resize, false)
const scrollFN = main.addEventListener('scroll', updateScroll, false)
window.addEventListener('resize', resize, false)
main.addEventListener('scroll', updateScroll, false)
resize()
return () => {
window.removeEventListener(resizeFN)
main.removeEventListener(scrollFN)
window.removeEventListener('resize', resize)
main.removeEventListener('scroll', updateScroll)
}
})
</script>
<div bind:this={wrapper}>
<Nav {segment} />
<Nav />
<main bind:this={main}>
<slot />
</main>

View File

@ -1,28 +0,0 @@
<script>
export let status
export let error
const dev = process.env.NODE_ENV === 'development'
</script>
<style>
h1 {
font-size: 8vw;
}
p {
margin: 1em auto;
}
</style>
<svelte:head>
<title>{status}</title>
</svelte:head>
<h1>{status}</h1>
<p>{error.message}</p>
{#if dev && error.stack}
<pre>{error.stack}</pre>
{/if}

View File

@ -1,16 +1,37 @@
<script context="module">
export async function preload() {
return this.fetch('/api/pages/about.json').then((res) => res.json())
<script lang="ts" context="module">
import type { Load } from '@sveltejs/kit'
export const prerender = true
export const load: Load = async ({ fetch }) => {
return {
props: {
data: await fetch('/api/pages/about.json').then((r) => r.json()),
image: await fetch('/api/media/about-2.json').then((r) => r.json()),
},
}
}
</script>
<script>
import WPAdapter from '../components/WPAdapter.svelte'
import SimplePage from '../components/SimplePage.svelte'
<script lang="ts">
import WPAdapter from '$lib/components/WPAdapter.svelte'
import SimplePage from '$lib/components/SimplePage.svelte'
import type { Page, MediaItem } from '$lib/api'
export let data
export let data: Page
export let image: MediaItem
</script>
<svelte:head>
<title>{data.title}</title>
</svelte:head>
<SimplePage title={data.title} expanded={false}>
{#if data.content}
<WPAdapter content={data.content} />
<img srcset={image.srcSet} alt="decoration" />
{/if}
</SimplePage>
<style>
img {
position: absolute;
@ -34,13 +55,3 @@
}
}
</style>
<svelte:head>
<title>About</title>
</svelte:head>
<SimplePage title="About" expanded={false}>
<WPAdapter content={data.content} />
<!-- {@html data.content} -->
<img src="/images/about.jpg" alt="decoration" />
</SimplePage>

View File

@ -1,13 +0,0 @@
import { respond, getAll, getOne, sortByDate } from '../../lib/wp'
export async function get(req, res) {
const [type, slug] = req.params.slug
if (slug) {
const data = await getOne(type, { slug })
respond(res, { data })
} else {
const data = await getAll(type)
respond(res, { data: sortByDate(data) })
}
}

View File

@ -0,0 +1,116 @@
import type { RequestHandler } from '@sveltejs/kit'
import { Call, gql, MediaItem, Page, Project } from '$lib/api'
export const get: RequestHandler = async (args) => {
const { type, slug } = args.params
const allChar = '*'
const all = slug === allChar
switch (type) {
case 'pages': {
if (all) {
const data = await Call<{ pages: { nodes: Page[] } }>(gql`
query {
pages {
nodes {
title
content
slug
status
id
}
}
}
`)
return { body: data.pages.nodes }
} else {
const data = await Call<{ page: Page }>(
gql`
query ($slug: ID!) {
page(id: $slug, idType: URI) {
title
content
slug
status
id
}
}
`,
{ slug: '/' + slug }
)
return { body: data.page }
}
}
case 'works': {
if (all) {
const data = await Call<{ works: { nodes: MediaItem[] } }>(gql`
query {
works {
nodes {
id
title
content
slug
status
work {
date
image {
sourceUrl
srcSet
altText
}
link
role
}
}
}
}
`)
return { body: data.works.nodes }
} else {
}
}
case 'projects': {
if (all) {
const data = await Call<{ projects: { nodes: Project[] } }>(gql`
query {
projects {
nodes {
id
title
content
slug
status
project {
date
description
link
}
}
}
}
`)
return { body: data.projects.nodes }
}
}
case 'media': {
const data = await Call<{ mediaItem: MediaItem }>(
gql`
query ($slug: ID!) {
mediaItem(id: $slug, idType: SLUG) {
srcSet
altText
sourceUrl
}
}
`,
{ slug }
)
return { body: data.mediaItem }
}
default:
return { status: 404 }
}
}

View File

@ -1,20 +0,0 @@
<script context="module">
export async function preload({ params }) {
return this.fetch(`/api/posts/${params.slug}.json`).then((res) =>
res.json()
)
}
</script>
<script>
import SimplePage from '../../components/SimplePage.svelte'
import WPAdapter from '../../components/WPAdapter.svelte'
import PostAttributes from '../../components/PostAttributes.svelte'
export let data
</script>
<SimplePage title={data.title} expanded={false} readable>
<PostAttributes post={data} full />
<WPAdapter content={data.content} />
</SimplePage>

View File

@ -1,45 +0,0 @@
<script context="module">
export async function preload(page) {
return this.fetch('/api/posts.json').then((res) => res.json())
}
</script>
<script>
import { onMount } from 'svelte'
import SimplePage from '../../components/SimplePage.svelte'
import PostPreview from '../../components/PostPreview.svelte'
export let data
export let redirected = false
onMount(() => {
redirected = new URL(location.href).searchParams.has('old')
})
</script>
<style>
div {
margin-bottom: 6em;
background-color: var(--clr-error);
padding: 1em;
}
</style>
<svelte:head>
<title>Blog</title>
</svelte:head>
<SimplePage title="Blog">
{#if redirected}
<div>
<h2>You have been redirected 🔄</h2>
<p>
Probably you are coming form my old blog (blog.nicco.io)
<br />
The article you were looking for is down here 👇
</p>
</div>
{/if}
{#each data as post}
<PostPreview {post} />
{/each}
</SimplePage>

View File

@ -1,7 +1,6 @@
<script>
import IconList from '../components/IconList.svelte'
import SimplePage from '../components/SimplePage.svelte'
<script lang="ts">
import IconList from '$lib/components/IconList.svelte'
import SimplePage from '$lib/components/SimplePage.svelte'
const links = [
{

View File

@ -1,72 +1,111 @@
<script>
import SpacedLetters from '../components/SpacedLetters.svelte'
<script lang="ts" context="module">
import { Call, gql } from '$lib/api'
export const prerender = true
type Data = Record<'signature' | 'home', { srcSet: string }>
export const load: Load = async () => {
const data = await Call<Data>(gql`
query {
signature: mediaItem(id: "signature", idType: SLUG) {
srcSet
}
home: mediaItem(id: "home", idType: SLUG) {
srcSet
}
}
`)
return { props: { data } }
}
</script>
<script lang="ts">
import SpacedLetters from '$lib/components/SpacedLetters.svelte'
import type { Load } from '@sveltejs/kit'
export let data: Data
</script>
<svelte:head>
<title>Niccolo Borgioli</title>
</svelte:head>
<section class="left" style="z-index: 3;">
<h1>
<SpacedLetters letters="Niccolò" even />
<SpacedLetters letters="Borgioli" even />
</h1>
<div class="wrapper">
<div class="left" style="z-index: 3;">
<h1>
<SpacedLetters letters="Niccolò" even />
<SpacedLetters letters="Borgioli" even />
</h1>
<p>Design & Development</p>
</section>
<p>Design & Development</p>
</div>
<section class="right" style="z-index: 2;">
<picture>
<source media="(min-width: 60em)" srcset="/images/home@1500.webp" />
<source media="(min-width: 45em)" srcset="/images/home@1000.webp" />
<source srcset="/images/home@500.webp" />
<img src="/images/decoration.jpg" alt="decoration" />
</picture>
</section>
<div class="right" style="z-index: 2;">
<img srcset={data.home.srcSet} alt="decoration" class="home" />
<img srcset={data.signature.srcSet} alt="signature" class="signature" />
</div>
</div>
<style>
p {
font-size: 4vw;
margin-top: 0;
}
section {
.wrapper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 1rem;
}
section.left {
align-items: flex-start;
padding-left: 1em;
width: initial;
.left {
flex: 1 0 auto;
max-width: 64vw;
}
section.right {
align-items: flex-end;
.right {
position: relative;
top: 5vh;
}
img {
p {
font-size: 4vw;
margin: 0;
}
img.home {
width: min(100%, 33vw);
object-fit: contain;
height: 65vh;
width: 33vw;
transform: translateY(5vh);
object-position: left;
max-height: 65vh;
}
img.signature {
position: absolute;
width: 50%;
top: -6%;
left: -10%;
transform: rotate(-8deg);
}
@media (max-width: 50em) {
img {
transform: translateY(15vh);
height: 60vh;
width: 69vw;
.wrapper {
flex-direction: column;
}
section.left {
transform: translateY(-25vh);
.left {
flex: initial;
}
.right {
left: 1rem;
}
img.home {
width: auto;
height: 100%;
max-height: calc(90vh - 35vw - 5vh);
max-width: 90%;
}
}
</style>

View File

@ -1,16 +1,30 @@
<script context="module">
export async function preload() {
return this.fetch('/api/pages/privacy.json').then((res) => res.json())
<script lang="ts" context="module">
import type { Load } from '@sveltejs/kit'
export const prerender = true
export const load: Load = async ({ fetch }) => {
return {
props: {
data: await fetch('/api/pages/privacy.json').then((r) => r.json()),
},
}
}
</script>
<script>
import WPAdapter from '../components/WPAdapter.svelte'
import SimplePage from '../components/SimplePage.svelte'
<script lang="ts">
import WPAdapter from '$lib/components/WPAdapter.svelte'
import SimplePage from '$lib/components/SimplePage.svelte'
import type { Page } from '$lib/api'
export let data
export let data: Page
</script>
<SimplePage title="Privacy Policy">
<WPAdapter content={data.content} />
<svelte:head>
<title>{data.title}</title>
</svelte:head>
<SimplePage title={data.title} expanded={false}>
{#if data.content}
<WPAdapter content={data.content} />
{/if}
</SimplePage>

View File

@ -1,14 +1,21 @@
<script context="module">
export async function preload() {
return this.fetch('/api/projects.json').then((res) => res.json())
<script lang="ts" context="module">
import type { Load } from '@sveltejs/kit'
export const load: Load = async ({ fetch }) => {
return {
props: {
data: await fetch('/api/projects/*.json').then((r) => r.json()),
},
}
}
</script>
<script lang="ts">
import SimplePage from '../components/SimplePage.svelte'
import Project from '../components/Project.svelte'
import type { Project as TProject } from '$lib/api'
import SimplePage from '$lib/components/SimplePage.svelte'
import Project from '$lib/components/Project.svelte'
export let data
export let data: TProject[]
</script>
<svelte:head>

View File

@ -1,34 +0,0 @@
import lunr from 'lunr'
import { getAll } from '../lib/wp'
function removeHTML(s) {
return s.replace(/<.*?>|\s+|&#\d+;/g, ' ').trim()
}
async function convertForIdx(type, fields = []) {
const items = await getAll(type)
const keys = ['title', 'content', 'slug', ...fields]
return items.map((item) => ({
url: `${item.type}/${item.slug}`,
data: keys.map((field) => removeHTML(item[field])).join(' '),
}))
}
export async function get(req, res) {
const all = await Promise.all([
convertForIdx('projects', ['description']),
convertForIdx('pages'),
convertForIdx('posts'),
convertForIdx('works', ['role']),
])
const idx = lunr(function () {
this.ref('url')
this.field('data')
all.flat().forEach((doc) => this.add(doc))
})
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify(idx))
}

View File

@ -1,70 +0,0 @@
<script context="module">
export async function preload({ query }) {
const prebuilt = await this.fetch(`/search.json`).then((res) => res.json())
return { prebuilt }
}
</script>
<script>
import lunr from 'lunr'
import { onMount } from 'svelte'
import SearchResult from '../components/SearchResult.svelte'
import SimplePage from '../components/SimplePage.svelte'
export let prebuilt
let needle
let results = []
const idx = lunr.Index.load(prebuilt)
async function search(needle) {
if (!needle || !idx) {
results = []
} else {
let found = idx.search(needle + '~1')
if (!found.length) found = idx.search(needle + '*')
results = found.slice(0, 20)
}
}
$: if (needle) {
if (typeof window !== 'undefined') {
window.history.replaceState(null, null, `/search?q=${needle || ''}`)
}
search(needle)
}
onMount(() => {
needle = new URLSearchParams(window.location.search).get('q')
})
</script>
<SimplePage title="Search" expanded={false}>
<input bind:value={needle} placeholder="needle" />
<ul>
{#each results as result (result.ref)}
<SearchResult {result} />
{/each}
</ul>
</SimplePage>
<style>
input {
appearance: none;
-webkit-appearance: none;
margin: 0;
padding: 0.5rem;
font-size: 1em;
width: 100%;
outline: none;
border: 2px solid var(--clr-primary);
border-radius: 0;
}
ul {
margin-top: 2em;
list-style: none;
padding: 0;
}
</style>

View File

@ -1,7 +1,6 @@
<script>
import IconList from '../components/IconList.svelte'
import SimplePage from '../components/SimplePage.svelte'
<script lang="ts">
import IconList from '$lib/components/IconList.svelte'
import SimplePage from '$lib/components/SimplePage.svelte'
const links = [
{

View File

@ -1,14 +1,21 @@
<script context="module">
export async function preload() {
return this.fetch('/api/works.json').then((res) => res.json())
<script lang="ts" context="module">
import type { Load } from '@sveltejs/kit'
export const load: Load = async ({ fetch }) => {
return {
props: {
data: await fetch('/api/works/*.json').then((r) => r.json()),
},
}
}
</script>
<script>
import SimplePage from '../components/SimplePage.svelte'
import Work from '../components/Work.svelte'
<script lang="ts">
import type { Work as TWork } from '$lib/api'
import SimplePage from '$lib/components/SimplePage.svelte'
import Work from '$lib/components/Work.svelte'
export let data
export let data: TWork[]
</script>
<svelte:head>

View File

@ -1,15 +0,0 @@
import sirv from 'sirv'
import polka from 'polka'
import compression from 'compression'
import * as sapper from '@sapper/server'
const { PORT, NODE_ENV } = process.env
const dev = NODE_ENV === 'development'
polka()
.use(compression({ threshold: 0 }))
.use(sirv('static', { dev }))
.use(sapper.middleware())
.listen(PORT, (err) => {
if (err) console.log('error', err)
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 363 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1,2 +0,0 @@
User-Agent: *
Allow: /

13
svelte.config.js Normal file
View File

@ -0,0 +1,13 @@
import preprocess from 'svelte-preprocess'
import adapter from '@sveltejs/adapter-static'
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: preprocess(),
kit: {
adapter: adapter(),
target: '#svelte',
},
}
export default config

34
tsconfig.json Normal file
View File

@ -0,0 +1,34 @@
{
"compilerOptions": {
"moduleResolution": "node",
"module": "es2020",
"lib": ["es2020"],
"target": "es2019",
"noImplicitAny": true,
"alwaysStrict": true,
"strictNullChecks": true,
/**
svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
to enforce using \`import type\` instead of \`import\` for Types.
*/
"importsNotUsedAsValues": "error",
"isolatedModules": true,
"resolveJsonModule": true,
/**
To have warnings/errors of the Svelte compiler at the correct position,
enable source maps by default.
*/
"sourceMap": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"allowJs": true,
"checkJs": true,
"paths": {
"$lib/*": ["src/lib/*"]
}
},
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"]
}