feat: landing page + smart components

This commit is contained in:
Liyas Thomas
2021-07-02 05:01:29 +00:00
committed by GitHub
parent fb93db6ad4
commit a130cfcadb
23 changed files with 1124 additions and 55 deletions

18
assets/js/getLinkTag.js Normal file
View File

@@ -0,0 +1,18 @@
const DEFAULT_TAG = "button"
const ANCHOR_TAG = "a"
const FRAMEWORK_LINK = "NuxtLink" // or "router-link", "g-link"...
const getLinkTag = ({ to, blank }) => {
if (!to) {
return DEFAULT_TAG
} else if (blank) {
return ANCHOR_TAG
} else if (/^\/(?!\/).*$/.test(to)) {
// regex101.com/r/LU1iFL/1
return FRAMEWORK_LINK
} else {
return ANCHOR_TAG
}
}
export { getLinkTag as default, ANCHOR_TAG, FRAMEWORK_LINK }

View File

@@ -149,6 +149,46 @@ hr {
@apply my-4;
}
.tippy-popper {
.tooltip-theme {
@apply bg-secondary;
@apply text-primaryLight;
@apply text-xs;
@apply font-semibold;
@apply px-2;
@apply px-4;
@apply shadow;
}
.popover-theme {
@apply bg-primaryLight;
@apply text-secondary;
@apply p-2;
@apply shadow-md;
}
&[x-placement^="top"] .tippy-tooltip .tippy-arrow {
border-top-color: var(--color-primary-light);
}
&[x-placement^="bottom"] .tippy-tooltip .tippy-arrow {
border-bottom-color: var(--color-primary-light);
}
&[x-placement^="left"] .tippy-tooltip .tippy-arrow {
border-left-color: var(--color-primary-light);
}
&[x-placement^="right"] .tippy-tooltip .tippy-arrow {
border-right-color: var(--color-primary-light);
}
}
[arrow] > div {
@apply flex;
@apply w-full;
}
.tooltip {
@apply z-50;
@apply outline-none;

View File

@@ -29,11 +29,11 @@
@mixin lightTheme {
// Background color
--primary-color: rgba(255, 255, 255, 1);
--primary-color: theme("colors.white");
// Light Background color
--primary-light-color: theme("colors.gray.200");
--primary-light-color: theme("colors.gray.50");
// Dark Background color
--primary-dark-color: rgba(0, 0, 0, 0.02);
--primary-dark-color: theme("colors.gray.100");
// Text color
--secondary-color: theme("colors.gray.600");
// Light Text color

View File

@@ -1,9 +1,10 @@
<template>
<header class="flex container items-center justify-between p-2 flex-1">
<div class="flex items-center relative ml-4">
<div class="flex items-center relative ml-2">
<nuxt-link :to="localePath('index')">
<h1 class="heading logo">Hoppscotch</h1>
</nuxt-link>
<AppGitHubStarButton class="ml-8" />
</div>
<div class="flex">
<button

View File

@@ -0,0 +1,125 @@
<template>
<SmartLink
:to="`${/^\/(?!\/).*$/.test(to) ? localePath(to) : to}`"
:exact="exact"
:blank="blank"
class="
inline-flex
items-center
justify-center
py-2
font-semibold
transition
focus:outline-none
"
:class="[
color
? `text-${color}-800 bg-${color}-200 hover:text-${color}-900 hover:bg-${color}-300 focus:text-${color}-900 focus:bg-${color}-300`
: `text-white bg-accent hover:bg-accentDark focus:bg-accentDark`,
label ? 'px-4' : 'px-2',
rounded ? 'rounded-full' : 'rounded-lg',
{ 'opacity-50 cursor-not-allowed': disabled },
{ 'pointer-events-none': loading },
{ 'px-4 py-4 text-lg': large },
{ 'shadow-lg hover:shadow-xl': shadow },
{
'text-white bg-gradient-to-tr from-gradientFrom via-gradientVia to-gradientTo':
gradient,
},
]"
:disabled="disabled"
:tabindex="loading ? '-1' : '0'"
>
<span
v-if="!loading"
class="inline-flex items-center justify-center whitespace-nowrap"
:class="{ 'flex-row-reverse': reverse }"
>
<i
v-if="icon"
:class="label ? (reverse ? 'ml-2' : 'mr-2') : ''"
class="material-icons"
>
{{ icon }}
</i>
<SmartIcon
v-if="svg"
:name="svg"
:class="label ? (reverse ? 'ml-4' : 'mr-4') : ''"
class="svg-icons"
/>
{{ label }}
<span v-if="shortkey" class="px-1 ml-2 rounded bg-accentLight">{{
shortkey
}}</span>
</span>
<SmartSpinner v-else />
</SmartLink>
</template>
<script>
export default {
props: {
to: {
type: String,
default: "",
},
exact: {
type: Boolean,
default: true,
},
blank: {
type: Boolean,
default: false,
},
label: {
type: String,
default: "",
},
icon: {
type: String,
default: "",
},
svg: {
type: String,
default: "",
},
color: {
type: String,
default: "",
},
disabled: {
type: Boolean,
default: false,
},
loading: {
type: Boolean,
default: false,
},
large: {
type: Boolean,
default: false,
},
shadow: {
type: Boolean,
default: false,
},
reverse: {
type: Boolean,
default: false,
},
rounded: {
type: Boolean,
default: false,
},
gradient: {
type: Boolean,
default: false,
},
shortkey: {
type: String,
default: "",
},
},
}
</script>

View File

@@ -0,0 +1,96 @@
<template>
<SmartLink
:to="`${/^\/(?!\/).*$/.test(to) ? localePath(to) : to}`"
:exact="exact"
:blank="blank"
class="
inline-flex
items-center
justify-center
py-2
font-semibold
transition
focus:outline-none
"
:class="[
color
? `text-${color}-500 hover:text-${color}-600 focus:text-${color}-600`
: 'hover:text-secondaryDark focus:text-secondaryDark',
label ? 'px-3 rounded-lg' : 'px-2 rounded-full',
rounded ? 'rounded-full' : 'rounded-lg',
{ 'opacity-50 cursor-not-allowed': disabled },
{ 'flex-row-reverse': reverse },
{
'border border-divider hover:border-dividerDark focus:border-dividerDark':
outline,
},
]"
:disabled="disabled"
>
<i
v-if="icon"
:class="label ? (reverse ? 'ml-2' : 'mr-2') : ''"
class="material-icons"
>
{{ icon }}
</i>
<SmartIcon
v-if="svg"
:name="svg"
:class="label ? (reverse ? 'ml-2' : 'mr-2') : ''"
class="svg-icons"
/>
{{ label }}
</SmartLink>
</template>
<script>
export default {
props: {
to: {
type: String,
default: "",
},
exact: {
type: Boolean,
default: true,
},
blank: {
type: Boolean,
default: false,
},
label: {
type: String,
default: "",
},
icon: {
type: String,
default: "",
},
svg: {
type: String,
default: "",
},
color: {
type: String,
default: "",
},
disabled: {
type: Boolean,
default: false,
},
reverse: {
type: Boolean,
default: false,
},
rounded: {
type: Boolean,
default: false,
},
outline: {
type: Boolean,
default: false,
},
},
}
</script>

View File

@@ -0,0 +1,118 @@
<template>
<div class="flex flex-col p-4">
<div class="flex flex-col items-center">
<p class="my-4 font-semibold tracking-widest text-center text-accent">
FEATURES
</p>
<h3
class="
max-w-xl
mt-4
mb-8
text-4xl
font-bold
leading-tight
tracking-tight
text-center
transition
text-secondaryDark
"
>
A technology-first approach to payments and finance
</h3>
</div>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
<div
v-for="(feature, index) in features"
:key="`feature-${index}`"
class="
inline-flex
flex-col
p-8
transition
border
rounded-lg
border-divider
"
>
<i class="text-3xl material-icons text-accent">{{ feature.icon }}</i>
<div class="flex-grow">
<h2
class="
mt-4
mb-2
text-lg
font-semibold
transition
text-secondaryDark
"
>
{{ feature.title }}
</h2>
<p>
{{ feature.description }}
</p>
<p class="mt-2">
<NuxtLink :to="feature.link.target" class="link">
{{ feature.link.title }}
<i class="material-icons">chevron_right</i>
</NuxtLink>
</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
features: [
{
icon: "offline_bolt",
title: "SaaS Executives",
description:
"Unblock your barriers to new markets to sell software globally, to companies of all sizes, at all different price points, and increase your revenue growth with Koolis SaaS Commerce Platform.",
link: { title: "Learn more", target: "/settings" },
},
{
icon: "stars",
title: "Product Managers",
description:
"Dont let your billing stack hold you back. Everything you need to grow your SaaS business. Subscription billing, payments, taxes and more, in one unified platform.",
link: { title: "Learn more", target: "/settings" },
},
{
icon: "supervised_user_circle",
title: "Creators",
description:
"Kooli is for artists and creators: writers, designers, software developers, musicians, educators, filmmakers, and anyone in-between. If you make stuff, you can sell that stuff.",
link: { title: "Learn more", target: "/settings" },
},
{
icon: "build_circle",
title: "Developers",
description:
"Focus on building, not billing. Save the blood, sweat and tears for your software development, not your billing stack, with Paddles subscription and commerce platform.",
link: { title: "Learn more", target: "/settings" },
},
{
icon: "monetization_on",
title: "Finance",
description:
"All your payments, subscriptions, taxes, invoices, SaaS metrics, and more in one place. Integrate your product, CRM, and accounting with only one tool, not dozens.",
link: { title: "Learn more", target: "/settings" },
},
{
icon: "group_work",
title: "Enterprise",
description:
"Accept payments in 135+ currencies to better serve your international customers with a single integration. No international business entity required. Fees exclude GST.",
link: { title: "Learn more", target: "/settings" },
},
],
}
},
}
</script>

View File

@@ -0,0 +1,168 @@
<template>
<footer class="flex flex-col px-8 py-16">
<nav class="grid grid-cols-2 gap-4 md:grid-cols-4">
<div class="flex flex-col space-y-2">
<SmartLink
:to="
!$store.state.authUser
? localePath('/')
: localePath(`/${$store.state.login}`)
"
class="my-2"
>
<AppLogo class="h-8" />
</SmartLink>
<span> Kooli </span>
<SmartChangeLanguage />
<ul class="space-y-2">
<li>
<SmartAnchor label="Terms" to="/about/terms" class="footer-nav" />
</li>
<li>
<SmartAnchor
label="Privacy"
to="/about/privacy"
class="footer-nav"
/>
</li>
</ul>
<SmartColorModePicker />
</div>
<div class="flex flex-col space-y-2">
<h4 class="my-2 font-semibold">Solutions</h4>
<ul class="space-y-2">
<li
v-for="(item, index) in navigation.solutions"
:key="`item-${index}`"
>
<SmartAnchor
:label="item.name"
:to="item.link"
class="footer-nav"
/>
</li>
</ul>
</div>
<div class="flex flex-col space-y-2">
<h4 class="my-2 font-semibold">Platform</h4>
<ul class="space-y-2">
<li
v-for="(item, index) in navigation.platform"
:key="`item-${index}`"
>
<SmartAnchor
:label="item.name"
:to="item.link"
class="footer-nav"
/>
</li>
</ul>
</div>
<div class="flex flex-col space-y-2">
<h4 class="my-2 font-semibold">Company</h4>
<ul class="space-y-2">
<li
v-for="(item, index) in navigation.company"
:key="`item-${index}`"
>
<SmartAnchor
:label="item.name"
:to="item.link"
class="footer-nav"
/>
</li>
</ul>
</div>
</nav>
</footer>
</template>
<script>
export default {
data() {
return {
navigation: {
solutions: [
{
name: "SaaS",
link: "/settings",
},
{
name: "Products",
link: "/settings",
},
{
name: "Creators",
link: "/settings",
},
{
name: "Developers",
link: "/settings",
},
{
name: "Finance",
link: "/settings",
},
{
name: "Enterprise",
link: "/settings",
},
],
platform: [
{
name: "Payments",
link: "/settings",
},
{
name: "Subscriptions",
link: "/settings",
},
{
name: "API",
link: "https://docs.kooli.tech/api",
},
{
name: "Guides",
link: "https://docs.kooli.tech/guides",
},
],
company: [
{
name: "About",
link: "/about",
},
{
name: "Jobs",
link: "/about/jobs",
},
{
name: "Integrations",
link: "/about/integrations",
},
{
name: "Support",
link: "",
},
{
name: "Contact",
link: "/about/contact",
},
{
name: "Blog",
link: "https://blog.kooli.tech",
},
],
},
}
},
}
</script>
<style lang="scss" scoped>
.footer-nav {
&:hover,
&:focus {
@apply text-secondaryDark;
}
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div id="globe" class="w-full"></div>
<div ref="globe"></div>
</template>
<script>
@@ -32,12 +32,23 @@ export default {
mounted() {
this.init()
this.animate()
window.addEventListener(
"resize",
() => {
this.camera.aspect =
this.$refs.globe.clientWidth / this.$refs.globe.clientHeight
this.camera.updateProjectionMatrix()
this.renderer.setSize(
this.$refs.globe.clientWidth,
this.$refs.globe.clientHeight
)
},
false
)
},
methods: {
init() {
const container = document.getElementById("globe")
this.globe = new ThreeGlobe()
.globeImageUrl(texture)
.atmosphereColor("#aaaaaa")
@@ -56,9 +67,12 @@ export default {
this.renderer = new THREE.WebGLRenderer({
alpha: true,
})
this.renderer.setSize(container.clientWidth, container.clientHeight)
this.renderer.setSize(
this.$refs.globe.clientWidth,
this.$refs.globe.clientHeight
)
container.appendChild(this.renderer.domElement)
this.$refs.globe.appendChild(this.renderer.domElement)
this.scene = new THREE.Scene()
this.scene.background = null
@@ -67,7 +81,8 @@ export default {
this.scene.add(new THREE.DirectionalLight(0xffffff, 0.8))
this.camera = new THREE.PerspectiveCamera()
this.camera.aspect = container.clientWidth / container.clientHeight
this.camera.aspect =
this.$refs.globe.clientWidth / this.$refs.globe.clientHeight
this.camera.updateProjectionMatrix()
this.camera.position.z = 300
},

View File

@@ -0,0 +1,42 @@
<template>
<div class="flex p-4 relative overflow-hidden">
<div class="relative my-16 z-10 max-w-3xl">
<h2
class="
leading-none
tracking-tighter
font-semibold
text-accent text-5xl
md:text-6xl
lg:text-8xl
"
>
Open Source
</h2>
<h3
class="
text-3xl
my-8
font-mono
text-secondaryDark
md:text-4xl
lg:text-4xl
font-semibold
"
>
API Development Ecosystem
</h3>
<p class="text-lg my-4 text-secondaryLight max-w-4/5">
Millions of developers and companies build, ship, and maintain their
APIs on Hoppscotch the largest and most advanced development platform
in the world.
</p>
<div class="my-8 flex items-center">
<button class="button rounded text-xl">Get Started</button>
</div>
</div>
<div class="lg:absolute lg:inset-y-0 lg:right-0 lg:w-1/2">
<LandingGlobe class="h-64 w-full sm:h-72 md:h-96 lg:w-full lg:h-full" />
</div>
</div>
</template>

View File

@@ -0,0 +1,142 @@
<template>
<div class="flex flex-col p-4">
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
<div
class="inline-flex flex-col transition border rounded-lg border-divider"
>
<div class="flex flex-col p-8">
<p class="font-semibold tracking-widest text-accent">API</p>
<h3
class="
max-w-xl
my-4
text-4xl
font-semibold
leading-tight
tracking-tight
transition
text-secondaryDark
"
>
Powerful and easy-to-use APIs
</h3>
<p class="my-4 text-lg md:w-4/5">
We agonize over the right abstractions so your teams dont need to
stitch together disparate systems or spend months integrating
payments functionality.
</p>
<div class="flex mt-4">
<ButtonPrimary
to="https://docs.kooli.tech/guides"
label="Read the docs"
icon="chevron_right"
reverse
/>
</div>
</div>
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
<div
v-for="(api, index) in apis"
:key="`api-${index}`"
class="inline-flex flex-col p-8"
>
<i class="text-3xl material-icons text-accent">{{ api.icon }}</i>
<div class="flex-grow">
<h2
class="
mt-4
mb-2
text-lg
font-semibold
transition
text-secondaryDark
"
>
{{ api.title }}
</h2>
<p>
{{ api.description }}
</p>
<p class="mt-2">
<SmartLink :to="api.link.target" class="link">
{{ api.link.title }}
<i class="material-icons">chevron_right</i>
</SmartLink>
</p>
</div>
</div>
</div>
</div>
<div
class="
inline-flex
flex-col
p-4
transition
border
rounded-lg
border-divider
bg-primaryLight
"
>
<div class="flex">
<div class="w-3 h-3 mr-2 bg-red-500 rounded-full"></div>
<div class="w-3 h-3 mr-2 bg-yellow-500 rounded-full"></div>
<div class="w-3 h-3 bg-green-500 rounded-full"></div>
</div>
<div class="flex flex-1 p-4 overflow-auto whitespace-pre">
<pre>
/*
** TailwindCSS Configuration File
**
** Docs: https://tailwindcss.com/docs/configuration
** Default: https://github.com/tailwindcss/tailwindcss/blob/master/stubs/defaultConfig.stub.js
*/
module.exports = {
future: {
removeDeprecatedGapUtilities: true,
purgeLayersByDefault: true,
},
experimental: "all",
dark: "class",
corePlugins: {
float: false,
clear: false,
transitionDelay: false,
skew: false,
},
}
</pre>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
apis: [
{
icon: "layers",
title: "Tools for every stack",
description:
"We offer client and server libraries in everything from React, PHP to .NET and iOS.",
link: {
title: "See libraries",
target: "https://docs.kooli.tech/api",
},
},
{
icon: "extension",
title: "Prebuilt integrations",
description:
"Use integrations for systems like Shopify, PayPal, and more.",
link: { title: "Explore partners", target: "/about/integrations" },
},
],
}
},
}
</script>

View File

@@ -0,0 +1,27 @@
<template>
<div class="flex p-4 font-mono space-x-16">
<div v-for="(stat, index) in stats" :key="`stat-${index}`">
<span class="text-xl font-bold">
{{ stat.count }}<span class="text-secondaryLight">+</span>
</span>
<br />
<span class="text-sm">
{{ stat.audience }}
</span>
</div>
</div>
</template>
<script>
export default {
data() {
return {
stats: [
{ count: "350k", audience: "Developers" },
{ count: "10k", audience: "Organizations" },
{ count: "1m", audience: "Requests" },
],
}
},
}
</script>

View File

@@ -0,0 +1,27 @@
<template>
<div class="flex flex-col p-4">
<div class="flex flex-col items-center">
<p
class="mt-4 mb-8 font-semibold tracking-widest text-center text-accent"
>
EMPOWERING CREATORS
</p>
</div>
<div class="grid grid-cols-3 gap-4 md:grid-cols-4 lg:grid-cols-6">
<div
v-for="(user, index) in 12"
:key="`user-${index}`"
class="inline-flex flex-col items-center justify-center p-4"
>
<img
:src="`https://i.pravatar.cc/${Math.floor(
Math.random() * (600 - 400 + 1) + 400
)}`"
alt="Profile picture"
loading="lazy"
class="inline-flex flex-col object-contain object-center h-24 w-24"
/>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,74 @@
<template>
<SmartLink
:to="`${/^\/(?!\/).*$/.test(to) ? localePath(to) : to}`"
:exact="exact"
:blank="blank"
class="inline-flex items-center justify-center focus:outline-none"
:class="[
color
? `text-${color}-500 transition hover:text-${color}-600 focus:text-${color}-600`
: 'transition hover:text-secondaryDark focus:text-secondaryDark',
{ 'opacity-50 cursor-not-allowed': disabled },
{ 'flex-row-reverse': reverse },
]"
:disabled="disabled"
>
<i
v-if="icon"
:class="label ? (reverse ? 'ml-2' : 'mr-2') : ''"
class="material-icons"
>
{{ icon }}
</i>
<SmartIcon
v-if="svg"
:name="svg"
:class="label ? (reverse ? 'ml-2' : 'mr-2') : ''"
class="svg-icons"
/>
{{ label }}
</SmartLink>
</template>
<script>
export default {
props: {
to: {
type: String,
default: "",
},
exact: {
type: Boolean,
default: true,
},
blank: {
type: Boolean,
default: false,
},
label: {
type: String,
default: "",
},
icon: {
type: String,
default: "",
},
svg: {
type: String,
default: "",
},
color: {
type: String,
default: "",
},
disabled: {
type: Boolean,
default: false,
},
reverse: {
type: Boolean,
default: false,
},
},
}
</script>

View File

@@ -0,0 +1,51 @@
<template>
<span>
<tippy
ref="language"
trigger="click"
theme="popover"
arrow
interactive
:animate-fill="false"
>
<template #trigger>
<button
v-tippy="{ animateFill: false, theme: 'tooltip' }"
:title="$t('choose_language')"
>
<span class="mr-2 text-lg">
{{
$i18n.locales.find(({ code }) => code == $i18n.locale).country
| formatCountry
}}
</span>
{{ $i18n.locales.find(({ code }) => code == $i18n.locale).name }}
</button>
</template>
<NuxtLink
v-for="locale in $i18n.locales.filter(
({ code }) => code !== $i18n.locale
)"
:key="locale.code"
class="
inline-flex
items-center
px-4
py-2
transition
rounded-lg
hover:bg-accentLight hover:text-secondaryDark
focus:bg-accentLight focus:text-secondaryDark focus:outline-none
"
:to="switchLocalePath(locale.code)"
>
<span class="mr-2 text-lg">
{{ locale.country | formatCountry }}
</span>
<span class="font-semibold">
{{ locale.name }}
</span>
</NuxtLink>
</tippy>
</span>
</template>

67
components/smart/Link.js Normal file
View File

@@ -0,0 +1,67 @@
/* Vue 2 Functional Component: https://vuejs.org/v2/guide/render-function.html#Functional-Components */
import { mergeData } from "vue-functional-data-merge"
import getLinkTag, { ANCHOR_TAG, FRAMEWORK_LINK } from "~/assets/js/getLinkTag"
const SmartLink = {
functional: true,
props: {
to: {
type: String,
default: "",
},
exact: {
type: Boolean,
default: false,
},
blank: {
type: Boolean,
default: false,
},
},
// It's a convention to rename `createElement` to `h`
render(h, context) {
const tag = getLinkTag(context.props)
// Map our attributes correctly
const attrs = {}
let on = {}
switch (tag) {
case ANCHOR_TAG:
// Map `to` prop to the correct attribute
attrs.href = context.props.to
// Handle `blank` prop
if (context.props.blank) {
attrs.target = "_blank"
attrs.rel = "noopener"
}
// Transform native events to regular events for HTML anchor tag
on = { ...context.data.nativeOn }
delete context.data.nativeOn
break
case FRAMEWORK_LINK:
// Map `to` prop to the correct attribute
attrs.to = context.props.to
// Handle `exact` prop
if (context.props.exact) {
attrs.exact = true
}
break
default:
break
}
// Merge our new data with existing ones
const data = mergeData(context.data, { attrs, on })
// Return a new virtual node
return h(tag, data, context.children)
},
}
export default SmartLink

View File

@@ -6,7 +6,7 @@
<Pane
v-if="!hideNavigationPane"
style="width: auto"
class="overflow-y-auto bg-primaryDark flex items-stretch"
class="overflow-y-auto bg-primaryLight flex items-stretch"
>
<AppSidenav />
</Pane>
@@ -30,7 +30,7 @@
</Pane>
</Splitpanes>
</Pane>
<Pane style="height: auto" class="bg-primaryDark">
<Pane style="height: auto" class="bg-primaryLight">
<button
class="button icon"
@click="hideNavigationPane = !hideNavigationPane"
@@ -120,12 +120,8 @@ export default {
<style lang="scss" scoped>
.splitpanes--vertical > .splitpanes__splitter {
display: none;
width: 0;
background: linear-gradient(90deg, #ccc, #111);
}
.splitpanes--horizontal > .splitpanes__splitter {
display: none;
height: 0;
background: linear-gradient(0deg, #ccc, #111);
}
</style>

View File

@@ -93,11 +93,13 @@ export default {
// Plugins to run before rendering page (https://go.nuxtjs.dev/config-plugins)
plugins: [
"~/plugins/v-tippy",
"~/plugins/vuex-persist",
"~/plugins/v-tooltip",
"~/plugins/vue-rx",
"~/plugins/vue-apollo",
{ src: "~/plugins/web-worker", ssr: false },
"~/plugins/formatCountry.js",
],
// Auto import components (https://go.nuxtjs.dev/config-components)
@@ -225,114 +227,133 @@ export default {
name: "English",
iso: "en-US",
file: "en-US.json",
country: "US",
},
{
code: "es",
name: "Español",
iso: "es-ES",
file: "es-ES.json",
country: "ES",
},
{
code: "fr",
name: "Français",
iso: "fr-FR",
file: "fr-FR.json",
country: "FR",
},
{
code: "fa",
name: "Farsi",
iso: "fa-IR",
file: "fa-IR.json",
country: "IR",
},
{
code: "pt",
name: "Português",
iso: "pt-PT",
file: "pt-PT.json",
country: "PT",
},
{
code: "pt-br",
name: "Português Brasileiro",
iso: "pt-BR",
file: "pt-BR.json",
country: "BR",
},
{
code: "cn",
name: "简体中文",
iso: "zh-CN",
file: "zh-CN.json",
country: "CN",
},
{
code: "tw",
name: "繁體中文",
iso: "zh-TW",
file: "zh-TW.json",
country: "TW",
},
{
code: "id",
name: "Bahasa Indonesia",
iso: "id-ID",
file: "id-ID.json",
country: "ID",
},
{
code: "tr",
name: "Türkçe",
iso: "tr-TR",
file: "tr-TR.json",
country: "TR",
},
{
code: "de",
name: "Deutsch",
iso: "de-DE",
file: "de-DE.json",
country: "DE",
},
{
code: "ja",
name: "日本語",
iso: "ja-JP",
file: "ja-JP.json",
country: "JP",
},
{
code: "ko",
name: "한국어",
iso: "ko-KR",
file: "ko-KR.json",
country: "KR",
},
{
code: "in",
name: "हिंदी",
iso: "in-IN",
file: "in-IN.json",
country: "IN",
},
{
code: "bn",
name: "Bengali",
iso: "bn-BD",
file: "bn-BD.json",
country: "BD",
},
{
code: "ml",
name: "മലയാളം",
iso: "ml-ML",
file: "ml-ML.json",
country: "ML",
},
{
code: "vi",
name: "Vietnamese",
iso: "vi-VN",
file: "vi-VN.json",
country: "VN",
},
{
code: "nl",
name: "Dutch",
iso: "nl-BE",
file: "nl-BE.json",
country: "BE",
},
{
code: "nb",
name: "Norwegian (Bokmål)",
iso: "nb-NO",
file: "nb-NO.json",
country: "NO",
},
],
defaultLocale: "en",

59
package-lock.json generated
View File

@@ -38,8 +38,10 @@
"v-tooltip": "^2.1.3",
"vue-apollo": "^3.0.7",
"vue-cli-plugin-apollo": "^0.22.2",
"vue-functional-data-merge": "^3.1.0",
"vue-github-button": "^1.3.0",
"vue-rx": "^6.2.0",
"vue-tippy": "^4.10.2",
"vuejs-auto-complete": "^0.9.0",
"vuex-persist": "^3.1.3",
"yargs-parser": "^20.2.9"
@@ -16826,6 +16828,11 @@
"node": ">=10.17.0"
}
},
"node_modules/humps": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/humps/-/humps-2.0.1.tgz",
"integrity": "sha1-3QLqYIG9BWjcXQcxhEY5V7qe+ao="
},
"node_modules/husky": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/husky/-/husky-6.0.0.tgz",
@@ -27863,6 +27870,14 @@
"node": "*"
}
},
"node_modules/tippy.js": {
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-4.3.5.tgz",
"integrity": "sha512-NDq3efte8nGK6BOJ1dDN1/WelAwfmh3UtIYXXck6+SxLzbIQNZE/cmRSnwScZ/FyiKdIcvFHvYUgqmoGx8CcyA==",
"dependencies": {
"popper.js": "^1.14.7"
}
},
"node_modules/tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@@ -29230,6 +29245,11 @@
"node": ">=6.0.0"
}
},
"node_modules/vue-functional-data-merge": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/vue-functional-data-merge/-/vue-functional-data-merge-3.1.0.tgz",
"integrity": "sha512-leT4kdJVQyeZNY1kmnS1xiUlQ9z1B/kdBFCILIjYYQDqZgLqCLa0UhjSSeRX6c3mUe6U5qYeM8LrEqkHJ1B4LA=="
},
"node_modules/vue-github-button": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/vue-github-button/-/vue-github-button-1.3.0.tgz",
@@ -29491,6 +29511,18 @@
"resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw=="
},
"node_modules/vue-tippy": {
"version": "4.10.2",
"resolved": "https://registry.npmjs.org/vue-tippy/-/vue-tippy-4.10.2.tgz",
"integrity": "sha512-o1E4Q7gJHrL5VKpPF10Mdp9Grrqi9JGUBrkOQQUmTrklxctYvjpsSxePZVM0mgfGfRFAboOClw33X1ueB4Byjw==",
"dependencies": {
"humps": "^2.0.1",
"tippy.js": "^4.3.5"
},
"peerDependencies": {
"vue": "^2.5.9"
}
},
"node_modules/vue-toasted": {
"version": "1.1.28",
"resolved": "https://registry.npmjs.org/vue-toasted/-/vue-toasted-1.1.28.tgz",
@@ -45572,6 +45604,11 @@
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
},
"humps": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/humps/-/humps-2.0.1.tgz",
"integrity": "sha1-3QLqYIG9BWjcXQcxhEY5V7qe+ao="
},
"husky": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/husky/-/husky-6.0.0.tgz",
@@ -54714,6 +54751,14 @@
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz",
"integrity": "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA=="
},
"tippy.js": {
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-4.3.5.tgz",
"integrity": "sha512-NDq3efte8nGK6BOJ1dDN1/WelAwfmh3UtIYXXck6+SxLzbIQNZE/cmRSnwScZ/FyiKdIcvFHvYUgqmoGx8CcyA==",
"requires": {
"popper.js": "^1.14.7"
}
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@@ -55855,6 +55900,11 @@
}
}
},
"vue-functional-data-merge": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/vue-functional-data-merge/-/vue-functional-data-merge-3.1.0.tgz",
"integrity": "sha512-leT4kdJVQyeZNY1kmnS1xiUlQ9z1B/kdBFCILIjYYQDqZgLqCLa0UhjSSeRX6c3mUe6U5qYeM8LrEqkHJ1B4LA=="
},
"vue-github-button": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/vue-github-button/-/vue-github-button-1.3.0.tgz",
@@ -56094,6 +56144,15 @@
"resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw=="
},
"vue-tippy": {
"version": "4.10.2",
"resolved": "https://registry.npmjs.org/vue-tippy/-/vue-tippy-4.10.2.tgz",
"integrity": "sha512-o1E4Q7gJHrL5VKpPF10Mdp9Grrqi9JGUBrkOQQUmTrklxctYvjpsSxePZVM0mgfGfRFAboOClw33X1ueB4Byjw==",
"requires": {
"humps": "^2.0.1",
"tippy.js": "^4.3.5"
}
},
"vue-toasted": {
"version": "1.1.28",
"resolved": "https://registry.npmjs.org/vue-toasted/-/vue-toasted-1.1.28.tgz",

View File

@@ -54,8 +54,10 @@
"v-tooltip": "^2.1.3",
"vue-apollo": "^3.0.7",
"vue-cli-plugin-apollo": "^0.22.2",
"vue-functional-data-merge": "^3.1.0",
"vue-github-button": "^1.3.0",
"vue-rx": "^6.2.0",
"vue-tippy": "^4.10.2",
"vuejs-auto-complete": "^0.9.0",
"vuex-persist": "^3.1.3",
"yargs-parser": "^20.2.9"

View File

@@ -1,42 +1,10 @@
<template>
<div class="flex p-4 justify-between">
<div class="max-w-md">
<h2
class="
leading-none
tracking-tighter
font-semibold
text-accent text-5xl
md:text-6xl
lg:text-8xl
"
>
Open Source
</h2>
<h3
class="
text-3xl
my-8
font-mono
text-secondaryDark
md:text-4xl
lg:text-4xl
font-semibold
"
>
API Development Ecosystem
</h3>
<p class="text-lg my-4 text-secondaryLight max-w-4/5">
Millions of developers and companies build, ship, and maintain their
APIs on Hoppscotch the largest and most advanced development platform
in the world.
</p>
<div class="my-8 flex items-center">
<button class="button rounded-2xl text-xl">Get Started</button>
<button class="button icon rounded-2xl text-xl">Login</button>
<AppGitHubStarButton class="ml-8" />
</div>
</div>
<AppGlobe />
<div>
<LandingHero />
<LandingStats />
<LandingUsers />
<LandingFeatures />
<LandingPackage />
<LandingFooter />
</div>
</template>

7
plugins/formatCountry.js Normal file
View File

@@ -0,0 +1,7 @@
import Vue from "vue"
Vue.filter("formatCountry", (countryCode) =>
String.fromCodePoint(
...[...countryCode.toUpperCase()].map((x) => 0x1f1a5 + x.charCodeAt())
)
)

5
plugins/v-tippy.js Normal file
View File

@@ -0,0 +1,5 @@
import Vue from "vue"
import VueTippy, { TippyComponent } from "vue-tippy"
Vue.use(VueTippy)
Vue.component("Tippy", TippyComponent)