From 0202375db8ccd03d98ed6c2ffbb800b67c026639 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Thu, 23 Nov 2023 09:16:23 -0400 Subject: content, css: vertical tooltips + basic external parsing --- src/content/dependencies/linkContribution.js | 14 +- src/content/dependencies/linkExternalAsIcon.js | 265 +++++++++++++++++++++---- 2 files changed, 235 insertions(+), 44 deletions(-) (limited to 'src/content') diff --git a/src/content/dependencies/linkContribution.js b/src/content/dependencies/linkContribution.js index 5bc398de..ef61c766 100644 --- a/src/content/dependencies/linkContribution.js +++ b/src/content/dependencies/linkContribution.js @@ -1,15 +1,8 @@ import {empty} from '#sugar'; export default { - contentDependencies: [ - 'linkArtist', - 'linkExternalAsIcon', - ], - - extraDependencies: [ - 'html', - 'language', - ], + contentDependencies: ['linkArtist', 'linkExternalAsIcon'], + extraDependencies: ['html', 'language'], relations(relation, contribution) { const relations = {}; @@ -85,7 +78,8 @@ export default { [html.joinChildren]: '', class: 'icons-tooltip-content', }, - relations.artistIcons)), + relations.artistIcons + .map(icon => icon.slot('withText', true)))), ]; } diff --git a/src/content/dependencies/linkExternalAsIcon.js b/src/content/dependencies/linkExternalAsIcon.js index cd168992..d3ed9122 100644 --- a/src/content/dependencies/linkExternalAsIcon.js +++ b/src/content/dependencies/linkExternalAsIcon.js @@ -1,6 +1,202 @@ -// TODO: Define these as extra dependencies and pass them somewhere -const BANDCAMP_DOMAINS = ['bc.s3m.us', 'music.solatrux.com']; -const MASTODON_DOMAINS = ['types.pl']; +import {stitchArrays} from '#sugar'; + +const fallbackDescriptor = { + icon: 'globe', + string: 'external', + + normal: 'domain', + compact: 'domain', +}; + +// TODO: Define all this stuff in data! +const externalSpec = [ + { + matchDomain: 'bandcamp.com', + + icon: 'bandcamp', + string: 'bandcamp', + + compact: 'handle', + + handle: {domain: /^[^.]*/}, + }, + + { + matchDomains: ['bc.s3m.us', 'music.solatrux.com'], + + icon: 'bandcamp', + string: 'bandcamp', + + normal: 'domain', + compact: 'domain', + }, + + { + matchDomains: ['types.pl'], + + icon: 'mastodon', + string: 'mastodon', + + compact: 'domain', + }, + + { + matchDomains: ['youtube.com', 'youtu.be'], + + icon: 'youtube', + string: 'youtube', + + compact: 'handle', + + handle: { + pathname: /^(@.*?)\/?$/, + }, + }, + + { + matchDomain: 'soundcloud.com', + + icon: 'soundcloud', + string: 'soundcloud', + + compact: 'handle', + + handle: /[^/]*\/?$/, + }, + + { + matchDomain: 'tumblr.com', + + icon: 'tumblr', + string: 'tumblr', + + compact: 'handle', + + handle: {domain: /^[^.]*/}, + }, + + { + matchDomain: 'twitter.com', + + icon: 'twitter', + string: 'twitter', + + compact: 'handle', + + handle: { + prefix: '@', + pathname: /^@?.*\/?$/, + }, + }, + + { + matchDomain: 'deviantart.com', + + icon: 'deviantart', + string: 'deviantart', + }, + + { + matchDomain: 'instagram.com', + + icon: 'instagram', + string: 'instagram', + }, + + { + matchDomain: 'newgrounds.com', + + icon: 'newgrounds', + string: 'newgrounds', + }, +]; + +function determineLinkText(url, descriptor, {language}) { + const prefix = 'misc.external'; + + const { + hostname: domain, + pathname, + } = new URL(url); + + let normal = null; + let compact = null; + + const place = language.$(prefix, descriptor.string); + + if (descriptor.normal === 'domain') { + normal = language.$(prefix, 'withDomain', {place, domain}); + } + + if (descriptor.compact === 'domain') { + compact = domain.replace(/^www\./, ''); + } + + let handle = null; + + if (descriptor.handle) { + let regexen = []; + let tests = []; + + let handlePrefix = ''; + + if (descriptor.handle instanceof RegExp) { + regexen.push(descriptor.handle); + tests.push(url); + } else { + for (const [key, value] of Object.entries(descriptor.handle)) { + switch (key) { + case 'prefix': + handlePrefix = value; + continue; + + case 'url': + tests.push(url); + break; + + case 'domain': + case 'hostname': + tests.push(domain); + break; + + case 'path': + case 'pathname': + tests.push(pathname.slice(1)); + break; + + default: + tests.push(''); + break; + } + + regexen.push(value); + } + } + + for (const {regex, test} of stitchArrays({ + regex: regexen, + test: tests, + })) { + const match = test.match(regex); + if (match) { + handle = handlePrefix + (match[1] ?? match[0]); + break; + } + } + } + + if (descriptor.compact === 'handle') { + compact = handle; + } + + if (normal === 'handle' && handle) { + normal = language.$(prefix, 'withHandle', {place, handle}); + } + + normal ??= language.$(prefix, descriptor.string); + + return {normal, compact}; +} export default { extraDependencies: ['html', 'language', 'to'], @@ -9,38 +205,39 @@ export default { return {url}; }, - generate(data, {html, language, to}) { - const domain = new URL(data.url).hostname; - const [id, msg] = ( - domain.includes('bandcamp.com') - ? ['bandcamp', language.$('misc.external.bandcamp')] - : BANDCAMP_DOMAINS.includes(domain) - ? ['bandcamp', language.$('misc.external.bandcamp.domain', {domain})] - : MASTODON_DOMAINS.includes(domain) - ? ['mastodon', language.$('misc.external.mastodon.domain', {domain})] - : domain.includes('youtu') - ? ['youtube', language.$('misc.external.youtube')] - : domain.includes('soundcloud') - ? ['soundcloud', language.$('misc.external.soundcloud')] - : domain.includes('tumblr.com') - ? ['tumblr', language.$('misc.external.tumblr')] - : domain.includes('twitter.com') - ? ['twitter', language.$('misc.external.twitter')] - : domain.includes('deviantart.com') - ? ['deviantart', language.$('misc.external.deviantart')] - : domain.includes('instagram.com') - ? ['instagram', language.$('misc.external.bandcamp')] - : domain.includes('newgrounds.com') - ? ['newgrounds', language.$('misc.external.newgrounds')] - : ['globe', language.$('misc.external.domain', {domain})]); + slots: { + withText: {type: 'boolean'}, + }, + + generate(data, slots, {html, language, to}) { + const {hostname: domain} = new URL(data.url); + + const descriptor = + externalSpec.find(({matchDomain, matchDomains}) => { + const compare = d => domain.includes(d); + if (matchDomain && compare(matchDomain)) return true; + if (matchDomains && matchDomains.some(compare)) return true; + return false; + }) ?? fallbackDescriptor; + + const {normal: normalText, compact: compactText} = + determineLinkText(data.url, descriptor, {language}); return html.tag('a', - {href: data.url, class: 'icon'}, - html.tag('svg', [ - html.tag('title', msg), - html.tag('use', { - href: to('shared.staticIcon', id), - }), - ])); + {href: data.url, class: ['icon', slots.withText && 'has-text']}, + [ + html.tag('svg', [ + !slots.withText && + html.tag('title', normalText), + + html.tag('use', { + href: to('shared.staticIcon', descriptor.icon), + }), + ]), + + slots.withText && + html.tag('span', {class: 'icon-text'}, + compactText ?? normalText), + ]); }, }; -- cgit 1.3.0-6-gf8a5