Skip to content

Commit 6fa584b

Browse files
authored
[FEATURE] Replace Tooltip with Modal for Code and Class Information (#926)
Replaced the tooltip with a modal to provide a more detailed and interactive interface for displaying code and class information. ![image](https://github.com/user-attachments/assets/7328f0b3-c492-4cda-8d99-1a0f45fa9f92) Resolves #743
1 parent 75426b8 commit 6fa584b

File tree

17 files changed

+2884
-1051
lines changed

17 files changed

+2884
-1051
lines changed
Lines changed: 95 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,98 @@
11
(() => {
2-
var popoverTriggerList = [].slice.call(document.querySelectorAll('.code-inline[aria-description]'));
3-
var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
4-
var ariaDescription = popoverTriggerEl.getAttribute('aria-description');
5-
var ariaDetails = popoverTriggerEl.getAttribute('aria-details');
6-
return new bootstrap.Popover(popoverTriggerEl, {
7-
title:ariaDetails?ariaDescription:'',
8-
content: ariaDetails?ariaDetails:ariaDescription,
9-
trigger: 'hover',
10-
placement: 'bottom'
2+
const SELECTOR_MODAL = '#generalModal';
3+
const SELECTOR_COPY_BUTTON = '.copy-button';
4+
const SELECTOR_ALERT_SUCCESS = '#general-alert-success';
5+
6+
function handleCopyButtons(generalModal) {
7+
const alertSuccessDiv = generalModal.querySelector(SELECTOR_ALERT_SUCCESS);
8+
const copyButtons = generalModal.querySelectorAll(SELECTOR_COPY_BUTTON);
9+
if (!navigator.clipboard || !navigator.clipboard.writeText) {
10+
console.info('"navigator.clipboard.writeText" is not available. Update to a modern browser to copy code to the system\'s clipboard');
11+
copyButtons.forEach(button => button.disabled = true);
12+
} else {
13+
copyButtons.forEach(button => {
14+
button.addEventListener('click', function () {
15+
const targetId = this.getAttribute('data-target');
16+
const targetElement = generalModal.querySelector(`#${targetId}`);
17+
if (!targetElement) {
18+
console.warn('Cannot copy as no input is available!');
19+
return;
20+
}
21+
alertSuccessDiv.classList.remove('d-none');
22+
alertSuccessDiv.innerHTML = `Code <code>${htmlEscape(targetElement.value)}</code> was copied to your clipboard.`;
23+
navigator.clipboard.writeText(targetElement.value);
24+
});
25+
});
26+
}
27+
}
28+
29+
function htmlEscape(text) {
30+
const div = document.createElement('div');
31+
div.textContent = text;
32+
return div.innerHTML;
33+
}
34+
35+
const generalModal = document.querySelector(SELECTOR_MODAL);
36+
if (generalModal) {
37+
generalModal.addEventListener('show.bs.modal', function (event) {
38+
const item = event.relatedTarget;
39+
if (!item.dataset.code) {
40+
return;
41+
}
42+
const generalModalLabel = generalModal.querySelector('#generalModalLabel');
43+
const content = generalModal.querySelector('#generalModalContent');
44+
generalModalLabel.innerText = item.dataset.code;
45+
46+
handleCopyButtons(generalModal);
47+
content.innerHTML = '';
48+
if (item.dataset.shortdescription) {
49+
content.innerHTML += `<p><strong>Language info:</strong> ${item.dataset.shortdescription}</p>`;
50+
}
51+
if (item.dataset.details) {
52+
content.innerHTML += `<p>${item.dataset.details}</p>`;
53+
}
54+
content.innerHTML += `
55+
<div class="mb-3">
56+
<label class="form-label" for="code-snippet">Code Snippet: </label>
57+
<div class="input-group">
58+
<textarea class="form-control code" id="code-snippet" readonly>${item.dataset.code}</textarea>
59+
<button type="button" class="btn btn-outline-secondary copy-button" data-target="code-snippet"><i class="far fa-clone"></i></button>
60+
</div>
61+
</div>
62+
`;
63+
64+
if (item.dataset.fqn) {
65+
if (item.dataset.fqn !== item.dataset.code) {
66+
content.innerHTML += `
67+
<div class="mb-3">
68+
<label class="form-label" for="fqn-snippet">Fully Qualified Name (FQN): </label>
69+
<div class="input-group">
70+
<textarea class="form-control code" id="fqn-snippet" readonly>${item.dataset.fqn}</textarea>
71+
<button type="button" class="btn btn-outline-secondary copy-button" data-target="fqn-snippet"><i class="far fa-clone"></i></button>
72+
</div>
73+
</div>
74+
`;
75+
}
76+
77+
content.innerHTML += `
78+
<div class="mb-3">
79+
<label class="form-label" for="use-statement">PHP Use Statement: </label>
80+
<div class="input-group">
81+
<textarea class="form-control code" id="use-statement" readonly>use ${item.dataset.fqn};</textarea>
82+
<button type="button" class="btn btn-outline-secondary copy-button" data-target="use-statement"><i class="far fa-clone"></i></button>
83+
</div>
84+
</div>
85+
`;
86+
}
87+
88+
let links = '';
89+
if (item.dataset.morelink) {
90+
links += `<a class="btn btn-light" href="${item.dataset.morelink}" target="_blank">More Info</a>`;
91+
}
92+
if (links) {
93+
content.innerHTML += `<div class="btn-group mt-2" role="group">${links}</div>`;
94+
}
95+
handleCopyButtons(generalModal);
1196
});
12-
});
97+
}
1398
})();

packages/typo3-docs-theme/resources/public/js/theme.min.js

Lines changed: 29 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
{% apply spaceless %}
22
<code class="code-inline{% if node.value|length > 10 %} code-inline-long{% endif %}"
33
translate="no"
4-
aria-description="{{ node.language|raw }}"
5-
aria-details="{{ node.helpText|raw }}"
6-
data-bs-html="true">
4+
data-bs-toggle="modal"
5+
data-bs-target="#generalModal"
6+
data-code="{{ node.value }}"
7+
data-shortdescription="{{ node.language|raw }}"
8+
data-details="{{ node.helpText|raw }}"
9+
data-morelink="{{ node.info.url }}"
10+
{%- if node.info.fqn %}
11+
data-fqn="{{ node.info.fqn }}"
12+
{%- endif %}
13+
data-bs-html="true"
14+
>
15+
{{ replaceLineBreakOpportunityTags(node.value) }}
716
{%- if node.info.url %}
8-
<a href="{{ node.info['url'] }}" target="_blank">
9-
{{- replaceLineBreakOpportunityTags(node.value) }} <span class="text-secondary"><i class="fa-regular fa-circle-question"></i></span></a>
10-
{%- else -%}
11-
{{ replaceLineBreakOpportunityTags(node.value) }}
17+
<span class="text-secondary"><i class="fa-regular fa-circle-question"></i></span>
1218
{%- endif -%}
1319
</code>
1420
{% endapply %}

packages/typo3-docs-theme/src/TextRoles/PhpTextRole.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ private function getClassCodeNode(string $fqn, array $apiInfo, string $role, str
152152
if ($apiInfo['summary']) {
153153
$infoArray[] = '<em>' . $apiInfo['summary'] . '</em>';
154154
}
155+
$apiInfo['fqn'] = $fqn;
155156
$info = implode('<br>', $infoArray);
156157
$info = str_replace('\\', '&#8203;\\', $info);
157158
return new CodeInlineNode($name, 'PHP ' . $type, $info, $apiInfo);

packages/typo3-guides-cli/src/Command/ConfigureCommand.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -576,9 +576,16 @@ private function operateOnXml(string $config, InputInterface $input, OutputInter
576576

577577
private function createEmptyGuides(string $config, OutputInterface $output): bool
578578
{
579-
if (!is_dir(dirname($config))) {
580-
$output->writeln(sprintf('<error>Cannot create guides.xml in missing directory "%s"</error>', dirname($config)));
581-
return false;
579+
$directory = dirname($config);
580+
581+
// Ensure the directory exists before proceeding
582+
if (!is_dir($directory)) {
583+
if (!mkdir($directory, 0o777, true) && !is_dir($directory)) {
584+
$output->writeln(sprintf('<error>Failed to create missing directory "%s"</error>', $directory));
585+
return false;
586+
}
587+
// Now the directory exists, proceed with the operation
588+
$output->writeln(sprintf('<info>Created directory "%s" for guides.xml</info>', $directory));
582589
}
583590

584591
$fp = fopen($config, 'w');

tests/Integration/tests-full/changelog/expected/Changelog/12.0/Breaking-87616-RemovedHookForAlteringPageLinks.html

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -170,25 +170,40 @@ <h2>Description<a class="headerlink" href="#description" data-bs-toggle="modal"
170170

171171
<p>The hook <code class="code-inline code-inline-long"
172172
translate="no"
173-
aria-description="Global PHP configuration"
174-
aria-details="The main configuration is achieved via a set of global
173+
data-bs-toggle="modal"
174+
data-bs-target="#generalModal"
175+
data-code="$GLOBALS[&#039;TYPO3_CONF_VARS&#039;][&#039;SC_OPTIONS&#039;][&#039;typolinkProcessing&#039;][&#039;typolinkModifyParameterForPageLinks&#039;]"
176+
data-shortdescription="Global PHP configuration"
177+
data-details="The main configuration is achieved via a set of global
175178
settings stored in a global array called $GLOBALS['TYPO3_CONF_VARS'].
176179
They are commonly set in config/system/settings.php, config/system/additional.php,
177180
or the ext_localconf.php file of an extension. "
178-
data-bs-html="true"><a href="https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/Configuration/Typo3ConfVars/Index.html" target="_blank">$GLOBALS<wbr>[&#039;TYPO3_<wbr>CONF_<wbr>VARS&#039;]<wbr>[&#039;SC_<wbr>OPTIONS&#039;]<wbr>[&#039;typolink<wbr>Processing&#039;]<wbr>[&#039;typolink<wbr>Modify<wbr>Parameter<wbr>For<wbr>Page<wbr>Links&#039;] <span class="text-secondary"><i class="fa-regular fa-circle-question"></i></span></a></code>
181+
data-morelink="https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/Configuration/Typo3ConfVars/Index.html" data-bs-html="true"
182+
>
183+
$GLOBALS<wbr>[&#039;TYPO3_<wbr>CONF_<wbr>VARS&#039;]<wbr>[&#039;SC_<wbr>OPTIONS&#039;]<wbr>[&#039;typolink<wbr>Processing&#039;]<wbr>[&#039;typolink<wbr>Modify<wbr>Parameter<wbr>For<wbr>Page<wbr>Links&#039;] <span class="text-secondary"><i class="fa-regular fa-circle-question"></i></span></code>
179184
has been removed in favor of a new PSR-14 event <code class="code-inline code-inline-long"
180185
translate="no"
181-
aria-description="PHP class"
182-
aria-details="<code>final class ModifyPageLinkConfigurationEvent</code><br><em>A generic PSR 14 Event to allow modifying the incoming (and resolved) page when building a &quot;page link&quot;.</em>"
183-
data-bs-html="true"><a href="https://api.typo3.org/main/classes/TYPO3-CMS-Frontend-Event-ModifyPageLinkConfigurationEvent.html" target="_blank">\TYPO3\<wbr>CMS\<wbr>Frontend\<wbr>Event\<wbr>Modify<wbr>Page<wbr>Link<wbr>Configuration<wbr>Event <span class="text-secondary"><i class="fa-regular fa-circle-question"></i></span></a></code>.</p>
186+
data-bs-toggle="modal"
187+
data-bs-target="#generalModal"
188+
data-code="\TYPO3\CMS\Frontend\Event\ModifyPageLinkConfigurationEvent"
189+
data-shortdescription="PHP class"
190+
data-details="<code>final class ModifyPageLinkConfigurationEvent</code><br><em>A generic PSR 14 Event to allow modifying the incoming (and resolved) page when building a &quot;page link&quot;.</em>"
191+
data-morelink="https://api.typo3.org/main/classes/TYPO3-CMS-Frontend-Event-ModifyPageLinkConfigurationEvent.html" data-fqn="\TYPO3\CMS\Frontend\Event\ModifyPageLinkConfigurationEvent" data-bs-html="true"
192+
>
193+
\TYPO3\<wbr>CMS\<wbr>Frontend\<wbr>Event\<wbr>Modify<wbr>Page<wbr>Link<wbr>Configuration<wbr>Event <span class="text-secondary"><i class="fa-regular fa-circle-question"></i></span></code>.</p>
184194

185195

186196
<p>The event is called after TYPO3 has already prepared some functionality
187197
within the <code class="code-inline code-inline-long"
188198
translate="no"
189-
aria-description="Code written in PHP"
190-
aria-details="Dynamic server-side scripting language."
191-
data-bs-html="true">Page<wbr>Link<wbr>Builder</code>. This therefore allows to modify more
199+
data-bs-toggle="modal"
200+
data-bs-target="#generalModal"
201+
data-code="PageLinkBuilder"
202+
data-shortdescription="Code written in PHP"
203+
data-details="Dynamic server-side scripting language."
204+
data-morelink="" data-bs-html="true"
205+
>
206+
Page<wbr>Link<wbr>Builder</code>. This therefore allows to modify more
192207
properties, if needed.</p>
193208

194209
</section>

tests/Integration/tests-full/edit-on-github/edit-on-github-per-page/expected/page1.html

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,14 @@ <h1>Avatar ViewHelper <code class="code-inline" translate="no">&lt;be:<wbr>avata
136136

137137
<p>Render the avatar markup, including the <code class="code-inline"
138138
translate="no"
139-
aria-description="Code written in HTML"
140-
aria-details="HyperText Markup Language."
141-
data-bs-html="true">&lt;img&gt;</code> tag, for a given backend user.</p>
139+
data-bs-toggle="modal"
140+
data-bs-target="#generalModal"
141+
data-code="&lt;img&gt;"
142+
data-shortdescription="Code written in HTML"
143+
data-details="HyperText Markup Language."
144+
data-morelink="" data-bs-html="true"
145+
>
146+
&lt;img&gt;</code> tag, for a given backend user.</p>
142147

143148
</section>
144149
<!-- content end -->

tests/Integration/tests/code-inline/expected/index.html

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,29 @@ <h1>Inline code<a class="headerlink" href="#inline-code" data-bs-toggle="modal"
44

55
<p><code class="code-inline code-inline-long"
66
translate="no"
7-
aria-description="Code written in Shell Script"
8-
aria-details="Raw command line interface code on operating-system level."
9-
data-bs-html="true">echo &#039;Hello&#039;</code></p>
7+
data-bs-toggle="modal"
8+
data-bs-target="#generalModal"
9+
data-code="echo &#039;Hello&#039;"
10+
data-shortdescription="Code written in Shell Script"
11+
data-details="Raw command line interface code on operating-system level."
12+
data-morelink="" data-bs-html="true"
13+
>
14+
echo &#039;Hello&#039;</code></p>
1015

1116

1217
<p><code class="code-inline">body <wbr>{ background: red; }</code></p>
1318

1419

1520
<p><code class="code-inline"
1621
translate="no"
17-
aria-description="Code written in PHP"
18-
aria-details="Dynamic server-side scripting language."
19-
data-bs-html="true">$var = 1;</code></p>
22+
data-bs-toggle="modal"
23+
data-bs-target="#generalModal"
24+
data-code="$var = 1;"
25+
data-shortdescription="Code written in PHP"
26+
data-details="Dynamic server-side scripting language."
27+
data-morelink="" data-bs-html="true"
28+
>
29+
$var = 1;</code></p>
2030

2131
</section>
2232
<!-- content end -->

tests/Integration/tests/code/code-php/expected/index.html

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@ <h1>PHP Code<a class="headerlink" href="#php-code" data-bs-toggle="modal" data-b
44

55
<p><code class="code-inline code-inline-long"
66
translate="no"
7-
aria-description="PHP class or interface"
8-
aria-details="This is a fully-qualified class or interface name,
7+
data-bs-toggle="modal"
8+
data-bs-target="#generalModal"
9+
data-code="\T3Docs\Typo3DocsTheme\TextRoles\PhpTextRole"
10+
data-shortdescription="PHP class or interface"
11+
data-details="This is a fully-qualified class or interface name,
912
try searching for \T3Docs\Typo3DocsTheme\TextRoles\PhpTextRole in the internet."
10-
data-bs-html="true">\T3Docs\<wbr>Typo3Docs<wbr>Theme\<wbr>Text<wbr>Roles\<wbr>Php<wbr>Text<wbr>Role</code>.</p>
13+
data-morelink="" data-bs-html="true"
14+
>
15+
\T3Docs\<wbr>Typo3Docs<wbr>Theme\<wbr>Text<wbr>Roles\<wbr>Php<wbr>Text<wbr>Role</code>.</p>
1116

1217
</section>
1318
<!-- content end -->

0 commit comments

Comments
 (0)