Updates
This commit is contained in:
689
composer.lock
generated
689
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Evomedien\Vitec\UserFunc;
|
namespace Evomedien\Vitec\UserFunc;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\ArrayParameterType;
|
||||||
use Doctrine\DBAL\ParameterType;
|
use Doctrine\DBAL\ParameterType;
|
||||||
use TYPO3\CMS\Core\Attribute\AsAllowedCallable;
|
use TYPO3\CMS\Core\Attribute\AsAllowedCallable;
|
||||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||||
@@ -20,12 +21,23 @@ use TYPO3\CMS\Extbase\Service\ImageService;
|
|||||||
* today or later), soonest first. FlexForm options: showpast (append past
|
* today or later), soonest first. FlexForm options: showpast (append past
|
||||||
* events, most recent first, under "pastEvents"), limit, debug.
|
* events, most recent first, under "pastEvents"), limit, debug.
|
||||||
*
|
*
|
||||||
|
* Layout `regions` additionally emits "regions": the regions that actually have
|
||||||
|
* an upcoming event, each with the image of its next one. See fetchRegions().
|
||||||
|
*
|
||||||
* `render()` = TypoScript entry (cObj->data or page discovery).
|
* `render()` = TypoScript entry (cObj->data or page discovery).
|
||||||
* `renderForRecord()` = one specific tt_content row; reused by
|
* `renderForRecord()` = one specific tt_content row; reused by
|
||||||
* ContainerChildrenProcessor / ContentElementResolver. Exception-safe.
|
* ContainerChildrenProcessor / ContentElementResolver. Exception-safe.
|
||||||
*/
|
*/
|
||||||
class EventlistJsonRenderer
|
class EventlistJsonRenderer
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Parent of the region categories. Hard-coded like the other taxonomy roots
|
||||||
|
* in this extension (Annex B-5 tracks them); making it a FlexForm setting
|
||||||
|
* would put a raw uid in front of editors for a value that belongs to the
|
||||||
|
* content model, not to a single content element.
|
||||||
|
*/
|
||||||
|
private const REGION_PARENT_CATEGORY = 104;
|
||||||
|
|
||||||
#[AsAllowedCallable]
|
#[AsAllowedCallable]
|
||||||
public function render(string $content, array $conf): string
|
public function render(string $content, array $conf): string
|
||||||
{
|
{
|
||||||
@@ -101,6 +113,10 @@ class EventlistJsonRenderer
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ($layout === 'regions') {
|
||||||
|
$response['regions'] = $this->fetchRegions($daysInAdvance);
|
||||||
|
}
|
||||||
|
|
||||||
if ($showPast) {
|
if ($showPast) {
|
||||||
$past = $this->fetchEvents(false, $limit);
|
$past = $this->fetchEvents(false, $limit);
|
||||||
$response['pastEvents'] = array_map(fn($e) => $this->serializeEvent($e), $past);
|
$response['pastEvents'] = array_map(fn($e) => $this->serializeEvent($e), $past);
|
||||||
@@ -111,6 +127,9 @@ class EventlistJsonRenderer
|
|||||||
'upcomingCount' => count($upcoming),
|
'upcomingCount' => count($upcoming),
|
||||||
'settings' => $settings,
|
'settings' => $settings,
|
||||||
];
|
];
|
||||||
|
if (isset($response['regions'])) {
|
||||||
|
$response['debug']['regionCount'] = count($response['regions']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return json_encode($response);
|
return json_encode($response);
|
||||||
@@ -181,6 +200,215 @@ class EventlistJsonRenderer
|
|||||||
return $qb->executeQuery()->fetchAllAssociative();
|
return $qb->executeQuery()->fetchAllAssociative();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regions for the `regions` layout: the categories below
|
||||||
|
* REGION_PARENT_CATEGORY that have at least one upcoming event, each
|
||||||
|
* carrying the image of its next one as the card logo.
|
||||||
|
*
|
||||||
|
* A region without an upcoming event is left out entirely -- that is the
|
||||||
|
* whole point of the layout, so `showpast` does not resurrect it. `limit`
|
||||||
|
* stays out too: it caps events, and applying it before the grouping would
|
||||||
|
* drop whole regions from the carousel without the editor seeing why.
|
||||||
|
*
|
||||||
|
* Order is the backend sorting of the categories, so the editor arranges the
|
||||||
|
* carousel by dragging them; `nextEvent.eventstart` is in the payload for a
|
||||||
|
* frontend that would rather sort chronologically.
|
||||||
|
*
|
||||||
|
* @return array<int,array<string,mixed>>
|
||||||
|
*/
|
||||||
|
private function fetchRegions(int $daysInAdvance): array
|
||||||
|
{
|
||||||
|
$regions = $this->fetchRegionCategories();
|
||||||
|
if ($regions === []) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Every category below a region -- the region itself included -- points
|
||||||
|
// at that region, so an event filed under a sub-category still counts.
|
||||||
|
$regionByCategory = [];
|
||||||
|
foreach ($regions as $regionUid => $region) {
|
||||||
|
foreach ($region['categoryUids'] as $categoryUid) {
|
||||||
|
$regionByCategory[$categoryUid] = $regionUid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = $this->fetchUpcomingEventsByCategory(array_keys($regionByCategory), $daysInAdvance);
|
||||||
|
|
||||||
|
// One row per event/category pair, sorted by eventstart -- so the first
|
||||||
|
// row seen for a region is its next event, and the ??= keeps it.
|
||||||
|
$eventsPerRegion = [];
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$regionUid = $regionByCategory[(int)$row['uid_local']] ?? null;
|
||||||
|
if ($regionUid === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$eventsPerRegion[$regionUid][(int)$row['uid']] ??= $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
foreach ($regions as $regionUid => $region) {
|
||||||
|
$regionEvents = $eventsPerRegion[$regionUid] ?? [];
|
||||||
|
if ($regionEvents === []) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$next = reset($regionEvents);
|
||||||
|
$start = (int)($next['eventstart'] ?? 0);
|
||||||
|
$end = (int)($next['eventend'] ?? 0);
|
||||||
|
|
||||||
|
$result[] = [
|
||||||
|
'uid' => $regionUid,
|
||||||
|
'title' => $region['title'],
|
||||||
|
// Plain text, not RTE: sys_category descriptions are named as
|
||||||
|
// out of scope for the richtext conversion in Clause 9.11.
|
||||||
|
'description' => $region['description'],
|
||||||
|
'eventCount' => count($regionEvents),
|
||||||
|
'image' => $this->getEventImage((int)$next['uid']),
|
||||||
|
'nextEvent' => [
|
||||||
|
'uid' => (int)$next['uid'],
|
||||||
|
'title' => (string)($next['title'] ?? ''),
|
||||||
|
'slug' => (string)($next['slug'] ?? ''),
|
||||||
|
'eventstart' => $start > 0 ? date('Y-m-d', $start) : null,
|
||||||
|
'eventend' => $end > 0 ? date('Y-m-d', $end) : null,
|
||||||
|
'eventurl' => (string)($next['eventurl'] ?? ''),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The region categories, keyed by uid and in backend sorting order, each
|
||||||
|
* with its own uid plus every descendant.
|
||||||
|
*
|
||||||
|
* sys_category is read in one go rather than per level: the table is small,
|
||||||
|
* and CustomerlogosJsonRenderer and MarketListJsonRenderer make the same
|
||||||
|
* trade-off.
|
||||||
|
*
|
||||||
|
* @return array<int,array{title:string,description:string,categoryUids:int[]}>
|
||||||
|
*/
|
||||||
|
private function fetchRegionCategories(): array
|
||||||
|
{
|
||||||
|
$qb = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('sys_category');
|
||||||
|
|
||||||
|
$rows = $qb
|
||||||
|
->select('uid', 'title', 'description', 'parent')
|
||||||
|
->from('sys_category')
|
||||||
|
->where(
|
||||||
|
$qb->expr()->eq('deleted', 0),
|
||||||
|
$qb->expr()->eq('hidden', 0)
|
||||||
|
)
|
||||||
|
->orderBy('sorting', 'ASC')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
$byUid = [];
|
||||||
|
$childrenByParent = [];
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$uid = (int)$row['uid'];
|
||||||
|
$byUid[$uid] = $row;
|
||||||
|
$childrenByParent[(int)($row['parent'] ?? 0)][] = $uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
$regions = [];
|
||||||
|
foreach ($childrenByParent[self::REGION_PARENT_CATEGORY] ?? [] as $regionUid) {
|
||||||
|
$regions[$regionUid] = [
|
||||||
|
'title' => (string)($byUid[$regionUid]['title'] ?? ''),
|
||||||
|
'description' => (string)($byUid[$regionUid]['description'] ?? ''),
|
||||||
|
'categoryUids' => $this->collectCategoryBranch($regionUid, $childrenByParent),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $regions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A category plus every descendant, breadth first. Iterative and
|
||||||
|
* seen-guarded: a parent pointing back up the tree would otherwise loop.
|
||||||
|
*
|
||||||
|
* @param array<int,int[]> $childrenByParent
|
||||||
|
* @return int[]
|
||||||
|
*/
|
||||||
|
private function collectCategoryBranch(int $uid, array $childrenByParent): array
|
||||||
|
{
|
||||||
|
$collected = [$uid];
|
||||||
|
$seen = [$uid => true];
|
||||||
|
$queue = [$uid];
|
||||||
|
|
||||||
|
while ($queue !== []) {
|
||||||
|
$current = array_shift($queue);
|
||||||
|
foreach ($childrenByParent[$current] ?? [] as $childUid) {
|
||||||
|
if (isset($seen[$childUid])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$seen[$childUid] = true;
|
||||||
|
$collected[] = $childUid;
|
||||||
|
$queue[] = $childUid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $collected;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upcoming events assigned to any of the given categories, soonest first.
|
||||||
|
* One row per event/category pair, so an event in two regions appears in
|
||||||
|
* both. The "upcoming" test is the one fetchEvents() uses.
|
||||||
|
*
|
||||||
|
* @param int[] $categoryUids
|
||||||
|
* @return array<int,array<string,mixed>>
|
||||||
|
*/
|
||||||
|
private function fetchUpcomingEventsByCategory(array $categoryUids, int $daysInAdvance): array
|
||||||
|
{
|
||||||
|
if ($categoryUids === []) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$todayMidnight = strtotime('today');
|
||||||
|
|
||||||
|
$qb = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('tx_vitec_domain_model_event');
|
||||||
|
$expr = $qb->expr();
|
||||||
|
|
||||||
|
$qb
|
||||||
|
->select('e.uid', 'e.title', 'e.slug', 'e.eventstart', 'e.eventend', 'e.eventurl')
|
||||||
|
->addSelect('mm.uid_local')
|
||||||
|
->from('tx_vitec_domain_model_event', 'e')
|
||||||
|
->join(
|
||||||
|
'e',
|
||||||
|
'sys_category_record_mm',
|
||||||
|
'mm',
|
||||||
|
'mm.uid_foreign = e.uid AND mm.tablenames = ' .
|
||||||
|
$qb->createNamedParameter('tx_vitec_domain_model_event', ParameterType::STRING) .
|
||||||
|
' AND mm.fieldname = ' .
|
||||||
|
$qb->createNamedParameter('categories', ParameterType::STRING)
|
||||||
|
)
|
||||||
|
->where(
|
||||||
|
$expr->in('mm.uid_local', $qb->createNamedParameter($categoryUids, ArrayParameterType::INTEGER)),
|
||||||
|
$expr->eq('e.deleted', 0),
|
||||||
|
$expr->eq('e.hidden', 0),
|
||||||
|
$expr->eq('e.hideonwebsite', 0),
|
||||||
|
$expr->or(
|
||||||
|
$expr->gte('e.eventend', $qb->createNamedParameter($todayMidnight, ParameterType::INTEGER)),
|
||||||
|
$expr->and(
|
||||||
|
$expr->eq('e.eventend', 0),
|
||||||
|
$expr->gte('e.eventstart', $qb->createNamedParameter($todayMidnight, ParameterType::INTEGER))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->orderBy('e.eventstart', 'ASC');
|
||||||
|
|
||||||
|
if ($daysInAdvance > 0) {
|
||||||
|
$qb->andWhere(
|
||||||
|
$expr->lt('e.eventstart', $qb->createNamedParameter($todayMidnight + ($daysInAdvance * 86400), ParameterType::INTEGER))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $qb->executeQuery()->fetchAllAssociative();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string,mixed> $event
|
* @param array<string,mixed> $event
|
||||||
* @return array<string,mixed>
|
* @return array<string,mixed>
|
||||||
|
|||||||
@@ -27,6 +27,10 @@
|
|||||||
<label>Teaser Bar</label>
|
<label>Teaser Bar</label>
|
||||||
<value>teaserbar</value>
|
<value>teaserbar</value>
|
||||||
</numIndex>
|
</numIndex>
|
||||||
|
<numIndex index="3">
|
||||||
|
<label>List by Region</label>
|
||||||
|
<value>regions</value>
|
||||||
|
</numIndex>
|
||||||
</items>
|
</items>
|
||||||
<default>list</default>
|
<default>list</default>
|
||||||
</config>
|
</config>
|
||||||
|
|||||||
@@ -110,6 +110,12 @@ editor:
|
|||||||
- { name: 'Keyboard input', element: 'kbd' }
|
- { name: 'Keyboard input', element: 'kbd' }
|
||||||
- { name: 'Delete / Strike', element: 'del' }
|
- { name: 'Delete / Strike', element: 'del' }
|
||||||
- { name: 'Insert / Underline', element: 'ins' }
|
- { name: 'Insert / Underline', element: 'ins' }
|
||||||
|
# Inline text colours. Class names mirror the button styles
|
||||||
|
# (btn-vitec--blue -> text-vitec--blue) so both share one vocabulary.
|
||||||
|
- { name: 'Text: VITEC Blue', element: 'span', classes: ['text-vitec--blue'] }
|
||||||
|
- { name: 'Text: VITEC Orange', element: 'span', classes: ['text-vitec--orange'] }
|
||||||
|
- { name: 'Text: VITEC Midnight', element: 'span', classes: ['text-vitec--midnight'] }
|
||||||
|
- { name: 'Text: VITEC White', element: 'span', classes: ['text-vitec--white'] }
|
||||||
|
|
||||||
# ---- Alignment -----------------------------------------
|
# ---- Alignment -----------------------------------------
|
||||||
alignment:
|
alignment:
|
||||||
|
|||||||
@@ -125,6 +125,26 @@ use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
|
|||||||
['label' => 'Vitec: Card Style', 'value' => 'vitec-card'],
|
['label' => 'Vitec: Card Style', 'value' => 'vitec-card'],
|
||||||
['label' => 'Vitec: Dark Background', 'value' => 'vitec-dark'],
|
['label' => 'Vitec: Dark Background', 'value' => 'vitec-dark'],
|
||||||
['label' => 'Vitec: Highlight Box', 'value' => 'vitec-highlight'],
|
['label' => 'Vitec: Highlight Box', 'value' => 'vitec-highlight'],
|
||||||
|
|
||||||
|
// Background variants, ordered light to dark. Mutually exclusive by
|
||||||
|
// intent, but frame_class is a multi-select here (selectCheckBox,
|
||||||
|
// see above) and stays that way on purpose -- a restriction would
|
||||||
|
// have to be built for every other class too. If an editor ticks two
|
||||||
|
// backgrounds, both end up comma-separated in the JSON `frameClass`
|
||||||
|
// and the frontend decides which one wins.
|
||||||
|
//
|
||||||
|
// `vitec-bg-graphite`, `-blue` and `-midnight` were already styled in
|
||||||
|
// the frontend bundle but had no backend option; `-white` and
|
||||||
|
// `-light-grey` still need their CSS.
|
||||||
|
//
|
||||||
|
// The older 'Vitec: Dark Background' (vitec-dark) above stays as it
|
||||||
|
// is: existing content depends on it, and it is the most heavily
|
||||||
|
// styled class of them all.
|
||||||
|
['label' => 'Vitec: White Background', 'value' => 'vitec-bg-white'],
|
||||||
|
['label' => 'Vitec: Light-Grey Background', 'value' => 'vitec-bg-light-grey'],
|
||||||
|
['label' => 'Vitec: VITEC-Graphite Background', 'value' => 'vitec-bg-graphite'],
|
||||||
|
['label' => 'Vitec: VITEC-Blue Background', 'value' => 'vitec-bg-blue'],
|
||||||
|
['label' => 'Vitec: VITEC-Midnight Background', 'value' => 'vitec-bg-midnight'],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -132,4 +152,53 @@ use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
|
|||||||
$GLOBALS['TCA']['tt_content']['columns']['tx_vitec_usecase_content'] = [
|
$GLOBALS['TCA']['tt_content']['columns']['tx_vitec_usecase_content'] = [
|
||||||
'config' => ['type' => 'passthrough'],
|
'config' => ['type' => 'passthrough'],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// The standard TYPO3 "Appearance" tab for the Content Blocks elements.
|
||||||
|
//
|
||||||
|
// Content Blocks builds each element's showitem itself
|
||||||
|
// (TcaGenerator::getContentElementStandardShowItem) and appends only the
|
||||||
|
// "Extended" tab -- Appearance is never part of it. The Extbase plugins are
|
||||||
|
// unaffected: ExtensionManagementUtility::addPlugin() seeds their type from
|
||||||
|
// tt_content types['header'], which carries the tab already.
|
||||||
|
//
|
||||||
|
// Until now that meant an editor could not reach layout, frame_class
|
||||||
|
// (including the Vitec classes registered above), spaceBefore/spaceAfter,
|
||||||
|
// sectionIndex or linkToTop on a Content Block at all.
|
||||||
|
//
|
||||||
|
// Deliberately NOT declared as a palette in every config.yaml: palettes live
|
||||||
|
// in TCA under a global identifier, so a second definition of `frames` would
|
||||||
|
// collide with the Core one -- the same trap that took the page module down
|
||||||
|
// on 2026-08-10. Pointing at the Core palettes keeps a single definition.
|
||||||
|
//
|
||||||
|
// This file runs after Content Blocks, which generates its TCA on
|
||||||
|
// BeforeTcaOverridesEvent.
|
||||||
|
$appearanceTab = '--div--;core.form.tabs:appearance,'
|
||||||
|
. '--palette--;;frames,'
|
||||||
|
. '--palette--;;appearanceLinks';
|
||||||
|
$extendedTab = '--div--;core.form.tabs:extended';
|
||||||
|
|
||||||
|
foreach ($GLOBALS['TCA']['tt_content']['types'] as $cType => &$typeConfig) {
|
||||||
|
// Own elements only, and only those still missing the tab -- that skips
|
||||||
|
// the plugins and makes a repeated run a no-op.
|
||||||
|
if (!str_starts_with((string)$cType, 'vitec_')
|
||||||
|
|| !isset($typeConfig['showitem'])
|
||||||
|
|| str_contains($typeConfig['showitem'], '--palette--;;frames')
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content Blocks puts "Extended" last, so Appearance goes in front of it
|
||||||
|
// and the tab order matches the Core content elements. Should a future
|
||||||
|
// version drop that marker, append rather than silently do nothing.
|
||||||
|
if (str_contains($typeConfig['showitem'], $extendedTab)) {
|
||||||
|
$typeConfig['showitem'] = str_replace(
|
||||||
|
$extendedTab,
|
||||||
|
$appearanceTab . ',' . $extendedTab,
|
||||||
|
$typeConfig['showitem']
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$typeConfig['showitem'] = rtrim($typeConfig['showitem'], ", \t\n\r") . ',' . $appearanceTab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($typeConfig);
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -19,5 +19,11 @@ fields:
|
|||||||
value: light
|
value: light
|
||||||
- label: 'LLL:EXT:vitec/Resources/Private/Language/locallang_buttons.xlf:button_style.items.dark.label'
|
- label: 'LLL:EXT:vitec/Resources/Private/Language/locallang_buttons.xlf:button_style.items.dark.label'
|
||||||
value: dark
|
value: dark
|
||||||
|
- label: 'LLL:EXT:vitec/Resources/Private/Language/locallang_buttons.xlf:button_style.items.transparent_white_outline.label'
|
||||||
|
value: transparent_white_outline
|
||||||
|
- label: 'LLL:EXT:vitec/Resources/Private/Language/locallang_buttons.xlf:button_style.items.transparent_black_outline.label'
|
||||||
|
value: transparent_black_outline
|
||||||
|
- label: 'LLL:EXT:vitec/Resources/Private/Language/locallang_buttons.xlf:button_style.items.transparent_red_outline.label'
|
||||||
|
value: transparent_red_outline
|
||||||
- label: 'LLL:EXT:vitec/Resources/Private/Language/locallang_buttons.xlf:button_style.items.text_only.label'
|
- label: 'LLL:EXT:vitec/Resources/Private/Language/locallang_buttons.xlf:button_style.items.text_only.label'
|
||||||
value: text_only
|
value: text_only
|
||||||
|
|||||||
@@ -19,5 +19,11 @@ fields:
|
|||||||
value: light
|
value: light
|
||||||
- label: 'LLL:EXT:vitec/Resources/Private/Language/locallang_buttons.xlf:secondary_button_style.items.dark.label'
|
- label: 'LLL:EXT:vitec/Resources/Private/Language/locallang_buttons.xlf:secondary_button_style.items.dark.label'
|
||||||
value: dark
|
value: dark
|
||||||
|
- label: 'LLL:EXT:vitec/Resources/Private/Language/locallang_buttons.xlf:secondary_button_style.items.transparent_white_outline.label'
|
||||||
|
value: transparent_white_outline
|
||||||
|
- label: 'LLL:EXT:vitec/Resources/Private/Language/locallang_buttons.xlf:secondary_button_style.items.transparent_black_outline.label'
|
||||||
|
value: transparent_black_outline
|
||||||
|
- label: 'LLL:EXT:vitec/Resources/Private/Language/locallang_buttons.xlf:secondary_button_style.items.transparent_red_outline.label'
|
||||||
|
value: transparent_red_outline
|
||||||
- label: 'LLL:EXT:vitec/Resources/Private/Language/locallang_buttons.xlf:secondary_button_style.items.text_only.label'
|
- label: 'LLL:EXT:vitec/Resources/Private/Language/locallang_buttons.xlf:secondary_button_style.items.text_only.label'
|
||||||
value: text_only
|
value: text_only
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
|
||||||
|
<rect x="1.5" y="3.5" width="13" height="9" rx="1" fill="none" stroke="#000" stroke-width="1.2"/>
|
||||||
|
<rect x="8" y="4.7" width="5.3" height="6.6" rx="0.3" fill="#000" opacity="0.5"/>
|
||||||
|
<rect x="3" y="5.4" width="4" height="1" rx="0.2" fill="#000" opacity="0.55"/>
|
||||||
|
<rect x="3" y="7.1" width="3.2" height="0.8" rx="0.2" fill="#000" opacity="0.35"/>
|
||||||
|
<rect x="3" y="8.6" width="4" height="0.8" rx="0.2" fill="#000" opacity="0.35"/>
|
||||||
|
<rect x="3" y="10.2" width="2.6" height="1.2" rx="0.3" fill="#000" opacity="0.6"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 609 B |
@@ -0,0 +1,129 @@
|
|||||||
|
name: vitec/featured-content
|
||||||
|
group: vitec_custom_components
|
||||||
|
prefixFields: true
|
||||||
|
prefixType: vendor
|
||||||
|
|
||||||
|
fields:
|
||||||
|
# ──────────────────────────────────────────────────────────────────────
|
||||||
|
- identifier: tab_content
|
||||||
|
type: Tab
|
||||||
|
label: Content
|
||||||
|
|
||||||
|
- identifier: header_section
|
||||||
|
type: Palette
|
||||||
|
label: Header
|
||||||
|
fields:
|
||||||
|
- identifier: header
|
||||||
|
useExistingField: true
|
||||||
|
required: true
|
||||||
|
- type: Linebreak
|
||||||
|
- identifier: header_layout
|
||||||
|
useExistingField: true
|
||||||
|
- identifier: header_position
|
||||||
|
useExistingField: true
|
||||||
|
- identifier: date
|
||||||
|
useExistingField: true
|
||||||
|
- type: Linebreak
|
||||||
|
- identifier: header_link
|
||||||
|
useExistingField: true
|
||||||
|
- type: Linebreak
|
||||||
|
- identifier: subheader
|
||||||
|
useExistingField: true
|
||||||
|
|
||||||
|
- identifier: bodytext
|
||||||
|
useExistingField: true
|
||||||
|
enableRichtext: true
|
||||||
|
|
||||||
|
- identifier: cta
|
||||||
|
type: Link
|
||||||
|
allowedTypes:
|
||||||
|
- page
|
||||||
|
- url
|
||||||
|
- file
|
||||||
|
- email
|
||||||
|
|
||||||
|
- identifier: cta_label
|
||||||
|
type: Text
|
||||||
|
default: 'Learn more'
|
||||||
|
max: 30
|
||||||
|
|
||||||
|
- identifier: Vitec/ButtonStyle
|
||||||
|
type: Basic
|
||||||
|
|
||||||
|
- identifier: featured_layout
|
||||||
|
type: Select
|
||||||
|
renderType: selectSingle
|
||||||
|
default: text_left
|
||||||
|
items:
|
||||||
|
- label: Text left, media right
|
||||||
|
value: text_left
|
||||||
|
- label: Text right, media left
|
||||||
|
value: text_right
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────
|
||||||
|
- identifier: tab_media
|
||||||
|
type: Tab
|
||||||
|
label: Media
|
||||||
|
|
||||||
|
- identifier: featured_image
|
||||||
|
type: File
|
||||||
|
minitems: 0
|
||||||
|
maxitems: 1
|
||||||
|
allowed: common-image-types
|
||||||
|
extendedPalette: true
|
||||||
|
|
||||||
|
- identifier: featured_video
|
||||||
|
type: File
|
||||||
|
minitems: 0
|
||||||
|
maxitems: 1
|
||||||
|
allowed: mp4,webm,ogv,mov,m4v
|
||||||
|
|
||||||
|
- identifier: featured_video_autoplay
|
||||||
|
type: Checkbox
|
||||||
|
default: 0
|
||||||
|
|
||||||
|
- identifier: featured_video_loop
|
||||||
|
type: Checkbox
|
||||||
|
default: 0
|
||||||
|
|
||||||
|
- identifier: featured_video_muted
|
||||||
|
type: Checkbox
|
||||||
|
default: 1
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────
|
||||||
|
- identifier: tab_overlay
|
||||||
|
type: Tab
|
||||||
|
label: Overlay
|
||||||
|
|
||||||
|
- identifier: overlay_color
|
||||||
|
type: Color
|
||||||
|
valuePicker:
|
||||||
|
items:
|
||||||
|
- label: 'VITEC Blue'
|
||||||
|
value: '#26358C'
|
||||||
|
|
||||||
|
- identifier: overlay_opacity
|
||||||
|
type: Number
|
||||||
|
format: decimal
|
||||||
|
default: 0.4
|
||||||
|
range:
|
||||||
|
lower: 0
|
||||||
|
upper: 1
|
||||||
|
slider:
|
||||||
|
step: 0.05
|
||||||
|
width: 200
|
||||||
|
|
||||||
|
# Horizontal position, not a vertical one: this element is wide rather than
|
||||||
|
# tall, so the overlay runs across the width. 0 = gradient starts at the very
|
||||||
|
# left edge (overlay covers everything from there), 100 = it starts at the
|
||||||
|
# right edge. Which side is covered follows featured_layout, so the text side
|
||||||
|
# stays readable.
|
||||||
|
- identifier: overlay_gradient_start
|
||||||
|
type: Number
|
||||||
|
default: 50
|
||||||
|
range:
|
||||||
|
lower: 0
|
||||||
|
upper: 100
|
||||||
|
slider:
|
||||||
|
step: 1
|
||||||
|
width: 300
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xliff version="1.2">
|
||||||
|
<file source-language="en" datatype="plaintext" original="messages">
|
||||||
|
<body>
|
||||||
|
<!-- Element -->
|
||||||
|
<trans-unit id="title">
|
||||||
|
<source>VITEC · Featured Content</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="description">
|
||||||
|
<source>Wide feature block: headline + RTE text + CTA next to an image or video, with a horizontal overlay gradient.</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<!-- Tabs -->
|
||||||
|
<trans-unit id="tab_content.label">
|
||||||
|
<source>Content</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="tab_media.label">
|
||||||
|
<source>Media</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="tab_overlay.label">
|
||||||
|
<source>Overlay</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<trans-unit id="cta.label">
|
||||||
|
<source>CTA Link</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="cta_label.label">
|
||||||
|
<source>CTA Button Text</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="featured_layout.label">
|
||||||
|
<source>Layout</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="featured_layout.description">
|
||||||
|
<source>Which side the text sits on. The media fills the other side.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="featured_layout.items.text_left.label">
|
||||||
|
<source>Text left, media right</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="featured_layout.items.text_right.label">
|
||||||
|
<source>Text right, media left</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<!-- Media -->
|
||||||
|
<trans-unit id="featured_image.label">
|
||||||
|
<source>Image</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="featured_video.label">
|
||||||
|
<source>Video</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="featured_video.description">
|
||||||
|
<source>If both image and video are set, the video wins and the image serves as its poster.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="featured_video_autoplay.label">
|
||||||
|
<source>Autoplay video</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="featured_video_loop.label">
|
||||||
|
<source>Loop video</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="featured_video_muted.label">
|
||||||
|
<source>Mute video</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<!-- Overlay -->
|
||||||
|
<trans-unit id="overlay_color.label">
|
||||||
|
<source>Overlay Colour</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="overlay_color.description">
|
||||||
|
<source>Colour of the gradient laid over the image or video. Leave empty for no overlay.</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="overlay_opacity.label">
|
||||||
|
<source>Overlay Opacity (0–1)</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="overlay_opacity.description">
|
||||||
|
<source>Strength of the overlay at its fully covered end.</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="overlay_gradient_start.label">
|
||||||
|
<source>Gradient Start (% of width)</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="overlay_gradient_start.description">
|
||||||
|
<source>Where across the width the overlay begins to fade out. 0 = fades out right at the text edge, 100 = the overlay covers the full width. The fade always runs away from the text side, so the text stays readable.</source>
|
||||||
|
</trans-unit>
|
||||||
|
</body>
|
||||||
|
</file>
|
||||||
|
</xliff>
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<f:layout name="Preview"/>
|
||||||
|
<f:section name="Content">
|
||||||
|
|
||||||
|
<f:asset.css identifier="vitec-backend-preview" href="EXT:vitec/Resources/Public/Css/backend-preview.css"/>
|
||||||
|
|
||||||
|
<div class="vitec-preview">
|
||||||
|
|
||||||
|
<f:comment>Thumbnail: video poster falls back to the image</f:comment>
|
||||||
|
<f:if condition="{data.featured_image.0}">
|
||||||
|
<f:then>
|
||||||
|
<f:image image="{data.featured_image.0}"
|
||||||
|
class="vitec-preview__thumb"
|
||||||
|
width="120c"
|
||||||
|
height="80c"
|
||||||
|
alt="Featured Content Preview"/>
|
||||||
|
</f:then>
|
||||||
|
<f:else>
|
||||||
|
<div class="vitec-preview__thumb-placeholder">No Image</div>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<div class="vitec-preview__body">
|
||||||
|
<div class="vitec-preview__label">VITEC · Featured Content</div>
|
||||||
|
|
||||||
|
<h3 class="vitec-preview__headline">
|
||||||
|
<f:if condition="{data.header}">
|
||||||
|
<f:then>{data.header}</f:then>
|
||||||
|
<f:else>
|
||||||
|
<em style="color:#c00;">⚠ Headline missing</em>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<f:if condition="{data.subheader}">
|
||||||
|
<div class="vitec-preview__subline">{data.subheader}</div>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:if condition="{data.bodytext}">
|
||||||
|
<div class="vitec-preview__body-text">
|
||||||
|
<f:format.stripTags>{data.bodytext}</f:format.stripTags>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:if condition="{data.cta_label}">
|
||||||
|
<div class="vitec-preview__ctas">
|
||||||
|
<span class="vitec-preview__cta-btn">{data.cta_label} →</span>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="vitec-preview__settings">
|
||||||
|
|
||||||
|
<span class="vitec-badge">
|
||||||
|
<f:switch expression="{data.featured_layout}">
|
||||||
|
<f:case value="text_left">◧ Text left</f:case>
|
||||||
|
<f:case value="text_right">◨ Text right</f:case>
|
||||||
|
<f:defaultCase>{data.featured_layout}</f:defaultCase>
|
||||||
|
</f:switch>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<f:if condition="{data.featured_video.0}">
|
||||||
|
<span class="vitec-badge">▶ Video</span>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:comment>
|
||||||
|
The preview cannot paint the styles, so the chosen one is named.
|
||||||
|
Same reasoning as in the hero section.
|
||||||
|
</f:comment>
|
||||||
|
<f:if condition="{data.cta_label}">
|
||||||
|
<span class="vitec-badge">Button: {data.button_style}</span>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:if condition="{data.overlay_color}">
|
||||||
|
<span class="vitec-badge">
|
||||||
|
Overlay: {data.overlay_color} · {data.overlay_opacity} · from {data.overlay_gradient_start}%
|
||||||
|
</span>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:if condition="{data.cta_label} && {data.cta.url} == ''">
|
||||||
|
<span class="vitec-badge vitec-badge--warning">⚠ Button text without link</span>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:if condition="{data.featured_image.0} == '' && {data.featured_video.0} == ''">
|
||||||
|
<span class="vitec-badge vitec-badge--warning">⚠ No image or video</span>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</f:section>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<!-- Headless mode: JSON is built by nb-headless-content-blocks -->
|
||||||
|
</html>
|
||||||
@@ -51,9 +51,24 @@ fields:
|
|||||||
default: 'Learn more'
|
default: 'Learn more'
|
||||||
max: 30
|
max: 30
|
||||||
|
|
||||||
|
- identifier: secondary_cta
|
||||||
|
type: Link
|
||||||
|
allowedTypes:
|
||||||
|
- page
|
||||||
|
- url
|
||||||
|
- file
|
||||||
|
- email
|
||||||
|
|
||||||
|
- identifier: secondary_cta_label
|
||||||
|
type: Text
|
||||||
|
max: 30
|
||||||
|
|
||||||
- identifier: Vitec/ButtonStyle
|
- identifier: Vitec/ButtonStyle
|
||||||
type: Basic
|
type: Basic
|
||||||
|
|
||||||
|
- identifier: Vitec/SecondaryButtonStyle
|
||||||
|
type: Basic
|
||||||
|
|
||||||
- identifier: hero_layout_variant
|
- identifier: hero_layout_variant
|
||||||
type: Select
|
type: Select
|
||||||
renderType: selectSingle
|
renderType: selectSingle
|
||||||
|
|||||||
@@ -26,6 +26,17 @@
|
|||||||
<source>CTA Button Text</source>
|
<source>CTA Button Text</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="secondary_cta.label">
|
||||||
|
<source>Second CTA Link (optional)</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="secondary_cta.description">
|
||||||
|
<source>Leave empty for a single button. With both link and label filled, the two buttons render side by side.</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="secondary_cta_label.label">
|
||||||
|
<source>Second CTA Button Text</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
<trans-unit id="hero_image.label">
|
<trans-unit id="hero_image.label">
|
||||||
<source>Hero Image</source>
|
<source>Hero Image</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|||||||
@@ -46,9 +46,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</f:if>
|
</f:if>
|
||||||
|
|
||||||
<f:if condition="{data.cta_label}">
|
<f:if condition="{data.cta_label} || {data.secondary_cta_label}">
|
||||||
<div class="vitec-preview__ctas">
|
<div class="vitec-preview__ctas">
|
||||||
<span class="vitec-preview__cta-btn">{data.cta_label} →</span>
|
<f:if condition="{data.cta_label}">
|
||||||
|
<span class="vitec-preview__cta-btn">{data.cta_label} →</span>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{data.secondary_cta_label}">
|
||||||
|
<span class="vitec-preview__cta-btn vitec-preview__cta-btn--secondary">{data.secondary_cta_label} →</span>
|
||||||
|
</f:if>
|
||||||
</div>
|
</div>
|
||||||
</f:if>
|
</f:if>
|
||||||
</div>
|
</div>
|
||||||
@@ -60,6 +65,27 @@
|
|||||||
Background: {data.background_variant}
|
Background: {data.background_variant}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<f:comment>
|
||||||
|
The preview has no per-style colours, so the chosen styles are named
|
||||||
|
instead of painted — otherwise "Transparent White Outline" would be
|
||||||
|
indistinguishable from the default orange here.
|
||||||
|
</f:comment>
|
||||||
|
<f:if condition="{data.cta_label}">
|
||||||
|
<span class="vitec-badge">Button: {data.button_style}</span>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:if condition="{data.secondary_cta_label}">
|
||||||
|
<span class="vitec-badge">2nd Button: {data.secondary_button_style}</span>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:if condition="{data.secondary_cta_label}">
|
||||||
|
<f:if condition="{data.secondary_cta}">
|
||||||
|
<f:else>
|
||||||
|
<span class="vitec-badge vitec-badge--warning">⚠ 2nd button without link</span>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
<f:if condition="{data.show_logo_wall}">
|
<f:if condition="{data.show_logo_wall}">
|
||||||
<span class="vitec-badge">🏢 Logo Wall</span>
|
<span class="vitec-badge">🏢 Logo Wall</span>
|
||||||
</f:if>
|
</f:if>
|
||||||
|
|||||||
@@ -121,6 +121,29 @@ fields:
|
|||||||
type: Checkbox
|
type: Checkbox
|
||||||
default: 0
|
default: 0
|
||||||
|
|
||||||
|
# Background colour for the whole element. No default on purpose: empty means
|
||||||
|
# "no background", so every existing record keeps the look it has today.
|
||||||
|
# The presets are the VITEC palette from Resources/Public/SCSS/_vitec.scss,
|
||||||
|
# in the same order as the button styles above so both dropdowns read alike.
|
||||||
|
# Lower-case hex, because the native colour picker emits lower case -- an
|
||||||
|
# upper-case preset would store the same colour under two spellings.
|
||||||
|
- identifier: background_color
|
||||||
|
type: Color
|
||||||
|
valuePicker:
|
||||||
|
items:
|
||||||
|
- label: 'VITEC Orange'
|
||||||
|
value: '#f47937'
|
||||||
|
- label: 'VITEC Blue'
|
||||||
|
value: '#26358c'
|
||||||
|
- label: 'VITEC Graphite'
|
||||||
|
value: '#313131'
|
||||||
|
- label: 'VITEC Midnight'
|
||||||
|
value: '#0d0d0d'
|
||||||
|
- label: 'VITEC Light Grey'
|
||||||
|
value: '#cccccc'
|
||||||
|
- label: 'White'
|
||||||
|
value: '#ffffff'
|
||||||
|
|
||||||
- identifier: debug
|
- identifier: debug
|
||||||
type: Checkbox
|
type: Checkbox
|
||||||
default: 0
|
default: 0
|
||||||
|
|||||||
@@ -66,6 +66,14 @@
|
|||||||
<trans-unit id="fullwidth.label">
|
<trans-unit id="fullwidth.label">
|
||||||
<source>Render full-width (edge to edge)</source>
|
<source>Render full-width (edge to edge)</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="background_color.label">
|
||||||
|
<source>Background Colour</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="background_color.description">
|
||||||
|
<source>Background for the whole element. Leave empty for no background. The list next to the field offers the VITEC palette; any other colour can still be picked freely.</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
<trans-unit id="debug.label">
|
<trans-unit id="debug.label">
|
||||||
<source>Allow Debug Output</source>
|
<source>Allow Debug Output</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|||||||
@@ -60,6 +60,18 @@
|
|||||||
<span class="vitec-badge">↔ Full width</span>
|
<span class="vitec-badge">↔ Full width</span>
|
||||||
</f:if>
|
</f:if>
|
||||||
|
|
||||||
|
<f:comment>
|
||||||
|
The dot modifiers in backend-preview.css are fixed palette classes;
|
||||||
|
this colour is free-form, so the swatch carries the stored hex as an
|
||||||
|
inline style. The outline keeps white visible on the light preview.
|
||||||
|
</f:comment>
|
||||||
|
<f:if condition="{data.background_color}">
|
||||||
|
<span class="vitec-badge">
|
||||||
|
<span class="vitec-badge__dot" style="background: {data.background_color}; border: 1px solid #999;"></span>
|
||||||
|
BG: {data.background_color}
|
||||||
|
</span>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
<f:if condition="{data.button_text} && {data.button_link.url} == ''">
|
<f:if condition="{data.button_text} && {data.button_link.url} == ''">
|
||||||
<span class="vitec-badge vitec-badge--warning">⚠ Button text without link</span>
|
<span class="vitec-badge vitec-badge--warning">⚠ Button text without link</span>
|
||||||
</f:if>
|
</f:if>
|
||||||
|
|||||||
@@ -25,6 +25,15 @@
|
|||||||
<trans-unit id="button_style.items.dark.label" resname="button_style.items.dark.label">
|
<trans-unit id="button_style.items.dark.label" resname="button_style.items.dark.label">
|
||||||
<source>Dark</source>
|
<source>Dark</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="button_style.items.transparent_white_outline.label" resname="button_style.items.transparent_white_outline.label">
|
||||||
|
<source>Transparent, White Outline</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="button_style.items.transparent_black_outline.label" resname="button_style.items.transparent_black_outline.label">
|
||||||
|
<source>Transparent, Black Outline</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="button_style.items.transparent_red_outline.label" resname="button_style.items.transparent_red_outline.label">
|
||||||
|
<source>Transparent, Red Outline</source>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="button_style.items.text_only.label" resname="button_style.items.text_only.label">
|
<trans-unit id="button_style.items.text_only.label" resname="button_style.items.text_only.label">
|
||||||
<source>Only Text (no button background)</source>
|
<source>Only Text (no button background)</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
@@ -51,6 +60,15 @@
|
|||||||
<trans-unit id="secondary_button_style.items.dark.label" resname="secondary_button_style.items.dark.label">
|
<trans-unit id="secondary_button_style.items.dark.label" resname="secondary_button_style.items.dark.label">
|
||||||
<source>Dark</source>
|
<source>Dark</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="secondary_button_style.items.transparent_white_outline.label" resname="secondary_button_style.items.transparent_white_outline.label">
|
||||||
|
<source>Transparent, White Outline</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="secondary_button_style.items.transparent_black_outline.label" resname="secondary_button_style.items.transparent_black_outline.label">
|
||||||
|
<source>Transparent, Black Outline</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="secondary_button_style.items.transparent_red_outline.label" resname="secondary_button_style.items.transparent_red_outline.label">
|
||||||
|
<source>Transparent, Red Outline</source>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="secondary_button_style.items.text_only.label" resname="secondary_button_style.items.text_only.label">
|
<trans-unit id="secondary_button_style.items.text_only.label" resname="secondary_button_style.items.text_only.label">
|
||||||
<source>Only Text (no button background)</source>
|
<source>Only Text (no button background)</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|||||||
@@ -73,6 +73,29 @@
|
|||||||
color: #26358c;
|
color: #26358c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Inline text colours. The names mirror the button styles above
|
||||||
|
(btn-vitec--blue -> text-vitec--blue) so both use one vocabulary. */
|
||||||
|
.text-vitec--orange {
|
||||||
|
color: #f47937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-vitec--blue {
|
||||||
|
color: #26358c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-vitec--midnight {
|
||||||
|
color: #0d0d0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-vitec--white {
|
||||||
|
color: #ffffff;
|
||||||
|
/* Editor only: white on the white editor canvas is invisible, so the preview
|
||||||
|
puts it on a dark plate. This file is loaded into the RTE iframe alone --
|
||||||
|
the frontend styles the class itself and needs no plate. */
|
||||||
|
background-color: #313131;
|
||||||
|
padding: 0 0.15em;
|
||||||
|
}
|
||||||
|
|
||||||
.text-start {
|
.text-start {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -401,6 +401,6 @@ AuthUserFile "/usr/www/users/vitecevo/live/public/.htpasswd"
|
|||||||
AuthName "DEV"
|
AuthName "DEV"
|
||||||
AuthType Basic
|
AuthType Basic
|
||||||
<RequireAny>
|
<RequireAny>
|
||||||
Require ip 217.70.192.164
|
Require ip 217.70.192.164
|
||||||
Require valid-user
|
Require valid-user
|
||||||
</RequireAny>
|
</RequireAny>
|
||||||
|
|||||||
@@ -25,8 +25,8 @@
|
|||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<title>VITEC</title>
|
<title>VITEC</title>
|
||||||
<script type="module" crossorigin src="/_frontend/assets/index-DjsvU0JX.js"></script>
|
<script type="module" crossorigin src="/_frontend/assets/index-CI1dU4kK.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/_frontend/assets/index-CUeIsXnS.css">
|
<link rel="stylesheet" crossorigin href="/_frontend/assets/index-DtQru7Bt.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="page"></div>
|
<div id="page"></div>
|
||||||
|
|||||||
Reference in New Issue
Block a user