feat: improve website accessibility interactions

This commit is contained in:
sladro 2026-04-13 12:10:29 +08:00
parent 03497f97a7
commit f033f29c08
22 changed files with 351 additions and 151 deletions

View File

@ -70,6 +70,13 @@ export function AddDiv (parentElement, className, innerHTML)
return AddDomElement (parentElement, 'div', className, innerHTML); return AddDomElement (parentElement, 'div', className, innerHTML);
} }
export function AddButtonElement (parentElement, className, innerHTML)
{
let button = AddDomElement (parentElement, 'button', className, innerHTML);
button.setAttribute ('type', 'button');
return button;
}
export function ClearDomElement (element) export function ClearDomElement (element)
{ {
while (element.firstChild) { while (element.firstChild) {
@ -139,3 +146,10 @@ export function CreateDiv (className, innerHTML)
{ {
return CreateDomElement ('div', className, innerHTML); return CreateDomElement ('div', className, innerHTML);
} }
export function CreateButtonElement (className, innerHTML)
{
let button = CreateDomElement ('button', className, innerHTML);
button.setAttribute ('type', 'button');
return button;
}

View File

@ -51,18 +51,23 @@ div.ov_thin_scrollbar::-webkit-scrollbar-thumb
background: #cccccc; background: #cccccc;
} }
div.ov_button .ov_button
{ {
color: var(--ov_button_text_color); color: var(--ov_button_text_color);
background: var(--ov_button_color); background: var(--ov_button_color);
text-align: center; text-align: center;
padding: 3px; padding: 8px 16px;
min-height: 44px;
border: 1px solid var(--ov_button_color); border: 1px solid var(--ov_button_color);
border-radius: 5px; border-radius: 5px;
cursor: pointer; cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
} }
div.ov_button.outline .ov_button.outline
{ {
color: var(--ov_outline_button_text_color); color: var(--ov_outline_button_text_color);
background: transparent; background: transparent;
@ -173,7 +178,6 @@ input.ov_slider
{ {
height: 1px; height: 1px;
background: var(--ov_border_color); background: var(--ov_border_color);
outline: none;
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
appearance: none; appearance: none;
@ -207,7 +211,7 @@ span.ov_slider_label
bottom: -4px; bottom: -4px;
} }
div.ov_toggle button.ov_toggle
{ {
width: 24px; width: 24px;
height: 8px; height: 8px;
@ -218,7 +222,7 @@ div.ov_toggle
cursor: pointer; cursor: pointer;
} }
div.ov_toggle_slider button.ov_toggle div.ov_toggle_slider
{ {
width: 6px; width: 6px;
height: 6px; height: 6px;
@ -227,12 +231,12 @@ div.ov_toggle_slider
border: 1px solid var(--ov_foreground_color); border: 1px solid var(--ov_foreground_color);
} }
div.ov_toggle.on button.ov_toggle.on
{ {
background: var(--ov_foreground_color); background: var(--ov_foreground_color);
} }
div.ov_toggle.on div.ov_toggle_slider button.ov_toggle.on div.ov_toggle_slider
{ {
background: var(--ov_background_color); background: var(--ov_background_color);
transform: translateX(16px); transform: translateX(16px);
@ -247,13 +251,13 @@ div.ov_svg_icon.selected:hover
color: var(--ov_hover_text_color); color: var(--ov_hover_text_color);
} }
div.ov_button:hover .ov_button:hover
{ {
background: var(--ov_button_hover_color); background: var(--ov_button_hover_color);
border: 1px solid var(--ov_button_hover_color); border: 1px solid var(--ov_button_hover_color);
} }
div.ov_button.outline:hover .ov_button.outline:hover
{ {
background: var(--ov_outline_button_hover_color); background: var(--ov_outline_button_hover_color);
border: 1px solid var(--ov_outline_button_color); border: 1px solid var(--ov_outline_button_color);

View File

@ -28,6 +28,21 @@ a
text-decoration: none; text-decoration: none;
} }
button
{
font-family: Quicksand, Helvetica, sans-serif;
font-size: 16px;
}
button[class]
{
color: inherit;
background: none;
border: 0px;
padding: 0px;
margin: 0px;
}
img img
{ {
display: block; display: block;
@ -54,7 +69,18 @@ input, select, textarea
{ {
font-family: Quicksand, Helvetica, sans-serif; font-family: Quicksand, Helvetica, sans-serif;
font-size: 16px; font-size: 16px;
outline: none; }
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
canvas:focus-visible,
[role=button]:focus-visible
{
outline: 2px solid var(--ov_focus_ring_color);
outline-offset: 2px;
} }
@media (hover) @media (hover)

View File

@ -57,7 +57,7 @@ div.ov_dialog div.ov_dialog_buttons_inner
overflow: auto; overflow: auto;
} }
div.ov_dialog div.ov_dialog_buttons div.ov_dialog_button div.ov_dialog div.ov_dialog_buttons .ov_dialog_button
{ {
margin-left: 10px; margin-left: 10px;
width: 80px; width: 80px;
@ -104,7 +104,7 @@ div.ov_dialog div.ov_dialog_import_file_list
overflow: auto; overflow: auto;
} }
div.ov_dialog div.ov_dialog_file_link div.ov_dialog .ov_dialog_file_link
{ {
color: var(--ov_button_color); color: var(--ov_button_color);
padding: 5px; padding: 5px;
@ -114,7 +114,7 @@ div.ov_dialog div.ov_dialog_file_link
cursor: pointer; cursor: pointer;
} }
div.ov_dialog div.ov_dialog_file_link div.ov_file_link_img div.ov_dialog .ov_dialog_file_link div.ov_file_link_img
{ {
color: var(--ov_button_color); color: var(--ov_button_color);
margin-top: 2px; margin-top: 2px;
@ -122,7 +122,7 @@ div.ov_dialog div.ov_dialog_file_link div.ov_file_link_img
float: left; float: left;
} }
div.ov_dialog div.ov_dialog_file_link div.ov_dialog_file_link_text div.ov_dialog .ov_dialog_file_link div.ov_dialog_file_link_text
{ {
float: left; float: left;
} }
@ -145,7 +145,7 @@ div.ov_dialog div.ov_dialog_copyable_input input
box-sizing: border-box; box-sizing: border-box;
} }
div.ov_dialog div.ov_dialog_copyable_input div.ov_dialog_copyable_input_button div.ov_dialog div.ov_dialog_copyable_input .ov_dialog_copyable_input_button
{ {
width: 28%; width: 28%;
margin-left: 0px; margin-left: 0px;
@ -196,12 +196,15 @@ div.ov_popup div.ov_popup_list
overflow: auto; overflow: auto;
} }
div.ov_popup div.ov_popup_list_item div.ov_popup .ov_popup_list_item
{ {
padding: 10px; padding: 10px;
border-radius: 5px; border-radius: 5px;
cursor: pointer; cursor: pointer;
overflow: auto; overflow: auto;
display: block;
width: 100%;
text-align: left;
} }
div.ov_popup div.ov_popup_list_item_icon div.ov_popup div.ov_popup_list_item_icon
@ -299,18 +302,18 @@ div.ov_snapshot_dialog_separator
@media (hover) @media (hover)
{ {
div.ov_dialog div.ov_dialog_file_link:hover div.ov_dialog .ov_dialog_file_link:hover
{ {
color: var(--ov_hover_text_color); color: var(--ov_hover_text_color);
background: var(--ov_hover_color); background: var(--ov_hover_color);
} }
div.ov_dialog div.ov_dialog_file_link:hover div.ov_file_link_img div.ov_dialog .ov_dialog_file_link:hover div.ov_file_link_img
{ {
color: var(--ov_hover_text_color); color: var(--ov_hover_text_color);
} }
div.ov_popup div.ov_popup_list_item:hover div.ov_popup .ov_popup_list_item:hover
{ {
background: var(--ov_hover_color); background: var(--ov_hover_color);
} }

View File

@ -6,14 +6,15 @@ div.ov_navigator_buttons
overflow: auto; overflow: auto;
} }
div.ov_navigator_button .ov_navigator_button
{ {
float: left; float: left;
cursor: pointer; cursor: pointer;
padding: 5px; padding: 13px;
display: block;
} }
div.ov_navigator_button.right .ov_navigator_button.right
{ {
float: right; float: right;
} }
@ -76,7 +77,7 @@ div.ov_navigator_info_panel div.ov_navigator_info_panel_title:hover
background: var(--ov_hover_color); background: var(--ov_hover_color);
} }
div.ov_navigator_button:hover .ov_navigator_button:hover
{ {
background: var(--ov_hover_color); background: var(--ov_hover_color);
} }

View File

@ -8,10 +8,21 @@ div.ov_panel_set_right_container div.ov_panel_set_menu
float: right; float: right;
} }
div.ov_panel_set_menu div.ov_panel_set_menu_button .ov_panel_set_menu .ov_panel_set_menu_button
{ {
padding: 10px; padding: 13px;
cursor: pointer; cursor: pointer;
display: block;
}
.ov_panel_set_menu .ov_panel_set_menu_button.selected
{
background: var(--ov_hover_color);
}
.ov_panel_set_menu .ov_panel_set_menu_button.selected .ov_svg_icon
{
color: var(--ov_selected_icon_color);
} }
div.ov_panel_set_container div.ov_panel_set_content div.ov_panel_set_container div.ov_panel_set_content
@ -28,13 +39,16 @@ div.ov_panel_set_right_container div.ov_panel_set_content
overflow: auto; overflow: auto;
} }
div.ov_panel_button .ov_panel_button
{ {
cursor: pointer; cursor: pointer;
margin-top: 10px; margin-top: 10px;
border: 1px solid var(--ov_border_color); border: 1px solid var(--ov_border_color);
border-radius: 5px; border-radius: 5px;
overflow: auto; overflow: auto;
display: block;
width: 100%;
text-align: left;
} }
div.ov_panel_button_text div.ov_panel_button_text
@ -60,13 +74,13 @@ div.ov_panel_button_left_icon
@media (hover) @media (hover)
{ {
div.ov_panel_button:hover .ov_panel_button:hover
{ {
background: var(--ov_hover_color); background: var(--ov_hover_color);
} }
div.ov_panel_set_menu div.ov_panel_set_menu_button:hover .ov_panel_set_menu .ov_panel_set_menu_button:hover
{ {
background: var(--ov_hover_color); background: var(--ov_hover_color);
} }

View File

@ -40,18 +40,19 @@ div.ov_sidebar_parameter div.ov_sidebar_parameter_text
float: left; float: left;
} }
div.ov_sidebar_image_picker .ov_sidebar_image_picker
{ {
background-size: cover; background-size: cover;
background-position: center center; background-position: center center;
width: 28px; width: 44px;
height: 13px; height: 26px;
margin-top: 3px; margin-top: 3px;
margin-right: 10px; margin-right: 10px;
border: 1px solid var(--ov_border_color); border: 1px solid var(--ov_border_color);
border-radius: 3px; border-radius: 3px;
float: left; float: left;
cursor: pointer; cursor: pointer;
display: block;
} }
div.ov_sidebar_content div.ov_sidebar_content
@ -105,7 +106,6 @@ div.ov_sidebar_content button.pcr-button
margin: 3px 10px 3px 0px; margin: 3px 10px 3px 0px;
border: 1px solid var(--ov_border_color); border: 1px solid var(--ov_border_color);
box-shadow: none; box-shadow: none;
outline: none;
float: left; float: left;
} }

View File

@ -3,18 +3,18 @@
--ov_foreground_color: #000000; --ov_foreground_color: #000000;
--ov_background_color: #ffffff; --ov_background_color: #ffffff;
--ov_disabled_foreground_color: #cccccc; --ov_disabled_foreground_color: #cccccc;
--ov_button_color: #3393bd; --ov_button_color: #1f6f94;
--ov_button_hover_color: #146a8f; --ov_button_hover_color: #165876;
--ov_button_text_color: #ffffff; --ov_button_text_color: #ffffff;
--ov_outline_button_color: #3393bd; --ov_outline_button_color: #1f6f94;
--ov_outline_button_hover_color: #c9e5f8; --ov_outline_button_hover_color: #d9e9f2;
--ov_outline_button_text_color: #3393bd; --ov_outline_button_text_color: #1f6f94;
--ov_icon_color: #263238; --ov_icon_color: #263238;
--ov_light_icon_color: #838383; --ov_light_icon_color: #838383;
--ov_selected_icon_color: #3393bd; --ov_selected_icon_color: #1f6f94;
--ov_disabled_icon_color: #cccccc; --ov_disabled_icon_color: #cccccc;
--ov_hover_color: #c9e5f8; --ov_hover_color: #d9e9f2;
--ov_hover_text_color: #3393bd; --ov_hover_text_color: #1f6f94;
--ov_logo_text_color: #15334a; --ov_logo_text_color: #15334a;
--ov_logo_border_color: #000000; --ov_logo_border_color: #000000;
--ov_toolbar_background_color: #f5f5f5; --ov_toolbar_background_color: #f5f5f5;
@ -26,19 +26,20 @@
--ov_dialog_control_border_color: #e1e1e1; --ov_dialog_control_border_color: #e1e1e1;
--ov_border_color: #dddddd; --ov_border_color: #dddddd;
--ov_shadow: 0px 0px 10px #cccccc; --ov_shadow: 0px 0px 10px #cccccc;
--ov_focus_ring_color: #0d5c80;
--ov_foreground_color_dark: #fafafa; --ov_foreground_color_dark: #fafafa;
--ov_background_color_dark: #2a2b2e; --ov_background_color_dark: #2a2b2e;
--ov_disabled_foreground_color_dark: #888888; --ov_disabled_foreground_color_dark: #888888;
--ov_button_color_dark: #3393bd; --ov_button_color_dark: #3e8fb5;
--ov_button_hover_color_dark: #146a8f; --ov_button_hover_color_dark: #5aa5c8;
--ov_button_text_color_dark: #ffffff; --ov_button_text_color_dark: #ffffff;
--ov_outline_button_color_dark: #c9e5f8; --ov_outline_button_color_dark: #9fd1ea;
--ov_outline_button_hover_color_dark: #2f6984; --ov_outline_button_hover_color_dark: #315d70;
--ov_outline_button_text_color_dark: #c9e5f8; --ov_outline_button_text_color_dark: #d8f0fb;
--ov_icon_color_dark: #fafafa; --ov_icon_color_dark: #fafafa;
--ov_light_icon_color_dark: #bababa; --ov_light_icon_color_dark: #bababa;
--ov_selected_icon_color_dark: #3393bd; --ov_selected_icon_color_dark: #9fd1ea;
--ov_disabled_icon_color_dark: #888888; --ov_disabled_icon_color_dark: #888888;
--ov_hover_color_dark: #667c86; --ov_hover_color_dark: #667c86;
--ov_hover_text_color_dark: #fafafa; --ov_hover_text_color_dark: #fafafa;
@ -53,4 +54,5 @@
--ov_dialog_control_border_color_dark: #e1e1e1; --ov_dialog_control_border_color_dark: #e1e1e1;
--ov_border_color_dark: #444444; --ov_border_color_dark: #444444;
--ov_shadow_dark: 0px 0px 10px #222222; --ov_shadow_dark: 0px 0px 10px #222222;
--ov_focus_ring_color_dark: #8fc8e5;
} }

View File

@ -27,20 +27,28 @@ div.ov_tree_view div.ov_tree_item_button_container
float: right; float: right;
} }
div.ov_tree_view div.ov_tree_item_button .ov_tree_view .ov_tree_item_button
{ {
color: var(--ov_light_icon_color); color: var(--ov_light_icon_color);
padding: 5px; padding: 13px;
float: left; float: left;
cursor: pointer; cursor: pointer;
display: block;
} }
div.ov_tree_view div.ov_tree_item_icon .ov_tree_view div.ov_tree_item_icon,
.ov_tree_view button.ov_tree_item_icon
{ {
padding: 5px; padding: 5px;
float: left; float: left;
} }
.ov_tree_view button.ov_tree_item_toggle
{
display: block;
cursor: pointer;
}
div.ov_tree_view div.ov_tree_item_name div.ov_tree_view div.ov_tree_item_name
{ {
padding: 4px 5px; padding: 4px 5px;

View File

@ -13,7 +13,7 @@ div.ov_color_circle
div.header div.header
{ {
overflow: auto; overflow: auto;
display: none; display: block;
} }
div.title div.title
@ -69,7 +69,7 @@ div.intro
text-align: center; text-align: center;
border: 2px dashed var(--ov_border_color); border: 2px dashed var(--ov_border_color);
overflow: auto; overflow: auto;
display: none; display: block;
} }
div.intro_content div.intro_content
@ -116,12 +116,16 @@ div.intro div.intro_file_formats a
color: var(--ov_outline_button_text_color); color: var(--ov_outline_button_text_color);
text-decoration: none; text-decoration: none;
font-size: 17px; font-size: 17px;
width: 50px; min-width: 56px;
min-height: 44px;
border-radius: 5px; border-radius: 5px;
padding: 4px 8px; padding: 4px 8px;
margin: 6px 4px; margin: 6px 4px;
border: 1px solid var(--ov_outline_button_color); border: 1px solid var(--ov_outline_button_color);
display: inline-block; display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
cursor: pointer; cursor: pointer;
} }
@ -212,19 +216,20 @@ div.ov_toolbar
user-select: none; user-select: none;
} }
div.ov_toolbar div.ov_toolbar_button .ov_toolbar .ov_toolbar_button
{ {
float: left; float: left;
cursor: pointer; cursor: pointer;
padding: 10px; padding: 13px;
display: block;
} }
div.ov_toolbar div.ov_toolbar_button.align_right .ov_toolbar .ov_toolbar_button.align_right
{ {
float: right; float: right;
} }
div.ov_toolbar div.ov_toolbar_button.selected .ov_toolbar .ov_toolbar_button.selected
{ {
background: var(--ov_toolbar_selected_color); background: var(--ov_toolbar_selected_color);
} }
@ -314,23 +319,27 @@ div.ov_bottom_floating_panel
background: var(--ov_background_color); background: var(--ov_background_color);
border-top: 1px solid var(--ov_border_color); border-top: 1px solid var(--ov_border_color);
width: 100%; width: 100%;
padding: 30px; padding: 20px 24px;
box-sizing: border-box; box-sizing: border-box;
position: absolute; position: absolute;
bottom: 0px; bottom: 0px;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 12px;
} }
div.ov_bottom_floating_panel div.ov_floating_panel_text div.ov_bottom_floating_panel div.ov_floating_panel_text
{ {
padding: 3px; padding: 3px 0px;
margin-bottom: 10px; flex: 1 1 260px;
float: left;
} }
div.ov_bottom_floating_panel div.ov_floating_panel_button .ov_bottom_floating_panel .ov_floating_panel_button
{ {
width: 120px; width: 120px;
float: right; flex: 0 0 auto;
margin-left: auto;
} }
div.ov_measure_panel div.ov_measure_panel
@ -362,7 +371,7 @@ div.title_right div.header_button:hover
color: var(--ov_button_color); color: var(--ov_button_color);
} }
div.ov_toolbar div.ov_toolbar_button:hover .ov_toolbar .ov_toolbar_button:hover
{ {
background: var(--ov_hover_color); background: var(--ov_hover_color);
} }
@ -392,6 +401,62 @@ div.intro_content
width: auto; width: auto;
} }
div.main_left_container.only_full_width,
div.main_right_container.only_full_width,
div.main_file_name.only_full_width
{
display: block;
}
div.main
{
position: relative;
}
div.main_file_name
{
margin: 8px 56px;
font-size: 14px;
}
div.main_left_container,
div.main_right_container
{
position: absolute;
top: 0px;
bottom: 0px;
z-index: 2;
overflow: visible;
}
div.main_left_container
{
left: 0px;
}
div.main_right_container
{
right: 0px;
}
div.main_navigator,
div.main_sidebar
{
margin: 0px;
background: var(--ov_background_color);
box-shadow: var(--ov_shadow);
}
div.main_splitter
{
display: none;
}
div.main_viewer
{
float: none;
}
div.main_viewer canvas div.main_viewer canvas
{ {
border: 0px; border: 0px;
@ -410,7 +475,7 @@ div.ov_progress
div.ov_bottom_floating_panel div.ov_bottom_floating_panel
{ {
padding: 10px; padding: 12px;
} }
} }

View File

@ -1,5 +1,5 @@
import { AddDiv, CreateDiv } from '../engine/viewer/domutils.js'; import { AddDiv, CreateDiv, AddButtonElement } from '../engine/viewer/domutils.js';
import { AddSvgIconElement, CreateInlineColorCircle, IsHoverEnabled } from './utils.js'; import { AddSvgIconElement, CreateInlineColorCircle, IsHoverEnabled, SetElementAccessibleName } from './utils.js';
let currentDialog = null; let currentDialog = null;
@ -164,10 +164,11 @@ export class ButtonDialog extends Dialog
{ {
function AddButton (button, buttonsDiv) function AddButton (button, buttonsDiv)
{ {
let buttonDiv = AddDiv (buttonsDiv, 'ov_button ov_dialog_button', button.name); let buttonDiv = AddButtonElement (buttonsDiv, 'ov_button ov_dialog_button', button.name);
if (button.subClass) { if (button.subClass) {
buttonDiv.classList.add (button.subClass); buttonDiv.classList.add (button.subClass);
} }
SetElementAccessibleName (buttonDiv, button.name);
buttonDiv.addEventListener ('click', () => { buttonDiv.addEventListener ('click', () => {
button.onClick (); button.onClick ();
}); });
@ -221,7 +222,8 @@ export class ListPopup extends PopupDialog
AddListItem (item, callbacks) AddListItem (item, callbacks)
{ {
let listItemDiv = AddDiv (this.listDiv, 'ov_popup_list_item'); let listItemDiv = AddButtonElement (this.listDiv, 'ov_popup_list_item');
SetElementAccessibleName (listItemDiv, item.name);
if (item.icon) { if (item.icon) {
AddSvgIconElement (listItemDiv, item.icon, 'left_inline'); AddSvgIconElement (listItemDiv, item.icon, 'left_inline');
} }

View File

@ -47,46 +47,42 @@ export function RegisterToolbarPlugin (plugin)
export function StartWebsite () export function StartWebsite ()
{ {
window.addEventListener ('load', () => { if (window.self !== window.top) {
if (window.self !== window.top) { let noEmbeddingDiv = AddDiv (document.body, 'noembed');
let noEmbeddingDiv = AddDiv (document.body, 'noembed'); AddDiv (noEmbeddingDiv, null, Loc ('Embedding Online 3D Viewer in an iframe is not supported.'));
AddDiv (noEmbeddingDiv, null, Loc ('Embedding Online 3D Viewer in an iframe is not supported.')); let link = AddDomElement (noEmbeddingDiv, 'a', null, Loc ('Open Online 3D Viewer'));
let link = AddDomElement (noEmbeddingDiv, 'a', null, Loc ('Open Online 3D Viewer')); link.target = '_blank';
link.target = '_blank'; link.href = window.self.location;
link.href = window.self.location; return;
return; }
}
document.getElementById ('intro_dragdrop_text').innerHTML = Loc ('Drag and drop 3D models here.'); document.getElementById ('intro_dragdrop_text').textContent = Loc ('Drag and drop 3D models here.');
document.getElementById ('intro_formats_title').innerHTML = Loc ('Check an example file:'); document.getElementById ('intro_formats_title').textContent = Loc ('Check an example file:');
let website = new Website ({ let website = new Website ({
headerDiv : document.getElementById ('header'), headerDiv : document.getElementById ('header'),
headerButtonsDiv : document.getElementById ('header_buttons'), headerButtonsDiv : document.getElementById ('header_buttons'),
toolbarDiv : document.getElementById ('toolbar'), toolbarDiv : document.getElementById ('toolbar'),
mainDiv : document.getElementById ('main'), mainDiv : document.getElementById ('main'),
introDiv : document.getElementById ('intro'), introDiv : document.getElementById ('intro'),
fileNameDiv : document.getElementById ('main_file_name'), fileNameDiv : document.getElementById ('main_file_name'),
leftContainerDiv : document.getElementById ('main_left_container'), leftContainerDiv : document.getElementById ('main_left_container'),
navigatorDiv : document.getElementById ('main_navigator'), navigatorDiv : document.getElementById ('main_navigator'),
navigatorSplitterDiv : document.getElementById ('main_navigator_splitter'), navigatorSplitterDiv : document.getElementById ('main_navigator_splitter'),
rightContainerDiv : document.getElementById ('main_right_container'), rightContainerDiv : document.getElementById ('main_right_container'),
sidebarDiv : document.getElementById ('main_sidebar'), sidebarDiv : document.getElementById ('main_sidebar'),
sidebarSplitterDiv : document.getElementById ('main_sidebar_splitter'), sidebarSplitterDiv : document.getElementById ('main_sidebar_splitter'),
viewerDiv : document.getElementById ('main_viewer'), viewerDiv : document.getElementById ('main_viewer'),
fileInput : document.getElementById ('open_file') fileInput : document.getElementById ('open_file')
});
website.Load ();
}); });
website.Load ();
} }
export function StartEmbed () export function StartEmbed ()
{ {
window.addEventListener ('load', () => { let embed = new Embed ({
let embed = new Embed ({ viewerDiv : document.getElementById ('embed_viewer'),
viewerDiv : document.getElementById ('embed_viewer'), websiteLinkDiv : document.getElementById ('website_link')
websiteLinkDiv : document.getElementById ('website_link')
});
embed.Load ();
}); });
embed.Load ();
} }

View File

@ -1,4 +1,5 @@
import { IsDefined } from '../engine/core/core.js'; import { IsDefined } from '../engine/core/core.js';
import { Loc } from '../engine/core/localization.js';
import { TreeViewButton, TreeViewButtonItem, TreeViewGroupButtonItem, TreeViewSingleItem } from './treeview.js'; import { TreeViewButton, TreeViewButtonItem, TreeViewGroupButtonItem, TreeViewSingleItem } from './treeview.js';
export const NavigatorItemRecurse = export const NavigatorItemRecurse =
@ -29,13 +30,13 @@ export class MeshItem extends TreeViewButtonItem
this.meshInstanceId = meshInstanceId; this.meshInstanceId = meshInstanceId;
this.visible = true; this.visible = true;
this.fitToWindowButton = new TreeViewButton ('fit'); this.fitToWindowButton = new TreeViewButton ('fit', Loc ('Fit mesh to window'));
this.fitToWindowButton.OnClick (() => { this.fitToWindowButton.OnClick (() => {
callbacks.onFitToWindow (this.meshInstanceId); callbacks.onFitToWindow (this.meshInstanceId);
}); });
this.AppendButton (this.fitToWindowButton); this.AppendButton (this.fitToWindowButton);
this.showHideButton = new TreeViewButton ('visible'); this.showHideButton = new TreeViewButton ('visible', Loc ('Show or hide mesh'));
this.showHideButton.OnClick (() => { this.showHideButton.OnClick (() => {
callbacks.onShowHide (this.meshInstanceId); callbacks.onShowHide (this.meshInstanceId);
}); });
@ -85,13 +86,13 @@ export class NodeItem extends TreeViewGroupButtonItem
this.callbacks = callbacks; this.callbacks = callbacks;
this.visible = true; this.visible = true;
this.fitToWindowButton = new TreeViewButton ('fit'); this.fitToWindowButton = new TreeViewButton ('fit', Loc ('Fit node to window'));
this.fitToWindowButton.OnClick (() => { this.fitToWindowButton.OnClick (() => {
this.callbacks.onFitToWindow (nodeId); this.callbacks.onFitToWindow (nodeId);
}); });
this.AppendButton (this.fitToWindowButton); this.AppendButton (this.fitToWindowButton);
this.showHideButton = new TreeViewButton ('visible'); this.showHideButton = new TreeViewButton ('visible', Loc ('Show or hide node'));
this.showHideButton.OnClick (() => { this.showHideButton.OnClick (() => {
this.callbacks.onShowHide (nodeId); this.callbacks.onShowHide (nodeId);
}); });

View File

@ -1,4 +1,4 @@
import { AddDiv } from '../engine/viewer/domutils.js'; import { AddDiv, AddButtonElement } from '../engine/viewer/domutils.js';
import { Panel } from './panelset.js'; import { Panel } from './panelset.js';
import { TreeView } from './treeview.js'; import { TreeView } from './treeview.js';
import { AddSvgIconElement } from './utils.js'; import { AddSvgIconElement } from './utils.js';
@ -11,7 +11,7 @@ export class NavigatorPopupButton
this.callbacks = null; this.callbacks = null;
this.popup = null; this.popup = null;
this.button = AddDiv (this.parentDiv, 'ov_panel_button'); this.button = AddButtonElement (this.parentDiv, 'ov_panel_button');
this.buttonText = AddDiv (this.button, 'ov_panel_button_text'); this.buttonText = AddDiv (this.button, 'ov_panel_button_text');
AddSvgIconElement (this.button, 'arrow_right', 'ov_panel_button_icon'); AddSvgIconElement (this.button, 'arrow_right', 'ov_panel_button_icon');
this.button.addEventListener ('click', () => { this.button.addEventListener ('click', () => {

View File

@ -1,5 +1,5 @@
import { AddDiv, ShowDomElement, IsDomElementVisible, SetDomElementWidth, SetDomElementHeight } from '../engine/viewer/domutils.js'; import { AddDiv, ShowDomElement, IsDomElementVisible, SetDomElementWidth, SetDomElementHeight } from '../engine/viewer/domutils.js';
import { AddSvgIconElement, SetSvgIconImageElement } from './utils.js'; import { AddSvgIconButtonElement, SetSvgIconImageElement } from './utils.js';
export class Panel export class Panel
{ {
@ -74,9 +74,7 @@ export class PanelSet
AddPanel (panel) AddPanel (panel)
{ {
this.panels.push (panel); this.panels.push (panel);
let button = AddSvgIconElement (this.menuDiv, panel.GetIcon (), 'ov_panel_set_menu_button'); let button = AddSvgIconButtonElement (this.menuDiv, panel.GetIcon (), 'ov_panel_set_menu_button', panel.GetName ());
button.setAttribute ('alt', panel.GetName ());
button.setAttribute ('title', panel.GetName ());
this.panelButtons.push (button); this.panelButtons.push (button);
button.addEventListener ('click', () => { button.addEventListener ('click', () => {
if (panel === this.GetVisiblePanel ()) { if (panel === this.GetVisiblePanel ()) {
@ -110,6 +108,7 @@ export class PanelSet
} else { } else {
for (let panelButton of this.panelButtons) { for (let panelButton of this.panelButtons) {
panelButton.classList.remove ('selected'); panelButton.classList.remove ('selected');
panelButton.setAttribute ('aria-pressed', 'false');
} }
for (let panel of this.panels) { for (let panel of this.panels) {
panel.Show (false); panel.Show (false);
@ -133,9 +132,11 @@ export class PanelSet
for (let otherPanelButton of this.panelButtons) { for (let otherPanelButton of this.panelButtons) {
if (otherPanelButton !== panelButton) { if (otherPanelButton !== panelButton) {
otherPanelButton.classList.remove ('selected'); otherPanelButton.classList.remove ('selected');
otherPanelButton.setAttribute ('aria-pressed', 'false');
} }
} }
panelButton.classList.add ('selected'); panelButton.classList.add ('selected');
panelButton.setAttribute ('aria-pressed', 'true');
for (let otherPanel of this.panels) { for (let otherPanel of this.panels) {
if (otherPanel !== panel) { if (otherPanel !== panel) {

View File

@ -1,10 +1,11 @@
import { FileSource } from '../engine/io/fileutils.js'; import { FileSource } from '../engine/io/fileutils.js';
import { AddDiv, AddDomElement } from '../engine/viewer/domutils.js'; import { AddDiv, AddDomElement, AddButtonElement } from '../engine/viewer/domutils.js';
import { AddCheckbox } from '../website/utils.js'; import { AddCheckbox } from '../website/utils.js';
import { CreateUrlBuilder } from '../engine/parameters/parameterlist.js'; import { CreateUrlBuilder } from '../engine/parameters/parameterlist.js';
import { ShowMessageDialog } from './dialogs.js'; import { ShowMessageDialog } from './dialogs.js';
import { ButtonDialog } from './dialog.js'; import { ButtonDialog } from './dialog.js';
import { CopyToClipboard } from './utils.js'; import { CopyToClipboard } from './utils.js';
import { SetElementAccessibleName } from './utils.js';
import { HandleEvent } from './eventhandler.js'; import { HandleEvent } from './eventhandler.js';
import { Loc } from '../engine/core/localization.js'; import { Loc } from '../engine/core/localization.js';
@ -26,12 +27,15 @@ export function ShowSharingDialog (fileList, settings, viewer)
let input = AddDomElement (container, 'input', null); let input = AddDomElement (container, 'input', null);
input.setAttribute ('type', 'text'); input.setAttribute ('type', 'text');
input.readOnly = true; input.readOnly = true;
let button = AddDiv (container, 'ov_button outline ov_dialog_copyable_input_button', copyText); let button = AddButtonElement (container, 'ov_button outline ov_dialog_copyable_input_button', copyText);
SetElementAccessibleName (button, copyText);
button.addEventListener ('click', () => { button.addEventListener ('click', () => {
CopyToClipboard (getText ()); CopyToClipboard (getText ());
button.innerHTML = copiedText; button.innerHTML = copiedText;
SetElementAccessibleName (button, copiedText);
setTimeout (() => { setTimeout (() => {
button.innerHTML = copyText; button.innerHTML = copyText;
SetElementAccessibleName (button, copyText);
}, 2000); }, 2000);
}); });
return input; return input;

View File

@ -3,9 +3,9 @@ import { SubCoord3D } from '../engine/geometry/coord3d.js';
import { GetBoundingBox, IsTwoManifold } from '../engine/model/modelutils.js'; import { GetBoundingBox, IsTwoManifold } from '../engine/model/modelutils.js';
import { CalculateVolume, CalculateSurfaceArea } from '../engine/model/quantities.js'; import { CalculateVolume, CalculateSurfaceArea } from '../engine/model/quantities.js';
import { Property, PropertyToString, PropertyType } from '../engine/model/property.js'; import { Property, PropertyToString, PropertyType } from '../engine/model/property.js';
import { AddDiv, AddDomElement, ClearDomElement } from '../engine/viewer/domutils.js'; import { AddDiv, AddDomElement, ClearDomElement, AddButtonElement } from '../engine/viewer/domutils.js';
import { SidebarPanel } from './sidebarpanel.js'; import { SidebarPanel } from './sidebarpanel.js';
import { CreateInlineColorCircle } from './utils.js'; import { CreateInlineColorCircle, SetElementAccessibleName } from './utils.js';
import { GetFileName, IsUrl } from '../engine/io/fileutils.js'; import { GetFileName, IsUrl } from '../engine/io/fileutils.js';
import { MaterialSource, MaterialType } from '../engine/model/material.js'; import { MaterialSource, MaterialType } from '../engine/model/material.js';
import { RGBColorToHexString } from '../engine/model/color.js'; import { RGBColorToHexString } from '../engine/model/color.js';
@ -170,7 +170,8 @@ export class SidebarDetailsPanel extends SidebarPanel
let valueColumn = AddDiv (row, 'ov_property_table_cell ov_property_table_value'); let valueColumn = AddDiv (row, 'ov_property_table_cell ov_property_table_value');
nameColumn.setAttribute ('title', name); nameColumn.setAttribute ('title', name);
let calculateButton = AddDiv (valueColumn, 'ov_property_table_button', Loc ('Calculate...')); let calculateButton = AddButtonElement (valueColumn, 'ov_property_table_button', Loc ('Calculate...'));
SetElementAccessibleName (calculateButton, name);
calculateButton.addEventListener ('click', () => { calculateButton.addEventListener ('click', () => {
ClearDomElement (valueColumn); ClearDomElement (valueColumn);
valueColumn.innerHTML = Loc ('Please wait...'); valueColumn.innerHTML = Loc ('Please wait...');

View File

@ -1,6 +1,6 @@
import { RGBColor, RGBColorToHexString, RGBAColor, RGBAColorToHexString, ColorComponentFromFloat } from '../engine/model/color.js'; import { RGBColor, RGBColorToHexString, RGBAColor, RGBAColorToHexString, ColorComponentFromFloat } from '../engine/model/color.js';
import { AddDiv, AddDomElement, ShowDomElement, SetDomElementOuterHeight } from '../engine/viewer/domutils.js'; import { AddDiv, AddDomElement, ShowDomElement, SetDomElementOuterHeight, AddButtonElement } from '../engine/viewer/domutils.js';
import { AddRangeSlider, AddToggle, AddCheckbox } from '../website/utils.js'; import { AddRangeSlider, AddToggle, AddCheckbox, SetElementAccessibleName, MakeElementButtonLike } from '../website/utils.js';
import { CalculatePopupPositionToElementTopLeft } from './dialogs.js'; import { CalculatePopupPositionToElementTopLeft } from './dialogs.js';
import { PopupDialog } from './dialog.js'; import { PopupDialog } from './dialog.js';
import { Settings } from './settings.js'; import { Settings } from './settings.js';
@ -107,7 +107,7 @@ class EnvironmentMapPopup extends PopupDialog
if (isSelected) { if (isSelected) {
envMapImage.element.classList.add ('selected'); envMapImage.element.classList.add ('selected');
} }
envMapImage.element.addEventListener ('click', () => { let onSelect = () => {
for (let otherImage of envMapImages) { for (let otherImage of envMapImages) {
otherImage.element.classList.remove ('selected'); otherImage.element.classList.remove ('selected');
} }
@ -120,7 +120,9 @@ class EnvironmentMapPopup extends PopupDialog
settings.environmentMapName = envMapImage.name; settings.environmentMapName = envMapImage.name;
} }
callbacks.onEnvironmentMapChanged (); callbacks.onEnvironmentMapChanged ();
}); };
MakeElementButtonLike (envMapImage.element, envMapImage.name, onSelect);
envMapImage.element.addEventListener ('click', onSelect);
} }
} else if (shadingType === ShadingType.Physical) { } else if (shadingType === ShadingType.Physical) {
let isPerspective = (callbacks.getProjectionMode () === ProjectionMode.Perspective); let isPerspective = (callbacks.getProjectionMode () === ProjectionMode.Perspective);
@ -138,14 +140,16 @@ class EnvironmentMapPopup extends PopupDialog
if (envMapImage.name === settings.environmentMapName) { if (envMapImage.name === settings.environmentMapName) {
envMapImage.element.classList.add ('selected'); envMapImage.element.classList.add ('selected');
} }
envMapImage.element.addEventListener ('click', () => { let onSelect = () => {
for (let otherImage of envMapImages) { for (let otherImage of envMapImages) {
otherImage.element.classList.remove ('selected'); otherImage.element.classList.remove ('selected');
} }
envMapImage.element.classList.add ('selected'); envMapImage.element.classList.add ('selected');
settings.environmentMapName = envMapImage.name; settings.environmentMapName = envMapImage.name;
callbacks.onEnvironmentMapChanged (); callbacks.onEnvironmentMapChanged ();
}); };
MakeElementButtonLike (envMapImage.element, envMapImage.name, onSelect);
envMapImage.element.addEventListener ('click', onSelect);
} }
} }
@ -229,7 +233,8 @@ class SettingsModelDisplaySection extends SettingsSection
}); });
this.environmentMapPhongDiv = AddDiv (this.contentDiv, 'ov_sidebar_parameter'); this.environmentMapPhongDiv = AddDiv (this.contentDiv, 'ov_sidebar_parameter');
this.environmentMapPhongInput = AddDiv (this.environmentMapPhongDiv, 'ov_sidebar_image_picker'); this.environmentMapPhongInput = AddButtonElement (this.environmentMapPhongDiv, 'ov_sidebar_image_picker');
SetElementAccessibleName (this.environmentMapPhongInput, Loc ('Background Image'));
AddDiv (this.environmentMapPhongDiv, null, Loc ('Background Image')); AddDiv (this.environmentMapPhongDiv, null, Loc ('Background Image'));
this.environmentMapPhongInput.addEventListener ('click', () => { this.environmentMapPhongInput.addEventListener ('click', () => {
this.environmentMapPopup = new EnvironmentMapPopup (); this.environmentMapPopup = new EnvironmentMapPopup ();
@ -245,7 +250,8 @@ class SettingsModelDisplaySection extends SettingsSection
}); });
this.environmentMapPbrDiv = AddDiv (this.contentDiv, 'ov_sidebar_parameter'); this.environmentMapPbrDiv = AddDiv (this.contentDiv, 'ov_sidebar_parameter');
this.environmentMapPbrInput = AddDiv (this.environmentMapPbrDiv, 'ov_sidebar_image_picker'); this.environmentMapPbrInput = AddButtonElement (this.environmentMapPbrDiv, 'ov_sidebar_image_picker');
SetElementAccessibleName (this.environmentMapPbrInput, Loc ('Environment'));
AddDiv (this.environmentMapPbrDiv, null, Loc ('Environment')); AddDiv (this.environmentMapPbrDiv, null, Loc ('Environment'));
this.environmentMapPbrInput.addEventListener ('click', () => { this.environmentMapPbrInput.addEventListener ('click', () => {
this.environmentMapPopup = new EnvironmentMapPopup (); this.environmentMapPopup = new EnvironmentMapPopup ();
@ -263,7 +269,7 @@ class SettingsModelDisplaySection extends SettingsSection
this.UpdateEnvironmentMap (); this.UpdateEnvironmentMap ();
let edgeParameterDiv = AddDiv (this.contentDiv, 'ov_sidebar_parameter'); let edgeParameterDiv = AddDiv (this.contentDiv, 'ov_sidebar_parameter');
this.edgeDisplayToggle = AddToggle (edgeParameterDiv, 'ov_sidebar_parameter_toggle'); this.edgeDisplayToggle = AddToggle (edgeParameterDiv, 'ov_sidebar_parameter_toggle', Loc ('Show Edges'));
AddDiv (edgeParameterDiv, 'ov_sidebar_parameter_text', Loc ('Show Edges')); AddDiv (edgeParameterDiv, 'ov_sidebar_parameter_text', Loc ('Show Edges'));
this.edgeSettingsDiv = AddDiv (this.contentDiv, 'ov_sidebar_settings_padded'); this.edgeSettingsDiv = AddDiv (this.contentDiv, 'ov_sidebar_settings_padded');
@ -458,7 +464,8 @@ export class SidebarSettingsPanel extends SidebarPanel
this.modelDisplaySection = new SettingsModelDisplaySection (this.sectionsDiv, this.settings); this.modelDisplaySection = new SettingsModelDisplaySection (this.sectionsDiv, this.settings);
this.importParametersSection = new SettingsImportParametersSection (this.sectionsDiv, this.settings); this.importParametersSection = new SettingsImportParametersSection (this.sectionsDiv, this.settings);
this.resetToDefaultsButton = AddDiv (this.contentDiv, 'ov_button ov_panel_button outline', 'Reset to Default'); this.resetToDefaultsButton = AddButtonElement (this.contentDiv, 'ov_button ov_panel_button outline', 'Reset to Default');
SetElementAccessibleName (this.resetToDefaultsButton, Loc ('Reset to Default'));
this.resetToDefaultsButton.addEventListener ('click', () => { this.resetToDefaultsButton.addEventListener ('click', () => {
this.ResetToDefaults (); this.ResetToDefaults ();
}); });

View File

@ -1,8 +1,8 @@
import { AddDiv } from '../engine/viewer/domutils.js'; import { AddDiv, AddButtonElement } from '../engine/viewer/domutils.js';
import { ThreeModelLoader } from '../engine/threejs/threemodelloader.js'; import { ThreeModelLoader } from '../engine/threejs/threemodelloader.js';
import { ShowMessageDialog } from './dialogs.js'; import { ShowMessageDialog } from './dialogs.js';
import { ButtonDialog, ProgressDialog } from './dialog.js'; import { ButtonDialog, ProgressDialog } from './dialog.js';
import { AddSvgIconElement } from './utils.js'; import { AddSvgIconElement, SetElementAccessibleName } from './utils.js';
import { ImportErrorCode } from '../engine/import/importer.js'; import { ImportErrorCode } from '../engine/import/importer.js';
import { Loc } from '../engine/core/localization.js'; import { Loc } from '../engine/core/localization.js';
@ -124,7 +124,8 @@ export class ThreeModelLoaderUI
for (let i = 0; i < fileNames.length; i++) { for (let i = 0; i < fileNames.length; i++) {
let fileName = fileNames[i]; let fileName = fileNames[i];
let fileLink = AddDiv (fileList, 'ov_dialog_file_link'); let fileLink = AddButtonElement (fileList, 'ov_dialog_file_link');
SetElementAccessibleName (fileLink, fileName);
AddSvgIconElement (fileLink, 'meshes', 'ov_file_link_img'); AddSvgIconElement (fileLink, 'meshes', 'ov_file_link_img');
AddDiv (fileLink, 'ov_dialog_file_link_text', fileName); AddDiv (fileLink, 'ov_dialog_file_link_text', fileName);
fileLink.addEventListener ('click', () => { fileLink.addEventListener ('click', () => {

View File

@ -1,5 +1,5 @@
import { AddDiv, CreateDiv } from '../engine/viewer/domutils.js'; import { AddDiv, CreateButtonElement } from '../engine/viewer/domutils.js';
import { AddSvgIconElement, InstallTooltip } from './utils.js'; import { AddSvgIconElement, InstallTooltip, SetElementAccessibleName } from './utils.js';
export class ToolbarButton export class ToolbarButton
{ {
@ -9,13 +9,13 @@ export class ToolbarButton
this.imageTitle = imageTitle; this.imageTitle = imageTitle;
this.selected = false; this.selected = false;
this.buttonDiv = CreateDiv ('ov_toolbar_button'); this.buttonDiv = CreateButtonElement ('ov_toolbar_button');
this.buttonImg = AddSvgIconElement (this.buttonDiv, this.image); this.buttonImg = AddSvgIconElement (this.buttonDiv, this.image);
if (onClick !== null) { if (onClick !== null) {
this.buttonDiv.addEventListener ('click', onClick); this.buttonDiv.addEventListener ('click', onClick);
} }
this.buttonDiv.setAttribute ('alt', this.imageTitle); SetElementAccessibleName (this.buttonDiv, this.imageTitle);
InstallTooltip (this.buttonDiv, this.imageTitle); InstallTooltip (this.buttonDiv, this.imageTitle);
} }
@ -57,6 +57,7 @@ export class ToolbarButton
} else { } else {
this.buttonDiv.classList.remove ('selected'); this.buttonDiv.classList.remove ('selected');
} }
this.buttonDiv.setAttribute ('aria-pressed', this.selected ? 'true' : 'false');
} }
} }

View File

@ -1,6 +1,6 @@
import { RGBColor, RGBColorToHexString } from '../engine/model/color.js'; import { RGBColor, RGBColorToHexString } from '../engine/model/color.js';
import { CreateObjectUrl } from '../engine/io/bufferutils.js'; import { CreateObjectUrl } from '../engine/io/bufferutils.js';
import { AddDiv, CreateDiv, AddDomElement } from '../engine/viewer/domutils.js'; import { AddDiv, CreateDiv, AddDomElement, AddButtonElement, CreateButtonElement } from '../engine/viewer/domutils.js';
import { Loc } from '../engine/core/localization.js'; import { Loc } from '../engine/core/localization.js';
import { Theme } from './settings.js'; import { Theme } from './settings.js';
@ -128,6 +128,7 @@ export function DownloadArrayBufferAsFile (arrayBuffer, fileName)
export function CreateSvgIconElement (iconName, className) export function CreateSvgIconElement (iconName, className)
{ {
let iconDiv = CreateDiv ('ov_svg_icon'); let iconDiv = CreateDiv ('ov_svg_icon');
iconDiv.setAttribute ('aria-hidden', 'true');
if (className) { if (className) {
iconDiv.classList.add (className); iconDiv.classList.add (className);
} }
@ -135,6 +136,29 @@ export function CreateSvgIconElement (iconName, className)
return iconDiv; return iconDiv;
} }
export function SetElementAccessibleName (element, text)
{
element.setAttribute ('aria-label', text);
element.setAttribute ('title', text);
}
export function CreateSvgIconButtonElement (iconName, className, accessibleName)
{
let button = CreateButtonElement (className);
if (accessibleName) {
SetElementAccessibleName (button, accessibleName);
}
AddSvgIconElement (button, iconName);
return button;
}
export function AddSvgIconButtonElement (parentElement, iconName, className, accessibleName)
{
let button = CreateSvgIconButtonElement (iconName, className, accessibleName);
parentElement.appendChild (button);
return button;
}
export function AddSvgIconElement (parentElement, iconName, className) export function AddSvgIconElement (parentElement, iconName, className)
{ {
let iconDiv = CreateSvgIconElement (iconName, className); let iconDiv = CreateSvgIconElement (iconName, className);
@ -148,6 +172,21 @@ export function SetSvgIconImageElement (iconElement, iconName)
iconDiv.className = 'icon icon-' + iconName; iconDiv.className = 'icon icon-' + iconName;
} }
export function MakeElementButtonLike (element, accessibleName, onActivate)
{
element.setAttribute ('role', 'button');
element.setAttribute ('tabindex', '0');
if (accessibleName) {
SetElementAccessibleName (element, accessibleName);
}
element.addEventListener ('keydown', (ev) => {
if (ev.key === 'Enter' || ev.key === ' ') {
ev.preventDefault ();
onActivate (ev);
}
});
}
export function CreateInlineColorCircle (color) export function CreateInlineColorCircle (color)
{ {
let hexString = '#' + RGBColorToHexString (color); let hexString = '#' + RGBColorToHexString (color);
@ -158,6 +197,7 @@ export function CreateInlineColorCircle (color)
); );
let darkerColorHexString = '#' + RGBColorToHexString (darkerColor); let darkerColorHexString = '#' + RGBColorToHexString (darkerColor);
let circleDiv = CreateDiv ('ov_color_circle'); let circleDiv = CreateDiv ('ov_color_circle');
circleDiv.setAttribute ('aria-hidden', 'true');
circleDiv.style.background = hexString; circleDiv.style.background = hexString;
circleDiv.style.border = '1px solid ' + darkerColorHexString; circleDiv.style.border = '1px solid ' + darkerColorHexString;
return circleDiv; return circleDiv;
@ -320,7 +360,7 @@ export function AddSelect (parentElement, options, selectedIndex, onChange)
return select; return select;
} }
export function AddToggle (parentElement, className) export function AddToggle (parentElement, className, accessibleName)
{ {
function UpdateStatus (toggle, status) function UpdateStatus (toggle, status)
{ {
@ -329,6 +369,7 @@ export function AddToggle (parentElement, className)
} else { } else {
toggle.classList.remove ('on'); toggle.classList.remove ('on');
} }
toggle.setAttribute ('aria-pressed', status ? 'true' : 'false');
} }
let status = false; let status = false;
@ -338,7 +379,10 @@ export function AddToggle (parentElement, className)
if (className) { if (className) {
toggleClassName += ' ' + className; toggleClassName += ' ' + className;
} }
let toggle = AddDiv (parentElement, toggleClassName); let toggle = AddButtonElement (parentElement, toggleClassName);
if (accessibleName) {
SetElementAccessibleName (toggle, accessibleName);
}
AddDiv (toggle, 'ov_toggle_slider'); AddDiv (toggle, 'ov_toggle_slider');
toggle.addEventListener ('click', () => { toggle.addEventListener ('click', () => {

View File

@ -1,9 +1,10 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="en">
<head> <head>
<meta http-equiv="content-type" content="text/html;charset=utf-8"> <meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Open, inspect, and share 3D models directly in your browser with Online 3D Viewer.">
<link rel="icon" type="image/png" href="assets/images/3dviewer_net_favicon.ico"> <link rel="icon" type="image/png" href="assets/images/3dviewer_net_favicon.ico">
<link rel="canonical" href="https://3dviewer.net"> <link rel="canonical" href="https://3dviewer.net">
@ -14,25 +15,29 @@
<!-- website start --> <!-- website start -->
<link rel="stylesheet" type="text/css" href="../build/website_dev/o3dv.website.min.css"> <link rel="stylesheet" type="text/css" href="../build/website_dev/o3dv.website.min.css">
<script type="text/javascript" src="../build/website_dev/o3dv.website.min.js"></script> <script type="text/javascript" src="../build/website_dev/o3dv.website.min.js" defer></script>
<!-- website end --> <!-- website end -->
<!-- plugins start --> <!-- plugins start -->
<!-- plugins end --> <!-- plugins end -->
<!-- website analytics start --> <!-- website analytics start -->
<script type="text/javascript"> <script type="text/javascript">
OV.SetWebsiteEventHandler ((eventName, eventLabel, eventParams) => { document.addEventListener ('DOMContentLoaded', () => {
console.log ({ OV.SetWebsiteEventHandler ((eventName, eventLabel, eventParams) => {
eventName : eventName, console.log ({
eventLabel : eventLabel, eventName : eventName,
eventParams : eventParams eventLabel : eventLabel,
eventParams : eventParams
});
}); });
}); });
</script> </script>
<!-- website analytics end --> <!-- website analytics end -->
<script type="text/javascript"> <script type="text/javascript">
OV.StartWebsite (); document.addEventListener ('DOMContentLoaded', () => {
OV.StartWebsite ();
});
</script> </script>
</head> </head>
@ -42,7 +47,7 @@
<div class="header" id="header"> <div class="header" id="header">
<div class="title"> <div class="title">
<div class="title_left"> <div class="title_left">
<a href="index.html"> <a href="index.html" aria-label="Online 3D Viewer home">
<svg class="logo_image"><use href="assets/images/3dviewer_net_logo_text.svg#logo"></use></svg> <svg class="logo_image"><use href="assets/images/3dviewer_net_logo_text.svg#logo"></use></svg>
</a> </a>
</div> </div>
@ -66,10 +71,10 @@
<div class="intro_content" id="intro_content"> <div class="intro_content" id="intro_content">
<div class="intro_logo"> <div class="intro_logo">
<svg class="intro_logo"><use href="assets/images/3dviewer_net_logo_text.svg#logo"></use></svg> <svg class="intro_logo"><use href="assets/images/3dviewer_net_logo_text.svg#logo"></use></svg>
<div class="intro_dragdrop_text" id="intro_dragdrop_text"></div> <div class="intro_dragdrop_text" id="intro_dragdrop_text">Drag and drop 3D models here.</div>
</div> </div>
<div class="intro_formats"> <div class="intro_formats">
<div class="intro_formats_title" id="intro_formats_title"></div> <div class="intro_formats_title" id="intro_formats_title">Check an example file:</div>
<div class="intro_file_formats"> <div class="intro_file_formats">
<a href="#model=assets/models/RhinoLogo.3dm">3dm</a> <a href="#model=assets/models/RhinoLogo.3dm">3dm</a>
<a href="#model=assets/models/cubes.3ds,assets/models/texture.png">3ds</a> <a href="#model=assets/models/cubes.3ds,assets/models/texture.png">3ds</a>