mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-22 11:16:01 +03:00
Stats page design fixes (#21171)
[ANAL-95](https://linear.app/tryghost/issue/ANAL-95/internal-beta-qa) Various design refinements and fixes for the Stats page: - Updated scroll area in detail modals so that the Close button and the footer is never outside the viewport - The detail modal didn't close after clicking on the filter values - "Show all" button was displayed also when there were no new items in the detail modal - Dropdown styles needed a visual update: the toggles were way too huge and inconsistent with other dropdowns - If no audience was selected we still showed stats. Now it's displaying the default empty screen in this case - Click through filter indicators had low discoverability - Technical data styles needed some love: changed the alignment and color scheme - Mobile size viewports were not handled - The google favicon API returned 404 many times for sources. Swapped the service for another one that returns favicons more reliably - Default favicon was not handled. Now it comes from static.ghost.org
This commit is contained in:
parent
d8c4dfef99
commit
8aaac5abe1
@ -126,7 +126,7 @@
|
||||
<ul class="nav-list">
|
||||
{{#if (and this.post.isPage this.post.lexical)}}
|
||||
<li class="nav-list-item">
|
||||
<div class="for-switch x-small">
|
||||
<div class="for-switch xs">
|
||||
<label class="switch">
|
||||
<span>
|
||||
<Icons::EyeOpenClose class="feature" @closed={{not this.post.showTitleAndFeatureImage}} />
|
||||
@ -154,7 +154,7 @@
|
||||
{{/if}}
|
||||
{{#unless this.session.user.isAuthorOrContributor}}
|
||||
<li class="nav-list-item">
|
||||
<div class="for-switch x-small">
|
||||
<div class="for-switch xs">
|
||||
<label class="switch" for="featured" {{action "toggleFeatured" bubbles="false"}}>
|
||||
<span>
|
||||
{{#if this.post.featured}}
|
||||
|
@ -18,7 +18,7 @@
|
||||
{{svg-jar type.icon class="gh-member-activity-actions-menu-icon"}}
|
||||
<span>{{type.name}}</span>
|
||||
</label>
|
||||
<div class="for-switch x-small">
|
||||
<div class="for-switch xxs">
|
||||
<label class="switch" for="type-{{idx}}">
|
||||
<input
|
||||
data-test-id="event-type-filter-checkbox-{{type.event}}"
|
||||
|
@ -54,7 +54,7 @@ export default class KpisComponent extends Component {
|
||||
options={{
|
||||
grid: {
|
||||
left: '10px',
|
||||
right: '10px',
|
||||
right: '20px',
|
||||
top: '10%',
|
||||
bottom: 0,
|
||||
containLabel: true
|
||||
|
@ -1 +1 @@
|
||||
<div {{react-render this.ReactComponent props=(hash chartRange=@chartRange audience=@audience device=@device browser=@browser location=@location source=@source pathname=@pathname selected=@selected)}}></div>
|
||||
<div class="gh-stats-technical-data" {{react-render this.ReactComponent props=(hash chartRange=@chartRange audience=@audience device=@device browser=@browser location=@location source=@source pathname=@pathname selected=@selected)}}></div>
|
||||
|
@ -29,7 +29,7 @@ export default class TechnicalComponent extends Component {
|
||||
ReactComponent = (props) => {
|
||||
const {selected} = props;
|
||||
|
||||
const colorPalette = statsStaticColors.slice(1, 5);
|
||||
const colorPalette = statsStaticColors.slice(0, 5);
|
||||
|
||||
const params = getStatsParams(
|
||||
this.config,
|
||||
@ -66,34 +66,6 @@ export default class TechnicalComponent extends Component {
|
||||
|
||||
return (
|
||||
<div className="gh-stats-piechart-container">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span className="gh-stats-data-header">{tableHead}</span></th>
|
||||
<th><span className="gh-stats-data-header">Visits</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{transformedData.map((item, index) => (
|
||||
<tr key={index}>
|
||||
<td>
|
||||
<a
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
this.navigateToFilter(indexBy, item.name.toLowerCase());
|
||||
}}
|
||||
className="gh-stats-data-label"
|
||||
>
|
||||
<span style={{backgroundColor: item.color, display: 'inline-block', width: '10px', height: '10px', marginRight: '5px', borderRadius: '2px'}}></span>
|
||||
{item.name}
|
||||
</a>
|
||||
</td>
|
||||
<td><span className="gh-stats-data-value">{formatNumber(item.value)}</span></td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="gh-stats-piechart">
|
||||
<DonutChart
|
||||
data={data}
|
||||
@ -135,8 +107,9 @@ export default class TechnicalComponent extends Component {
|
||||
{
|
||||
animation: true,
|
||||
name: tableHead,
|
||||
padAngle: 1.5,
|
||||
type: 'pie',
|
||||
radius: ['60%', '90%'],
|
||||
radius: ['65%', '90%'],
|
||||
center: ['50%', '50%'], // Adjusted to align the chart to the top
|
||||
data: transformedData,
|
||||
label: {
|
||||
@ -157,6 +130,34 @@ export default class TechnicalComponent extends Component {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span className="gh-stats-data-header">{tableHead}</span></th>
|
||||
<th><span className="gh-stats-data-header">Visits</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{transformedData.map((item, index) => (
|
||||
<tr key={index}>
|
||||
<td>
|
||||
<a
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
this.navigateToFilter(indexBy, item.name.toLowerCase());
|
||||
}}
|
||||
className="gh-stats-data-label"
|
||||
>
|
||||
<span style={{backgroundColor: item.color, display: 'inline-block', width: '10px', height: '10px', marginRight: '5px', borderRadius: '2px'}}></span>
|
||||
{item.name}
|
||||
</a>
|
||||
</td>
|
||||
<td><span className="gh-stats-data-value">{formatNumber(item.value)}</span></td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -3,8 +3,10 @@
|
||||
<div {{react-render this.ReactComponent props=(hash chartRange=@chartRange audience=@audience device=@device browser=@browser location=@location source=@source pathname=@pathname)}}></div>
|
||||
</div>
|
||||
|
||||
{{#if this.showSeeAll}}
|
||||
<div class="gh-stats-see-all-container">
|
||||
<button type="button" class="gh-btn gh-btn-link gh-stats-see-all-btn" {{on "click" (fn this.openSeeAll @chartRange @audience @device @browser @location @source @pathname)}}>
|
||||
<span>See all →</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
@ -9,12 +9,17 @@ import {barListColor, getCountryFlag, getStatsParams} from 'ghost-admin/utils/st
|
||||
import {formatNumber} from 'ghost-admin/helpers/format-number';
|
||||
import {inject} from 'ghost-admin/decorators/inject';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
const LIMIT = 5;
|
||||
|
||||
export default class TopLocations extends Component {
|
||||
@inject config;
|
||||
@service modals;
|
||||
@service router;
|
||||
|
||||
@tracked showSeeAll = true;
|
||||
|
||||
@action
|
||||
openSeeAll() {
|
||||
this.modals.open(AllStatsModal, {
|
||||
@ -36,6 +41,10 @@ export default class TopLocations extends Component {
|
||||
this.router.transitionTo({queryParams: newQueryParams});
|
||||
}
|
||||
|
||||
updateSeeAllVisibility(data) {
|
||||
this.showSeeAll = data && data.length > LIMIT;
|
||||
}
|
||||
|
||||
ReactComponent = (props) => {
|
||||
const params = getStatsParams(
|
||||
this.config,
|
||||
@ -49,9 +58,11 @@ export default class TopLocations extends Component {
|
||||
params
|
||||
});
|
||||
|
||||
this.updateSeeAllVisibility(data);
|
||||
|
||||
return (
|
||||
<BarList
|
||||
data={data}
|
||||
data={data ? data.slice(0, LIMIT) : []}
|
||||
meta={meta}
|
||||
error={error}
|
||||
loading={loading}
|
||||
@ -68,7 +79,7 @@ export default class TopLocations extends Component {
|
||||
}}
|
||||
className="gh-stats-domain"
|
||||
>
|
||||
{getCountryFlag(label)} {label || 'Unknown'}
|
||||
<span title={label || 'Unknown'}>{getCountryFlag(label)} {label || 'Unknown'}</span>
|
||||
</a>
|
||||
</span>
|
||||
)
|
||||
|
@ -21,8 +21,10 @@
|
||||
<div {{react-render this.ReactComponent props=(hash chartRange=@chartRange audience=@audience device=@device browser=@browser location=@location source=@source pathname=@pathname)}}></div>
|
||||
</div>
|
||||
|
||||
{{#if this.showSeeAll}}
|
||||
<div class="gh-stats-see-all-container">
|
||||
<button type="button" class="gh-btn gh-btn-link gh-stats-see-all-btn" {{on "click" (fn this.openSeeAll @chartRange @audience @device @browser @location @source @pathname)}}>
|
||||
<span>See all →</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
@ -11,6 +11,8 @@ import {inject} from 'ghost-admin/decorators/inject';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
const LIMIT = 7;
|
||||
|
||||
export default class TopPages extends Component {
|
||||
@inject config;
|
||||
@service modals;
|
||||
@ -18,6 +20,7 @@ export default class TopPages extends Component {
|
||||
|
||||
@tracked contentOption = CONTENT_OPTIONS[0];
|
||||
@tracked contentOptions = CONTENT_OPTIONS;
|
||||
@tracked showSeeAll = true;
|
||||
|
||||
@action
|
||||
openSeeAll(chartRange, audience) {
|
||||
@ -45,11 +48,15 @@ export default class TopPages extends Component {
|
||||
this.router.transitionTo({queryParams: newQueryParams});
|
||||
}
|
||||
|
||||
updateSeeAllVisibility(data) {
|
||||
this.showSeeAll = data && data.length > LIMIT;
|
||||
}
|
||||
|
||||
ReactComponent = (props) => {
|
||||
const params = getStatsParams(
|
||||
this.config,
|
||||
props,
|
||||
{limit: 7}
|
||||
{limit: LIMIT + 1}
|
||||
);
|
||||
|
||||
const {data, meta, error, loading} = useQuery({
|
||||
@ -58,9 +65,11 @@ export default class TopPages extends Component {
|
||||
params
|
||||
});
|
||||
|
||||
this.updateSeeAllVisibility(data);
|
||||
|
||||
return (
|
||||
<BarList
|
||||
data={data}
|
||||
data={data ? data.slice(0, LIMIT) : []}
|
||||
meta={meta}
|
||||
error={error}
|
||||
loading={loading}
|
||||
@ -77,7 +86,7 @@ export default class TopPages extends Component {
|
||||
}}
|
||||
className="gh-stats-domain"
|
||||
>
|
||||
{label}
|
||||
<span title={label}>{label}</span>
|
||||
</a>
|
||||
</span>
|
||||
)
|
||||
|
@ -22,8 +22,10 @@
|
||||
<div {{react-render this.ReactComponent props=(hash chartRange=@chartRange audience=@audience device=@device browser=@browser location=@location source=@source pathname=@pathname)}}></div>
|
||||
</div>
|
||||
|
||||
{{#if this.showSeeAll}}
|
||||
<div class="gh-stats-see-all-container">
|
||||
<button type="button" class="gh-btn gh-btn-link gh-stats-see-all-btn" {{on "click" (fn this.openSeeAll @chartRange @audience @device @browser @location @source @pathname)}}>
|
||||
<span>See all →</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
@ -11,6 +11,9 @@ import {inject} from 'ghost-admin/decorators/inject';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
const LIMIT = 7;
|
||||
const DEFAULT_ICON_URL = 'https://static.ghost.org/v5.0.0/images/globe-icon.svg';
|
||||
|
||||
export default class TopSources extends Component {
|
||||
@inject config;
|
||||
@service modals;
|
||||
@ -18,6 +21,7 @@ export default class TopSources extends Component {
|
||||
|
||||
@tracked campaignOption = CAMPAIGN_OPTIONS[0];
|
||||
@tracked campaignOptions = CAMPAIGN_OPTIONS;
|
||||
@tracked showSeeAll = true;
|
||||
|
||||
@action
|
||||
onCampaignOptionChange(selected) {
|
||||
@ -45,6 +49,10 @@ export default class TopSources extends Component {
|
||||
this.router.transitionTo({queryParams: newQueryParams});
|
||||
}
|
||||
|
||||
updateSeeAllVisibility(data) {
|
||||
this.showSeeAll = data && data.length > LIMIT;
|
||||
}
|
||||
|
||||
ReactComponent = (props) => {
|
||||
const {data, meta, error, loading} = useQuery({
|
||||
endpoint: `${this.config.stats.endpoint}/v0/pipes/top_sources.json`,
|
||||
@ -56,9 +64,11 @@ export default class TopSources extends Component {
|
||||
)
|
||||
});
|
||||
|
||||
this.updateSeeAllVisibility(data);
|
||||
|
||||
return (
|
||||
<BarList
|
||||
data={data}
|
||||
data={data ? data.slice(0, LIMIT) : []}
|
||||
meta={meta}
|
||||
error={error}
|
||||
loading={loading}
|
||||
@ -75,8 +85,13 @@ export default class TopSources extends Component {
|
||||
}}
|
||||
className="gh-stats-domain"
|
||||
>
|
||||
<img src={`https://www.google.com/s2/favicons?domain=${label || 'direct'}&sz=32`} className="gh-stats-favicon" />
|
||||
{label || 'Direct'}
|
||||
<img
|
||||
src={`https://www.faviconextractor.com/favicon/${label || 'direct'}?larger=true`}
|
||||
className="gh-stats-favicon"
|
||||
onError={(e) => {
|
||||
e.target.src = DEFAULT_ICON_URL;
|
||||
}} />
|
||||
<span title={label || 'Direct'}>{label || 'Direct'}</span>
|
||||
</a>
|
||||
</span>
|
||||
)
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
<button type="button" class="close" title="Close" {{on "click" @close}} data-test-button="close-publish-flow">{{svg-jar "close"}}<span class="hidden">Close</span></button>
|
||||
|
||||
<div {{react-render this.ReactComponent props=(hash chartRange=this.chartRange audience=this.audience type=this.type)}}></div>
|
||||
<div class="gh-stats-all-container" {{react-render this.ReactComponent props=(hash chartRange=this.chartRange audience=this.audience type=this.type)}}></div>
|
||||
|
||||
<footer class="modal-footer">
|
||||
<button
|
||||
|
@ -12,6 +12,7 @@ import {inject as service} from '@ember/service';
|
||||
export default class AllStatsModal extends Component {
|
||||
@inject config;
|
||||
@service router;
|
||||
@service modals;
|
||||
|
||||
get type() {
|
||||
return this.args.data.type;
|
||||
@ -47,6 +48,7 @@ export default class AllStatsModal extends Component {
|
||||
params.pathname = label;
|
||||
}
|
||||
|
||||
this.args.close();
|
||||
this.updateQueryParams(params);
|
||||
}
|
||||
|
||||
@ -121,7 +123,7 @@ export default class AllStatsModal extends Component {
|
||||
className="gh-stats-favicon"
|
||||
/>
|
||||
)}
|
||||
{label || unknownOption}
|
||||
<span title={label || unknownOption}>{label || unknownOption}</span>
|
||||
</a>
|
||||
</span>
|
||||
)
|
||||
|
@ -1,19 +1,20 @@
|
||||
<GhBasicDropdown @verticalPosition="below" as |dd|>
|
||||
<dd.Trigger class="gh-btn gh-btn-icon gh-btn-action-icon" data-test-id="filter-events-button">
|
||||
<dd.Trigger class="gh-btn gh-btn-icon gh-btn-action-icon gh-stats-btn-audience-filter" data-test-id="filter-events-button">
|
||||
<span class={{if @excludedAudiences "gh-btn-label-green"}}>
|
||||
{{svg-jar "members"}}
|
||||
Audience
|
||||
<span class="gh-stats-audience-filter-btn-label">Audience</span>
|
||||
{{svg-jar "arrow-down-small" class="gh-btn-dropdown-arrow"}}
|
||||
</span>
|
||||
</dd.Trigger>
|
||||
|
||||
<dd.Content class="gh-member-activity-actions-menu dropdown-menu dropdown-triangle-top-right gh-member-activity-actions-menu--suppression">
|
||||
<dd.Content class="gh-member-activity-actions-menu gh-stats-audience-filter-menu dropdown-menu dropdown-triangle-top-right gh-member-activity-actions-menu--suppression">
|
||||
<ul class="ember-power-select-options" role="listbox">
|
||||
{{#each this.audienceTypes as |type idx|}}
|
||||
<li class="ember-power-select-option mb0 gh-member-activity-actions-menu-item">
|
||||
<label for="type-{{idx}}">
|
||||
<span>{{type.name}}</span>
|
||||
</label>
|
||||
<div class="for-switch x-small">
|
||||
<div class="for-switch xxs">
|
||||
<label class="switch" for="type-{{idx}}">
|
||||
<input
|
||||
data-test-id="audience-type-filter-checkbox-{{type.value}}"
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div>
|
||||
<div class="gh-stats-technical-container">
|
||||
<div class="gh-stats-tabs-header">
|
||||
<div class="gh-stats-tabs">
|
||||
<button type="button" class="gh-stats-tab {{if this.devicesTabSelected 'is-selected'}}" {{on "click" this.changeTabToDevices}}>
|
||||
|
@ -25,6 +25,7 @@ export default class StatsController extends Controller {
|
||||
*/
|
||||
@tracked audience = [];
|
||||
@tracked excludedAudiences = '';
|
||||
@tracked showStats = true;
|
||||
|
||||
@action
|
||||
onRangeChange(selected) {
|
||||
@ -42,6 +43,16 @@ export default class StatsController extends Controller {
|
||||
this.excludedAudiences = '';
|
||||
this.audience = this.audienceOptions.map(a => a.value);
|
||||
}
|
||||
|
||||
const excludedArray = this.excludedAudiences.split(',');
|
||||
this.showStats = this.audienceOptions.length !== excludedArray.length;
|
||||
}
|
||||
|
||||
@action
|
||||
clearAudienceFilter() {
|
||||
this.excludedAudiences = '';
|
||||
this.audience = this.audienceOptions.map(a => a.value);
|
||||
this.showStats = true;
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -71,7 +71,7 @@
|
||||
font-weight: 500;
|
||||
line-height: 1.4em;
|
||||
transition: none;
|
||||
border-radius: 3px
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.dropdown-menu li > button:disabled {
|
||||
|
@ -69,15 +69,20 @@
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.ember-basic-dropdown-trigger--below.ember-power-select-trigger[aria-expanded="true"] {
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.ember-power-select-dropdown.ember-basic-dropdown-content--above {
|
||||
border-top: 1px solid var(--input-border-color);
|
||||
border-radius: var(--border-radius) var(--border-radius) 0 0;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.ember-power-select-option {
|
||||
margin: 0;
|
||||
padding: 6px 14px;
|
||||
color: var(--darkgrey);
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.ember-power-select-option[aria-current="true"] {
|
||||
|
@ -319,7 +319,7 @@ li.nav-list-item .switch {
|
||||
padding: 2rem 2.4rem;
|
||||
}
|
||||
|
||||
li.nav-list-item .for-switch.x-small label {
|
||||
li.nav-list-item .for-switch.xs label {
|
||||
width: initial;
|
||||
height: initial !important;
|
||||
}
|
||||
|
@ -316,13 +316,19 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 13px;
|
||||
border-radius: calc(var(--border-radius) - 2px);
|
||||
}
|
||||
|
||||
.gh-member-activity-actions-menu-item:hover {
|
||||
background: color-mod(var(--whitegrey) a(60%) s(-12%));
|
||||
}
|
||||
|
||||
.gh-member-activity-actions-menu--suppression {
|
||||
min-width: 260px;
|
||||
}
|
||||
.gh-member-activity-actions-menu--suppression.gh-member-activity-actions-menu {
|
||||
padding: 1rem 0;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.gh-member-activity-actions-menu--suppression.gh-member-activity-actions-menu .ember-power-select-option:first-child {
|
||||
@ -332,13 +338,13 @@
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.gh-member-activity-actions-menu--suppression.gh-member-activity-actions-menu .for-switch.x-small label {
|
||||
.gh-member-activity-actions-menu--suppression.gh-member-activity-actions-menu .for-switch.xs label {
|
||||
width: 34px !important;
|
||||
height: 20px !important;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.gh-member-activity-actions-menu--suppression.gh-member-activity-actions-menu .for-switch.x-small {
|
||||
.gh-member-activity-actions-menu--suppression.gh-member-activity-actions-menu .for-switch.xs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@ -355,7 +361,9 @@
|
||||
}
|
||||
|
||||
.gh-member-activity-actions-menu--suppression.gh-member-activity-actions-menu .ember-power-select-option {
|
||||
padding: 0.3rem 1.6rem;
|
||||
padding: 0 1.0rem;
|
||||
min-height: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.gh-member-activity-actions-menu--suppression.gh-member-activity-actions-menu .ember-power-select-option label {
|
||||
@ -365,8 +373,9 @@
|
||||
|
||||
.gh-member-activity-actions-menu--suppression .gh-member-activity-actions-menu-divider {
|
||||
height: 1px;
|
||||
margin: 1rem 0;
|
||||
margin: 1rem -4px;
|
||||
background: var(--lightgrey-l1);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* End of styles for dropdown for suppressionList feature */
|
||||
@ -380,6 +389,7 @@
|
||||
|
||||
.gh-member-activity-actions-menu .ember-power-select-options[role=listbox] {
|
||||
max-height: 60vh;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.gh-member-activity-actions-menu .ember-power-select-option {
|
||||
|
@ -161,7 +161,7 @@
|
||||
background: var(--white);
|
||||
}
|
||||
|
||||
.gh-post-history-footer .for-switch.x-small {
|
||||
.gh-post-history-footer .for-switch.xs {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.gh-post-history-footer .for-switch.x-small label {
|
||||
.gh-post-history-footer .for-switch.xs label {
|
||||
width: inherit !important;
|
||||
height: inherit !important;
|
||||
}
|
||||
|
@ -153,7 +153,7 @@
|
||||
.gh-stats-piechart-container {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: flex-start;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex-grow: 1;
|
||||
margin-right: -20px;
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
.gh-stats-section-dropdown {
|
||||
@ -261,6 +261,17 @@ a.gh-stats-data-label:hover {
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.gh-stats-all-container {
|
||||
max-height: calc(100vh - 12vw - 172px);
|
||||
overflow: auto;
|
||||
margin: -4px -32px;
|
||||
padding: 4px 32px;
|
||||
}
|
||||
|
||||
.gh-stats-all-container > div > div {
|
||||
overflow-y: unset !important;
|
||||
}
|
||||
|
||||
.gh-stats-kpis-chart-container {
|
||||
margin-top: -20px;
|
||||
}
|
||||
@ -270,6 +281,7 @@ a.gh-stats-data-label:hover {
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: var(--black);
|
||||
line-height: 1.3em;
|
||||
}
|
||||
|
||||
.gh-stats-domain:hover {
|
||||
@ -281,6 +293,13 @@ a.gh-stats-data-label:hover {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.gh-stats-domain span {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gh-stats-tooltip-header {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
@ -328,9 +347,11 @@ a.gh-stats-data-label:hover {
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
border: 1px solid var(--whitegrey);
|
||||
/* border: 1px solid var(--purple); */
|
||||
border: 1px solid rgba(142,68,254,0.75);
|
||||
border-radius: 999px;
|
||||
padding: 4px 12px;
|
||||
background-color: rgba(142,68,254,0.06);
|
||||
}
|
||||
|
||||
.gh-stats-filter-pill .value {
|
||||
@ -363,4 +384,70 @@ a.gh-stats-data-label:hover {
|
||||
|
||||
.gh-btn-clear-filters svg path {
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.gh-stats-audience-filter-menu {
|
||||
max-width: 240px;
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
.gh-stats-placeholder {
|
||||
width: 60px;
|
||||
color: var(--lightgrey);
|
||||
margin-bottom: -8px;
|
||||
}
|
||||
|
||||
.gh-stats-technical-container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.gh-stats-technical-data {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.gh-stats-audience-filter-btn-label {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
@media (max-width: 1440px) {
|
||||
.gh-stats-filters {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1140px) {
|
||||
.gh-stats-grid.cols-2 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.gh-stats-tab.is-selected:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.gh-stats-tab.is-selected {
|
||||
border-bottom: 2px solid var(--black);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 990px) {
|
||||
.gh-stats-tabs {
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 390px) {
|
||||
.gh-stats-audience-filter-btn-label {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.gh-stats-btn-audience-filter svg {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
}
|
@ -377,6 +377,13 @@ svg.gh-btn-icon-right {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.gh-btn-dropdown-arrow {
|
||||
margin-left: 5px !important;
|
||||
margin-right: -4px !important;
|
||||
height: 6px !important;
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/* Button Variations
|
||||
|
@ -540,17 +540,12 @@ textarea {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.for-switch label:not(.x-small .switch),
|
||||
.for-switch label:not(.xs .switch):not(.xxs .switch),
|
||||
.for-switch .container {
|
||||
width: 50px !important;
|
||||
height: 28px !important;
|
||||
}
|
||||
|
||||
.for-switch.x-small label {
|
||||
width: 34px !important;
|
||||
height: 20px !important;
|
||||
}
|
||||
|
||||
.for-switch label p,
|
||||
.for-switch .container p {
|
||||
overflow: auto;
|
||||
@ -576,7 +571,7 @@ textarea {
|
||||
width: 48px !important;
|
||||
height: 26px !important;
|
||||
border-radius: 999px;
|
||||
transition: background 0.15s ease-in-out, border-color 0.15s ease-in-out;
|
||||
transition: all 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.for-switch label:hover input:not(:checked) + .input-toggle-component,
|
||||
@ -595,6 +590,7 @@ textarea {
|
||||
transition: .3s;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,.15);
|
||||
border-radius: 999px;
|
||||
transition: all 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.for-switch input:checked + .input-toggle-component {
|
||||
@ -625,22 +621,47 @@ textarea {
|
||||
|
||||
.for-switch.small input:checked + .input-toggle-component:before {
|
||||
transform: translateX(16px);
|
||||
transition: all 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.for-switch.x-small .input-toggle-component {
|
||||
.for-switch.xs label {
|
||||
width: 34px !important;
|
||||
height: 20px !important;
|
||||
}
|
||||
|
||||
.for-switch.x-small .input-toggle-component:before {
|
||||
.for-switch.xs .input-toggle-component {
|
||||
width: 34px !important;
|
||||
height: 20px !important;
|
||||
}
|
||||
|
||||
.for-switch.xs .input-toggle-component:before {
|
||||
height: 16px !important;
|
||||
width: 16px !important;
|
||||
}
|
||||
|
||||
.for-switch.x-small input:checked + .input-toggle-component:before {
|
||||
.for-switch.xs input:checked + .input-toggle-component:before {
|
||||
transform: translateX(14px);
|
||||
}
|
||||
|
||||
.for-switch.xxs label {
|
||||
width: 28px !important;
|
||||
height: 16px !important;
|
||||
}
|
||||
|
||||
.for-switch.xxs .input-toggle-component {
|
||||
width: 28px !important;
|
||||
height: 16px !important;
|
||||
}
|
||||
|
||||
.for-switch.xxs .input-toggle-component:before {
|
||||
height: 12px !important;
|
||||
width: 12px !important;
|
||||
}
|
||||
|
||||
.for-switch.xxs input:checked + .input-toggle-component:before {
|
||||
transform: translateX(12px);
|
||||
}
|
||||
|
||||
.for-switch.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
|
@ -61,6 +61,8 @@
|
||||
</GhCanvasHeader>
|
||||
|
||||
<section class="view-container">
|
||||
|
||||
{{#if this.showStats}}
|
||||
<section class="gh-stats-container no-gap">
|
||||
<Stats::KpisOverview
|
||||
@chartRange={{this.chartRange}}
|
||||
@ -121,6 +123,18 @@
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
{{else}}
|
||||
<div class="no-posts-box">
|
||||
<div class="no-posts">
|
||||
{{svg-jar "stats-placeholder" class="gh-stats-placeholder"}}
|
||||
<h4>No stats available for the current filter</h4>
|
||||
<button type="button" class="gh-btn" {{on "click" this.clearAudienceFilter}}>
|
||||
<span>See all stats</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
No audience selected
|
||||
{{/if}}
|
||||
|
||||
</section>
|
||||
|
||||
|
@ -111,7 +111,7 @@ export function generateMonochromePalette(baseColor, count = 10) {
|
||||
export const barListColor = '#F1F3F4';
|
||||
|
||||
export const statsStaticColors = [
|
||||
'#8E42FF', '#B07BFF', '#C7A0FF', '#DDC6FF', '#EBDDFF', '#F7EDFF'
|
||||
'#A568FF', '#7B7BFF', '#B3CEFF', '#D4ECF7', '#EFFDFD', '#F7F7F7'
|
||||
];
|
||||
|
||||
export const getCountryFlag = (countryCode) => {
|
||||
|
1
ghost/admin/public/assets/icons/stats-placeholder.svg
Normal file
1
ghost/admin/public/assets/icons/stats-placeholder.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-1 -1 48 48" id="Analytics-Board-Graph-Line--Streamline-Ultimate" height="48" width="48"><desc>Analytics Board Graph Line Streamline Icon: https://streamlinehq.com</desc><defs></defs><title>analytics-board-graph-line</title><path d="M4.3125 7.1875h37.375s2.875 0 2.875 2.875v25.875s0 2.875 -2.875 2.875H4.3125s-2.875 0 -2.875 -2.875V10.0625s0 -2.875 2.875 -2.875" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path><path d="M7.1875 28.75 13.225000000000001 18.6875 18.6875 28.75l5.75 -5.75 5.75 5.75 8.625 -12.9375" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
|
After Width: | Height: | Size: 730 B |
Loading…
Reference in New Issue
Block a user