@stellar-expert/ui-framework 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.npmrc +1 -0
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/account/account-address.js +127 -0
- package/account/account-address.scss +20 -0
- package/account/available-balance.js +21 -0
- package/account/identicon.js +91 -0
- package/account/identicon.scss +6 -0
- package/account/signer-key.js +65 -0
- package/api/api-cache.js +5 -0
- package/api/explorer-api-call.js +37 -0
- package/api/explorer-api-hooks.js +202 -0
- package/api/explorer-api-paginated-list-hooks.js +439 -0
- package/api/explorer-batch-info-loader.js +85 -0
- package/api/explorer-tx-api.js +29 -0
- package/asset/amount.js +53 -0
- package/asset/asset-icon.js +34 -0
- package/asset/asset-issuer.js +22 -0
- package/asset/asset-link.js +93 -0
- package/asset/asset-link.scss +26 -0
- package/asset/asset-list-hooks.js +60 -0
- package/asset/asset-meta-hooks.js +78 -0
- package/asset/asset-selector.js +72 -0
- package/asset/asset-selector.scss +41 -0
- package/basic-styles/base.scss +53 -0
- package/basic-styles/form.scss +90 -0
- package/basic-styles/grid.scss +388 -0
- package/basic-styles/link.scss +17 -0
- package/basic-styles/lists.scss +68 -0
- package/basic-styles/loader.scss +22 -0
- package/basic-styles/responsive.scss +29 -0
- package/basic-styles/scrollbars.scss +19 -0
- package/basic-styles/spacing.scss +50 -0
- package/basic-styles/table.scss +104 -0
- package/basic-styles/themes.scss +59 -0
- package/basic-styles/typography.scss +191 -0
- package/basic-styles/variables.scss +26 -0
- package/claimable-balance/claimable-balance-claimants.js +12 -0
- package/contract/sc-val.js +70 -0
- package/contract/wasm-section-parser.js +159 -0
- package/contract/wasm-section-reader.js +79 -0
- package/contract/xdr-reader.js +148 -0
- package/controls/active-icon.scss +23 -0
- package/controls/button-group.js +20 -0
- package/controls/button-group.scss +48 -0
- package/controls/button.js +74 -0
- package/controls/button.scss +148 -0
- package/controls/code-block.js +35 -0
- package/controls/code-block.scss +74 -0
- package/controls/dropdown.js +281 -0
- package/controls/dropdown.scss +161 -0
- package/controls/external-link.js +5 -0
- package/controls/info-tooltip.js +17 -0
- package/controls/info-tooltip.scss +4 -0
- package/controls/slider.js +20 -0
- package/controls/slider.scss +22 -0
- package/controls/tabs.js +94 -0
- package/controls/tabs.scss +73 -0
- package/controls/tooltip.js +214 -0
- package/controls/tooltip.scss +97 -0
- package/controls/update-highlighter.js +28 -0
- package/controls/update-highlighter.scss +10 -0
- package/date/date-selector.js +44 -0
- package/date/elapsed-time.js +22 -0
- package/date/utc-timestamp.js +23 -0
- package/dex/price-dynamic.js +45 -0
- package/dex/price-dynamic.scss +36 -0
- package/directory/directory-hooks.js +89 -0
- package/effect/effect-description.js +289 -0
- package/effect/op-effects-view.js +11 -0
- package/effect/op-effects.scss +8 -0
- package/errors/error-boundary.js +98 -0
- package/fonts/font.scss +23 -0
- package/fonts/icons/config.json +1858 -0
- package/fonts/icons/icons-embedded.scss +644 -0
- package/horizon/horizon-account-helpers.js +104 -0
- package/horizon/horizon-client-helpers.js +52 -0
- package/horizon/horizon-ledger-helpers.js +36 -0
- package/horizon/horizon-orderbook-helpers.js +14 -0
- package/horizon/horizon-trades-helper.js +89 -0
- package/horizon/horizon-transaction-helpers.js +36 -0
- package/index.js +85 -0
- package/index.scss +16 -0
- package/interaction/autofocus.js +9 -0
- package/interaction/block-select.js +54 -0
- package/interaction/block-select.scss +24 -0
- package/interaction/copy-to-clipboard.js +19 -0
- package/interaction/inline-progress.js +16 -0
- package/interaction/qr-code.js +45 -0
- package/interaction/responsive.js +21 -0
- package/interaction/spoiler.js +40 -0
- package/interaction/spoiler.scss +9 -0
- package/interaction/theme-selector.js +11 -0
- package/ledger/ledger-entry-href-formatter.js +22 -0
- package/ledger/ledger-entry-link.js +59 -0
- package/meta/page-meta-tags.js +229 -0
- package/module/dynamic-module.js +48 -0
- package/package.json +41 -0
- package/state/on-screen-hooks.js +23 -0
- package/state/page-visibility-helpers.js +17 -0
- package/state/page-visibility-hooks.js +12 -0
- package/state/screen-orientation-hooks.js +16 -0
- package/state/state-hooks.js +77 -0
- package/state/stellar-network-hooks.js +45 -0
- package/state/theme.js +29 -0
- package/stellar/key-type.js +92 -0
- package/stellar/ledger-generic-id.js +40 -0
- package/stellar/signature-hint-utils.js +65 -0
- package/toast/toast-notification-instance.js +43 -0
- package/toast/toast-notification.js +46 -0
- package/toast/toast-notifications-block.js +55 -0
- package/toast/toast-notifications.scss +166 -0
- package/tx/op-accounting-changes.js +27 -0
- package/tx/op-description-view.js +925 -0
- package/tx/op-description.scss +98 -0
- package/tx/op-icon.js +93 -0
- package/tx/parser/op-balance-changes.js +62 -0
- package/tx/parser/op-descriptor.js +49 -0
- package/tx/parser/tx-details-parser.js +75 -0
- package/tx/parser/tx-matcher.js +372 -0
- package/tx/parser/type-filter-matcher.js +127 -0
- package/tx/tx-fee-effect.js +14 -0
- package/tx/tx-list-hooks.js +19 -0
- package/tx/tx-operations-list.js +105 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import cn from 'classnames'
|
|
3
|
+
import {shortenString} from '@stellar-expert/formatter'
|
|
4
|
+
import {AssetDescriptor} from '@stellar-expert/asset-descriptor'
|
|
5
|
+
import {useStellarNetwork} from '../state/stellar-network-hooks'
|
|
6
|
+
import {formatExplorerLink} from '../ledger/ledger-entry-href-formatter'
|
|
7
|
+
import {AccountAddress} from '../account/account-address'
|
|
8
|
+
import {useAssetMeta} from './asset-meta-hooks'
|
|
9
|
+
import {AssetIcon} from './asset-icon'
|
|
10
|
+
import {AssetIssuer} from './asset-issuer'
|
|
11
|
+
import './asset-link.scss'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Explorer asset link
|
|
15
|
+
* @param {String|AssetDescriptor|Asset} asset - Asset name/descriptor
|
|
16
|
+
* @param {Boolean|String} [link] - Reference link
|
|
17
|
+
* @param {Boolean} [issuer] - Whether to show asset issuer
|
|
18
|
+
* @param {Boolean} [icon] - Wheter to show asset icon
|
|
19
|
+
* @param {String} [className] - Optional CSS class name
|
|
20
|
+
* @param {{}} [style] - Optional CSS style
|
|
21
|
+
* @param {*} [children] - Optional inner link text
|
|
22
|
+
* @constructor
|
|
23
|
+
*/
|
|
24
|
+
export const AssetLink = React.memo(function AssetLink({asset, link, issuer, icon, className, style, children: innerText}) {
|
|
25
|
+
if (!(asset instanceof AssetDescriptor)) {
|
|
26
|
+
asset = AssetDescriptor.parse(asset)
|
|
27
|
+
}
|
|
28
|
+
useStellarNetwork()
|
|
29
|
+
const meta = useAssetMeta(asset)
|
|
30
|
+
|
|
31
|
+
if (!asset)
|
|
32
|
+
return null
|
|
33
|
+
|
|
34
|
+
let children = innerText
|
|
35
|
+
if (!innerText) {
|
|
36
|
+
if (asset.poolId) {
|
|
37
|
+
if (meta) {
|
|
38
|
+
const [assetA, assetB] = meta.assets.map(a => AssetDescriptor.parse(a.asset))
|
|
39
|
+
children = <span title={'Liquidity pool ' + asset.poolId} className="nowrap">
|
|
40
|
+
<span>
|
|
41
|
+
<AssetWarningStatus meta={assetA}/>
|
|
42
|
+
{icon !== false && <AssetIcon asset={assetA}/>}
|
|
43
|
+
{assetA.code}
|
|
44
|
+
{issuer === true && <AssetIssuer asset={assetA}/>}
|
|
45
|
+
</span>
|
|
46
|
+
<span style={{fontSize: '0.7em'}}> <i className="icon icon-plus dimmed"/> </span>
|
|
47
|
+
<span>
|
|
48
|
+
<AssetWarningStatus meta={assetB}/>
|
|
49
|
+
{icon !== false && <AssetIcon asset={assetB}/>}
|
|
50
|
+
{assetB.code}
|
|
51
|
+
{issuer === true && <AssetIssuer asset={assetB}/>}
|
|
52
|
+
</span>
|
|
53
|
+
</span>
|
|
54
|
+
} else {
|
|
55
|
+
children = <>{shortenString(asset.poolId)}</>
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
children = <>
|
|
59
|
+
<AssetWarningStatus meta={meta}/>
|
|
60
|
+
{icon !== false && <AssetIcon asset={asset}/>}
|
|
61
|
+
{!!asset.code && asset.code}
|
|
62
|
+
{!!asset.isContract && <AccountAddress account={asset.contract} chars={8} link={false} icon={false}/>}
|
|
63
|
+
{issuer !== false && <AssetIssuer asset={asset}/>}
|
|
64
|
+
</>
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const props = {
|
|
69
|
+
'aria-label': asset.toString(),
|
|
70
|
+
className: cn('asset-link', className),
|
|
71
|
+
style,
|
|
72
|
+
children
|
|
73
|
+
}
|
|
74
|
+
if (link === false)
|
|
75
|
+
return <span {...props}/>
|
|
76
|
+
if (typeof link === 'string') {
|
|
77
|
+
props.href = link
|
|
78
|
+
} else {
|
|
79
|
+
props.href = asset.poolId ?
|
|
80
|
+
formatExplorerLink('liquidity-pool', asset.poolId) :
|
|
81
|
+
formatExplorerLink('asset', asset.toString())
|
|
82
|
+
if (window.origin !== explorerFrontendOrigin) {
|
|
83
|
+
props.target = '_blank'
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return <a {...props}/>
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
function AssetWarningStatus({meta}) {
|
|
90
|
+
if (!meta?.unsafe)
|
|
91
|
+
return null
|
|
92
|
+
return <i className="icon icon-warning color-warning" title="Warning: asset reported as unsafe"/>
|
|
93
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
.asset-link {
|
|
2
|
+
white-space: nowrap;
|
|
3
|
+
|
|
4
|
+
.asset-issuer {
|
|
5
|
+
color: var(--color-dimmed);
|
|
6
|
+
font-size: 0.65em;
|
|
7
|
+
padding-left: 0.1em;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.asset-icon {
|
|
11
|
+
display: inline-block;
|
|
12
|
+
color: var(--color-text);
|
|
13
|
+
width: 1.1em;
|
|
14
|
+
height: 1.1em;
|
|
15
|
+
vertical-align: middle;
|
|
16
|
+
margin-right: .1em;
|
|
17
|
+
line-height: 1.1;
|
|
18
|
+
background: transparent center center no-repeat;
|
|
19
|
+
background-size: contain;
|
|
20
|
+
margin-top: -0.2em;
|
|
21
|
+
|
|
22
|
+
&.icon {
|
|
23
|
+
margin-top: 0;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React, {useState} from 'react'
|
|
2
|
+
import {throttle} from 'throttle-debounce'
|
|
3
|
+
import equal from 'react-fast-compare'
|
|
4
|
+
import {stringifyQuery} from '@stellar-expert/navigation'
|
|
5
|
+
import {fetchExplorerApi} from '../api/explorer-api-call'
|
|
6
|
+
import {useDeepEffect} from '../state/state-hooks'
|
|
7
|
+
import {getCurrentStellarNetwork} from '../state/stellar-network-hooks'
|
|
8
|
+
|
|
9
|
+
const defaults = {
|
|
10
|
+
limit: 20,
|
|
11
|
+
order: 'desc',
|
|
12
|
+
sort: 'rating'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getCurrentCursor(data) {
|
|
16
|
+
if (!data || !data.length) return undefined
|
|
17
|
+
return data[data.length - 1].paging_token
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
*
|
|
22
|
+
* @param {Object} params
|
|
23
|
+
* @return {{assets: Array<Object>, loadPage: Function, loading: Boolean}}
|
|
24
|
+
*/
|
|
25
|
+
export function useAssetList(params) {
|
|
26
|
+
const [loading, setLoading] = useState(false),
|
|
27
|
+
[assets, setAssets] = useState({data: [], params}),
|
|
28
|
+
loadPage = throttle(1000, function () {
|
|
29
|
+
if (loading) return
|
|
30
|
+
const requestParams = {
|
|
31
|
+
...defaults, ...params
|
|
32
|
+
}
|
|
33
|
+
if (equal(params, assets.params)) {
|
|
34
|
+
requestParams.cursor = getCurrentCursor(assets.data)
|
|
35
|
+
}
|
|
36
|
+
const endpoint = getCurrentStellarNetwork() + '/asset' + stringifyQuery(requestParams)
|
|
37
|
+
setLoading(true)
|
|
38
|
+
fetchExplorerApi(endpoint)
|
|
39
|
+
.then(res => {
|
|
40
|
+
const {records} = res._embedded
|
|
41
|
+
setAssets(existing => {
|
|
42
|
+
let data = records
|
|
43
|
+
if (equal(params, existing.params)) {
|
|
44
|
+
data = [...existing.data, ...records]
|
|
45
|
+
}
|
|
46
|
+
return {data, params}
|
|
47
|
+
})
|
|
48
|
+
setLoading(false)
|
|
49
|
+
})
|
|
50
|
+
.catch(e => console.error(e))
|
|
51
|
+
.finally(() => {
|
|
52
|
+
setLoading(false)
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
useDeepEffect(() => {
|
|
56
|
+
loadPage()
|
|
57
|
+
}, [params])
|
|
58
|
+
|
|
59
|
+
return {assets: assets.data, loadPage, loading}
|
|
60
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {useEffect, useState} from 'react'
|
|
2
|
+
import {AssetDescriptor} from '@stellar-expert/asset-descriptor'
|
|
3
|
+
import {stringifyQuery} from '@stellar-expert/navigation'
|
|
4
|
+
import {InMemoryClientCache} from '@stellar-expert/client-cache'
|
|
5
|
+
import {fetchExplorerApi} from '../api/explorer-api-call'
|
|
6
|
+
import {ExplorerBatchInfoLoader} from '../api/explorer-batch-info-loader'
|
|
7
|
+
import {getCurrentStellarNetwork} from '../state/stellar-network-hooks'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef AssetBasicTomlInfo
|
|
11
|
+
* @property {String} name
|
|
12
|
+
* @property {String} orgName
|
|
13
|
+
* @property {String} image
|
|
14
|
+
* @property {Number} decimals
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef AssetMeta
|
|
19
|
+
* @property {String} name
|
|
20
|
+
* @property {String} domain
|
|
21
|
+
* @property {AssetBasicTomlInfo} toml_info
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const cache = new InMemoryClientCache()
|
|
25
|
+
|
|
26
|
+
const loader = new ExplorerBatchInfoLoader(batch => {
|
|
27
|
+
return fetchExplorerApi(getCurrentStellarNetwork() + '/asset/meta' + stringifyQuery({asset: batch, origin: window.location.origin}))
|
|
28
|
+
}, entry => {
|
|
29
|
+
cache.set(entry.name, entry)
|
|
30
|
+
return {
|
|
31
|
+
key: entry.name,
|
|
32
|
+
info: entry
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
function retrieveFromCache(asset) {
|
|
37
|
+
//try to load from the shared cache
|
|
38
|
+
const cachedEntry = cache.get(asset)
|
|
39
|
+
if (cachedEntry && !cachedEntry.isStale) {
|
|
40
|
+
if (!cachedEntry.isExpired)
|
|
41
|
+
return cachedEntry.data//everything is up to date - no need to re-fetch
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function normalizeAssetName(asset) {
|
|
46
|
+
if (!asset)
|
|
47
|
+
return null
|
|
48
|
+
if (typeof asset === 'string' && asset.length === 56)
|
|
49
|
+
return asset //contract id
|
|
50
|
+
return AssetDescriptor.parse(asset).toFQAN()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
*
|
|
55
|
+
* @param {AssetDescriptor|String} asset
|
|
56
|
+
* @return {AssetMeta}
|
|
57
|
+
*/
|
|
58
|
+
export function useAssetMeta(asset) {
|
|
59
|
+
asset = normalizeAssetName(asset)
|
|
60
|
+
const [assetInfo, setAssetInfo] = useState(retrieveFromCache(asset))
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (!asset)
|
|
63
|
+
return
|
|
64
|
+
const cached = retrieveFromCache(asset)
|
|
65
|
+
setAssetInfo(cached)
|
|
66
|
+
if (cached)
|
|
67
|
+
return
|
|
68
|
+
let unloaded = false
|
|
69
|
+
//load from the server
|
|
70
|
+
loader.loadEntry(asset)
|
|
71
|
+
.then(a => !unloaded && setAssetInfo(a))
|
|
72
|
+
return () => {
|
|
73
|
+
unloaded = true
|
|
74
|
+
}
|
|
75
|
+
}, [asset])
|
|
76
|
+
|
|
77
|
+
return assetInfo
|
|
78
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, {useCallback, useRef, useState} from 'react'
|
|
2
|
+
import {Dropdown} from '../controls/dropdown'
|
|
3
|
+
import {AssetLink} from './asset-link'
|
|
4
|
+
import {useAssetList} from './asset-list-hooks'
|
|
5
|
+
import './asset-selector.scss'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {AssetSelectorOnAssetChanged} onChange - On asset selected callback
|
|
9
|
+
* @param {String} [value] - Selected asset
|
|
10
|
+
* @param {String[]} [predefinedAssets] - Optional lists of predefined assets that should be shown at the top of the dropdown list
|
|
11
|
+
* @param {Boolean} [restricted] - If set, the selector is limited to the predefined assets list only
|
|
12
|
+
* @param {String} [title] - Dropdown selector title
|
|
13
|
+
* @param {String} [expanded] - Expanded by default
|
|
14
|
+
* @return {JSX.Element}
|
|
15
|
+
* @constructor
|
|
16
|
+
*/
|
|
17
|
+
export function AssetSelector({value, predefinedAssets, onChange, restricted, title, expanded}) {
|
|
18
|
+
const [search, setSearch] = useState('')
|
|
19
|
+
const searchRef = useRef()
|
|
20
|
+
const options = []
|
|
21
|
+
|
|
22
|
+
const focusSearch = useCallback(() => {
|
|
23
|
+
setTimeout(() => searchRef.current?.focus(), 200)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
if (predefinedAssets) {
|
|
27
|
+
for (const asset of predefinedAssets) {
|
|
28
|
+
options.push({
|
|
29
|
+
value: asset,
|
|
30
|
+
title: <AssetLink link={false} asset={asset}/>,
|
|
31
|
+
hidden: search && !asset.split('-')[0].toLowerCase().includes(search.toLowerCase())
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let loadNextPage
|
|
37
|
+
if (!restricted) {
|
|
38
|
+
const {assets, loadPage, loading} = useAssetList({search: search?.trim() || undefined})
|
|
39
|
+
for (let {asset} of assets) {
|
|
40
|
+
if (!predefinedAssets || !predefinedAssets.includes(asset)) {
|
|
41
|
+
options.push({value: asset, title: <AssetLink link={false} asset={asset}/>})
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (!options.filter(opt => !opt.hidden).length) {
|
|
45
|
+
if (loading) {
|
|
46
|
+
options.push({value: '...', disabled: true, title: <div className="loader"/>})
|
|
47
|
+
} else {
|
|
48
|
+
options.push({
|
|
49
|
+
value: 'no',
|
|
50
|
+
disabled: true,
|
|
51
|
+
title: <div className="dimmed text-center text-small">(not found)</div>
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
loadNextPage = loadPage
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return <Dropdown solo className="asset-selector" options={options} value={value} onOpen={focusSearch} title={title} expanded={expanded}
|
|
59
|
+
showToggle={!title} onChange={onChange} onScroll={e => e.rel === 'bottom' && loadNextPage?.call(this)} header={<>
|
|
60
|
+
<h3>Select an asset</h3>
|
|
61
|
+
<div className="relative">
|
|
62
|
+
<input type="text" value={search} ref={searchRef} onChange={e => setSearch(e.target.value)}
|
|
63
|
+
placeholder="Search by asset code or website"/>
|
|
64
|
+
<i className="icon-search dimmed"/>
|
|
65
|
+
</div>
|
|
66
|
+
</>}/>
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @callback AssetSelectorOnAssetChanged
|
|
71
|
+
* @param {String} value
|
|
72
|
+
*/
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
.asset-selector {
|
|
2
|
+
.dd-header {
|
|
3
|
+
.asset-issuer {
|
|
4
|
+
display: none;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.dd-list {
|
|
9
|
+
width: 22em;
|
|
10
|
+
max-width: 94vw;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.dd-list-header {
|
|
14
|
+
min-width: 16em;
|
|
15
|
+
|
|
16
|
+
h3 {
|
|
17
|
+
margin: 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.icon-search {
|
|
21
|
+
position: absolute;
|
|
22
|
+
right: 0.3em;
|
|
23
|
+
top: 0.5em;
|
|
24
|
+
opacity: 0.5;
|
|
25
|
+
font-size: $font-size-base;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
input {
|
|
29
|
+
margin-bottom: 0;
|
|
30
|
+
|
|
31
|
+
&:not(:placeholder-shown) + .icon-search {
|
|
32
|
+
display: none;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&::placeholder {
|
|
36
|
+
font-size: 0.85em;
|
|
37
|
+
line-height: 1;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
*,
|
|
2
|
+
*:after,
|
|
3
|
+
*:before {
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
* {
|
|
8
|
+
@include scrollbars(6px, var(--color-dimmed), var(--color-contrast-border));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
html {
|
|
12
|
+
font-size: 62.5%;
|
|
13
|
+
height: 100%;
|
|
14
|
+
-ms-text-size-adjust: 100%;
|
|
15
|
+
-webkit-text-size-adjust: 100%;
|
|
16
|
+
scroll-behavior: smooth;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
body {
|
|
20
|
+
color: var(--color-text);
|
|
21
|
+
background: var(--color-bg);
|
|
22
|
+
font-family: $font-face-primary;
|
|
23
|
+
font-size: $font-size-base;
|
|
24
|
+
font-weight: $font-weight-base;
|
|
25
|
+
letter-spacing: 0.01em;
|
|
26
|
+
line-height: $font-line-height-base;
|
|
27
|
+
height: 100%;
|
|
28
|
+
overflow-x: hidden;
|
|
29
|
+
min-width: 320px;
|
|
30
|
+
margin: 0;
|
|
31
|
+
font-smoothing: antialiased;
|
|
32
|
+
-webkit-font-smoothing: antialiased;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@include scrollbars(6px, var(--color-dimmed), var(--color-contrast-border));
|
|
36
|
+
|
|
37
|
+
::selection {
|
|
38
|
+
background-color: transparentize($color-primary, 0.8) !important;
|
|
39
|
+
color: var(--color-text);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.page-container {
|
|
43
|
+
min-height: calc(100vh - 8rem);
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
/*@media (min-width: $responsive-mobile-browser-width) {
|
|
47
|
+
justify-content: center;
|
|
48
|
+
}*/
|
|
49
|
+
|
|
50
|
+
> .no-center {
|
|
51
|
+
margin-bottom: auto;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
input:not([type=checkbox],[type=radio],[type=range]),
|
|
2
|
+
textarea,
|
|
3
|
+
select,
|
|
4
|
+
.input-like {
|
|
5
|
+
appearance: none;
|
|
6
|
+
background-color: transparent !important;
|
|
7
|
+
border: none !important;
|
|
8
|
+
border-bottom: 1px solid var(--color-contrast-border) !important;
|
|
9
|
+
box-shadow: none;
|
|
10
|
+
box-sizing: inherit;
|
|
11
|
+
height: 4rem;
|
|
12
|
+
font-size: $font-size-base;
|
|
13
|
+
font-family: $font-face-primary;
|
|
14
|
+
color: var(--color-text);
|
|
15
|
+
padding: 0.6rem;
|
|
16
|
+
width: 100%;
|
|
17
|
+
transition: border-bottom-color 0.2s;
|
|
18
|
+
|
|
19
|
+
&:focus {
|
|
20
|
+
background-color: var(--color-alt-bg) !important;
|
|
21
|
+
border-bottom-color: var(--color-highlight) !important;
|
|
22
|
+
outline: 0;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
input[type=range] {
|
|
27
|
+
height: 2px;
|
|
28
|
+
width: 100%;
|
|
29
|
+
margin: 0;
|
|
30
|
+
filter: grayscale(100%);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
input[type='checkbox'],
|
|
34
|
+
input[type='radio'] {
|
|
35
|
+
display: inline;
|
|
36
|
+
margin-left: 0;
|
|
37
|
+
filter: grayscale(100%);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
input[type=checkbox] {
|
|
41
|
+
vertical-align: bottom;
|
|
42
|
+
position: relative;
|
|
43
|
+
top: -0.3em;
|
|
44
|
+
padding: 0;
|
|
45
|
+
margin: 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
input[type='text'].key {
|
|
49
|
+
letter-spacing: -0.05em;
|
|
50
|
+
font-family: $font-face-mono;
|
|
51
|
+
@media (max-width: 519px) {
|
|
52
|
+
font-size: 0.9em;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&::placeholder {
|
|
56
|
+
letter-spacing: 0;
|
|
57
|
+
font-size: $font-size-base;
|
|
58
|
+
font-family: $font-face-primary;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.input-like {
|
|
63
|
+
min-height: 4rem;
|
|
64
|
+
height: auto;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
textarea {
|
|
68
|
+
min-height: 4em;
|
|
69
|
+
|
|
70
|
+
&.no-resize {
|
|
71
|
+
resize: none;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
label,
|
|
76
|
+
legend {
|
|
77
|
+
display: block;
|
|
78
|
+
font-size: $font-size-base;
|
|
79
|
+
margin-bottom: $space-nano;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.label-inline {
|
|
83
|
+
display: inline-block;
|
|
84
|
+
font-weight: normal;
|
|
85
|
+
margin-left: $space-nano;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
::placeholder {
|
|
89
|
+
color: var(--color-dimmed);
|
|
90
|
+
}
|