pageflow 17.0.5 → 17.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +342 -41
- data/README.md +3 -5
- data/admins/pageflow/accounts.rb +22 -20
- data/admins/pageflow/entry.rb +76 -26
- data/admins/pageflow/entry_templates.rb +10 -9
- data/admins/pageflow/folder.rb +1 -1
- data/admins/pageflow/membership.rb +15 -15
- data/admins/pageflow/revisions.rb +5 -5
- data/admins/pageflow/site_root_entry.rb +64 -0
- data/admins/pageflow/sites.rb +12 -3
- data/admins/pageflow/translations.rb +75 -0
- data/admins/pageflow/user.rb +21 -35
- data/app/assets/images/pageflow/admin/icons/comment.svg +1 -0
- data/app/assets/javascripts/pageflow/admin/entries.js +30 -5
- data/app/assets/javascripts/pageflow/dist/ui.js +4228 -900
- data/app/assets/javascripts/pageflow/editor/vendor.js +0 -1
- data/app/assets/javascripts/pageflow/ui.js +0 -1
- data/app/assets/stylesheets/pageflow/admin/entries/index_table.scss +2 -1
- data/app/assets/stylesheets/pageflow/admin/entry_comments_indicator.scss +45 -0
- data/app/assets/stylesheets/pageflow/admin/permalink_input.scss +8 -0
- data/app/assets/stylesheets/pageflow/admin.scss +1 -0
- data/app/assets/stylesheets/pageflow/animations/spin.scss +9 -0
- data/app/assets/stylesheets/pageflow/animations.scss +1 -0
- data/app/assets/stylesheets/pageflow/editor/background_positioning.scss +27 -5
- data/app/assets/stylesheets/pageflow/editor/base.scss +11 -1
- data/app/assets/stylesheets/pageflow/editor/drop_down_button.scss +20 -2
- data/app/assets/stylesheets/pageflow/editor/edit_configuration_view.scss +5 -0
- data/app/assets/stylesheets/pageflow/editor/file_folders.scss +127 -0
- data/app/assets/stylesheets/pageflow/editor/file_meta_data.scss +5 -2
- data/app/assets/stylesheets/pageflow/editor/file_meta_data_overlay.scss +45 -0
- data/app/assets/stylesheets/pageflow/editor/file_preview.scss +138 -0
- data/app/assets/stylesheets/pageflow/editor/file_references.scss +68 -0
- data/app/assets/stylesheets/pageflow/editor/file_settings_dialog.scss +1 -1
- data/app/assets/stylesheets/pageflow/editor/file_stage_icons.scss +17 -0
- data/app/assets/stylesheets/pageflow/editor/file_stages.scss +25 -32
- data/app/assets/stylesheets/pageflow/editor/file_thumbnails.scss +31 -48
- data/app/assets/stylesheets/pageflow/editor/file_type_pills.scss +73 -0
- data/app/assets/stylesheets/pageflow/editor/files.scss +242 -50
- data/app/assets/stylesheets/pageflow/editor/filtered_files.scss +161 -9
- data/app/assets/stylesheets/pageflow/editor/folder_breadcrumb.scss +40 -0
- data/app/assets/stylesheets/pageflow/editor/info_box.scss +23 -3
- data/app/assets/stylesheets/pageflow/editor/inputs/edit_defaults_button.scss +15 -0
- data/app/assets/stylesheets/pageflow/editor/inputs.scss +1 -0
- data/app/assets/stylesheets/pageflow/editor/list.scss +89 -37
- data/app/assets/stylesheets/pageflow/editor/list_search_field.scss +52 -0
- data/app/assets/stylesheets/pageflow/editor/manage_files.scss +9 -0
- data/app/assets/stylesheets/pageflow/editor/menu.scss +14 -0
- data/app/assets/stylesheets/pageflow/editor/move_to_folder_dialog.scss +83 -0
- data/app/assets/stylesheets/pageflow/editor/outline.scss +0 -13
- data/app/assets/stylesheets/pageflow/editor/sortable.scss +12 -0
- data/app/assets/stylesheets/pageflow/mixins/buttons.scss +4 -4
- data/app/assets/stylesheets/pageflow/mixins/pageflow.scss +9 -5
- data/app/assets/stylesheets/pageflow/ui/color_picker.scss +230 -0
- data/app/assets/stylesheets/pageflow/ui/forms.scss +38 -4
- data/app/assets/stylesheets/pageflow/ui/functions.scss +12 -2
- data/app/assets/stylesheets/pageflow/ui/input/color_input.scss +9 -8
- data/app/assets/stylesheets/pageflow/ui/input/file_name_input.scss +37 -0
- data/app/assets/stylesheets/pageflow/ui/input/text_area_input.scss +15 -0
- data/app/assets/stylesheets/pageflow/ui/overrides.scss +8 -0
- data/app/assets/stylesheets/pageflow/ui/properties.scss +34 -0
- data/app/assets/stylesheets/pageflow/ui/tabs_view.scss +8 -3
- data/app/assets/stylesheets/pageflow/ui.scss +4 -1
- data/app/controllers/concerns/pageflow/controller_delegation.rb +1 -1
- data/app/controllers/concerns/pageflow/edit_locking.rb +17 -10
- data/app/controllers/concerns/pageflow/entry_password_protection.rb +1 -0
- data/app/controllers/concerns/pageflow/public_https_mode.rb +2 -1
- data/app/controllers/concerns/pageflow/quota_verification.rb +5 -4
- data/app/controllers/pageflow/admin/initial_passwords_controller.rb +1 -0
- data/app/controllers/pageflow/application_controller.rb +19 -19
- data/app/controllers/pageflow/chapters_controller.rb +3 -2
- data/app/controllers/pageflow/edit_locks_controller.rb +3 -2
- data/app/controllers/pageflow/editor/encoding_confirmations_controller.rb +1 -0
- data/app/controllers/pageflow/editor/entries_controller.rb +3 -1
- data/app/controllers/pageflow/editor/entry_publications_controller.rb +1 -0
- data/app/controllers/pageflow/editor/file_folders_controller.rb +80 -0
- data/app/controllers/pageflow/editor/file_import_controller.rb +4 -3
- data/app/controllers/pageflow/editor/files_controller.rb +20 -10
- data/app/controllers/pageflow/editor/oembeds_controller.rb +21 -0
- data/app/controllers/pageflow/editor/widgets_controller.rb +1 -0
- data/app/controllers/pageflow/entries_controller.rb +85 -21
- data/app/controllers/pageflow/feeds_controller.rb +1 -1
- data/app/controllers/pageflow/files_controller.rb +1 -0
- data/app/controllers/pageflow/pages_controller.rb +4 -2
- data/app/controllers/pageflow/review/comment_thread_reads_controller.rb +30 -0
- data/app/controllers/pageflow/review/comment_threads_controller.rb +60 -0
- data/app/controllers/pageflow/review/comments_controller.rb +53 -0
- data/app/controllers/pageflow/revisions_controller.rb +5 -1
- data/app/controllers/pageflow/sitemaps_controller.rb +1 -1
- data/app/controllers/pageflow/storylines_controller.rb +1 -0
- data/app/helpers/pageflow/admin/cutoff_modes_helper.rb +12 -0
- data/app/helpers/pageflow/admin/entries_helper.rb +41 -1
- data/app/helpers/pageflow/admin/entry_translations_helper.rb +18 -0
- data/app/helpers/pageflow/admin/features_helper.rb +1 -1
- data/app/helpers/pageflow/admin/form_helper.rb +3 -3
- data/app/helpers/pageflow/admin/locales_helper.rb +10 -2
- data/app/helpers/pageflow/admin/memberships_helper.rb +32 -32
- data/app/helpers/pageflow/admin/revisions_helper.rb +1 -1
- data/app/helpers/pageflow/admin/sites_helper.rb +22 -0
- data/app/helpers/pageflow/admin/users_helper.rb +7 -5
- data/app/helpers/pageflow/admin/widgets_helper.rb +5 -5
- data/app/helpers/pageflow/asset_urls_helper.rb +1 -1
- data/app/helpers/pageflow/audio_files_helper.rb +5 -2
- data/app/helpers/pageflow/background_image_helper.rb +7 -6
- data/app/helpers/pageflow/common_entry_seed_helper.rb +7 -7
- data/app/helpers/pageflow/editor/config_helper.rb +3 -1
- data/app/helpers/pageflow/editor/files_helper.rb +1 -1
- data/app/helpers/pageflow/embed_code_helper.rb +3 -3
- data/app/helpers/pageflow/entries_helper.rb +52 -46
- data/app/helpers/pageflow/entry_json_seed_helper.rb +7 -6
- data/app/helpers/pageflow/feeds_helper.rb +2 -2
- data/app/helpers/pageflow/file_background_images_helper.rb +2 -2
- data/app/helpers/pageflow/file_thumbnails_helper.rb +4 -3
- data/app/helpers/pageflow/files_helper.rb +5 -4
- data/app/helpers/pageflow/folders_helper.rb +8 -5
- data/app/helpers/pageflow/help_entries_helper.rb +2 -2
- data/app/helpers/pageflow/hreflang_links_helper.rb +37 -0
- data/app/helpers/pageflow/info_box_helper.rb +7 -11
- data/app/helpers/pageflow/media_query_helper.rb +3 -3
- data/app/helpers/pageflow/navigation_bar_helper.rb +1 -1
- data/app/helpers/pageflow/overview_helper.rb +1 -1
- data/app/helpers/pageflow/page_types_helper.rb +5 -5
- data/app/helpers/pageflow/pages_helper.rb +29 -15
- data/app/helpers/pageflow/public_i18n_helper.rb +10 -9
- data/app/helpers/pageflow/quota_helper.rb +4 -4
- data/app/helpers/pageflow/render_json_helper.rb +6 -3
- data/app/helpers/pageflow/revision_file_helper.rb +2 -1
- data/app/helpers/pageflow/sites_helper.rb +3 -3
- data/app/helpers/pageflow/social_share_helper.rb +24 -10
- data/app/helpers/pageflow/social_share_links_helper.rb +12 -9
- data/app/helpers/pageflow/structured_data_helper.rb +5 -2
- data/app/helpers/pageflow/stub_page_configuration.rb +7 -3
- data/app/helpers/pageflow/text_direction_helper.rb +1 -1
- data/app/helpers/pageflow/themes_helper.rb +2 -4
- data/app/helpers/pageflow/video_files_helper.rb +29 -13
- data/app/helpers/pageflow/widgets_helper.rb +8 -6
- data/app/inputs/pageflow_permalink_input.rb +8 -3
- data/app/jobs/pageflow/application_job.rb +1 -0
- data/app/jobs/pageflow/entry_export_import/upload_and_publish_file_job.rb +1 -1
- data/app/jobs/pageflow/poll_meta_data_from_zencoder_job.rb +1 -0
- data/app/jobs/pageflow/poll_zencoder_job.rb +3 -2
- data/app/jobs/pageflow/process_image_or_text_track_job.rb +1 -0
- data/app/jobs/pageflow/prune_auto_snapshots_job.rb +1 -0
- data/app/jobs/pageflow/request_meta_data_from_zencoder_job.rb +1 -0
- data/app/jobs/pageflow/submit_file_to_zencoder_job.rb +1 -0
- data/app/mailers/pageflow/user_mailer.rb +1 -0
- data/app/models/concerns/pageflow/auto_generated_perma_id.rb +20 -1
- data/app/models/concerns/pageflow/entry_publication_states.rb +7 -3
- data/app/models/concerns/pageflow/feature_target.rb +8 -4
- data/app/models/concerns/pageflow/output_source.rb +3 -3
- data/app/models/concerns/pageflow/permalinkable.rb +2 -1
- data/app/models/concerns/pageflow/reusable_file.rb +24 -24
- data/app/models/concerns/pageflow/serialization_blacklist.rb +3 -5
- data/app/models/concerns/pageflow/suspendable.rb +5 -3
- data/app/models/concerns/pageflow/theme_referencer.rb +1 -1
- data/app/models/concerns/pageflow/translatable.rb +62 -0
- data/app/models/concerns/pageflow/translated_attributes.rb +101 -0
- data/app/models/concerns/pageflow/uploadable_file.rb +6 -6
- data/app/models/pageflow/account.rb +6 -5
- data/app/models/pageflow/account_member_query.rb +1 -1
- data/app/models/pageflow/account_role_query.rb +1 -1
- data/app/models/pageflow/application_query.rb +1 -1
- data/app/models/pageflow/application_record.rb +1 -1
- data/app/models/pageflow/audio_file.rb +4 -4
- data/app/models/pageflow/audio_file_url_templates.rb +1 -1
- data/app/models/pageflow/authentication_token.rb +2 -2
- data/app/models/pageflow/chapter.rb +6 -1
- data/app/models/pageflow/chapter_scaffold.rb +2 -4
- data/app/models/pageflow/cname_site_request_scope.rb +1 -1
- data/app/models/pageflow/comment.rb +30 -0
- data/app/models/pageflow/comment_thread.rb +65 -0
- data/app/models/pageflow/comment_thread_read.rb +22 -0
- data/app/models/pageflow/customized_theme.rb +9 -8
- data/app/models/pageflow/draft_entry.rb +49 -9
- data/app/models/pageflow/edit_lock.rb +19 -21
- data/app/models/pageflow/encoding_confirmation.rb +2 -2
- data/app/models/pageflow/entries_feed.rb +2 -2
- data/app/models/pageflow/entry.rb +46 -30
- data/app/models/pageflow/entry_at_revision.rb +14 -2
- data/app/models/pageflow/entry_comment_summary.rb +114 -0
- data/app/models/pageflow/entry_duplicate.rb +8 -5
- data/app/models/pageflow/entry_publication.rb +2 -2
- data/app/models/pageflow/entry_role_query.rb +4 -4
- data/app/models/pageflow/entry_template.rb +4 -4
- data/app/models/pageflow/entry_title_or_account_name_query.rb +2 -2
- data/app/models/pageflow/entry_translation_group.rb +42 -0
- data/app/models/pageflow/file_folder.rb +74 -0
- data/app/models/pageflow/file_reuse.rb +5 -4
- data/app/models/pageflow/file_usage.rb +18 -3
- data/app/models/pageflow/folder.rb +2 -2
- data/app/models/pageflow/home_button.rb +7 -7
- data/app/models/pageflow/image_file.rb +44 -25
- data/app/models/pageflow/image_file_url_templates.rb +4 -4
- data/app/models/pageflow/invitation_form.rb +2 -1
- data/app/models/pageflow/legal_info.rb +38 -0
- data/app/models/pageflow/managed_user_query.rb +1 -1
- data/app/models/pageflow/membership.rb +5 -5
- data/app/models/pageflow/nested_revision_component_copy.rb +263 -0
- data/app/models/pageflow/null_user.rb +1 -1
- data/app/models/pageflow/overview_button.rb +1 -1
- data/app/models/pageflow/page.rb +7 -3
- data/app/models/pageflow/permalink.rb +24 -2
- data/app/models/pageflow/permalink_directory.rb +7 -0
- data/app/models/pageflow/permalink_redirect.rb +7 -0
- data/app/models/pageflow/positioned_file.rb +5 -5
- data/app/models/pageflow/potential_entry_translations.rb +55 -0
- data/app/models/pageflow/potential_memberships.rb +5 -4
- data/app/models/pageflow/published_entry.rb +62 -11
- data/app/models/pageflow/revision.rb +29 -16
- data/app/models/pageflow/roles.rb +14 -18
- data/app/models/pageflow/site.rb +46 -3
- data/app/models/pageflow/site_root_entry_form.rb +27 -0
- data/app/models/pageflow/sitemaps.rb +10 -1
- data/app/models/pageflow/storyline.rb +5 -2
- data/app/models/pageflow/storyline_scaffold.rb +1 -1
- data/app/models/pageflow/text_track_file.rb +1 -1
- data/app/models/pageflow/text_track_file_url_templates.rb +1 -1
- data/app/models/pageflow/theme_customization_file.rb +3 -2
- data/app/models/pageflow/thumbnail_file_resolver.rb +1 -1
- data/app/models/pageflow/url_template.rb +1 -1
- data/app/models/pageflow/used_file.rb +13 -3
- data/app/models/pageflow/user_name_query.rb +2 -2
- data/app/models/pageflow/video_file.rb +22 -15
- data/app/models/pageflow/video_file_url_templates.rb +6 -4
- data/app/models/pageflow/widget.rb +5 -4
- data/app/models/pageflow/with_file_usage_extension.rb +1 -1
- data/app/models/pageflow/zencoder_attachment.rb +8 -8
- data/app/policies/pageflow/account_policy.rb +3 -1
- data/app/policies/pageflow/admin/admin_only_tab_policy.rb +1 -0
- data/app/policies/pageflow/admin/entry_tab_policy.rb +1 -0
- data/app/policies/pageflow/application_policy.rb +2 -0
- data/app/policies/pageflow/entry_policy.rb +7 -1
- data/app/policies/pageflow/entry_template_policy.rb +2 -1
- data/app/policies/pageflow/file_policy.rb +4 -7
- data/app/policies/pageflow/folder_policy.rb +18 -8
- data/app/policies/pageflow/membership_policy.rb +6 -4
- data/app/policies/pageflow/site_policy.rb +10 -3
- data/app/policies/pageflow/user_policy.rb +6 -4
- data/app/state_machines/pageflow/image_and_text_track_processing_state_machine.rb +3 -2
- data/app/state_machines/pageflow/media_encoding_state_machine.rb +5 -4
- data/app/views/admin/accounts/_entry_template_details.html.arb +2 -2
- data/app/views/admin/entries/_attributes_table.html.arb +6 -5
- data/app/views/admin/entries/_form.html.erb +2 -1
- data/app/views/admin/entries/_permalink_inputs.html.erb +9 -2
- data/app/views/admin/entry_templates/_form.html.erb +1 -3
- data/app/views/admin/site_root_entry/choose.html.erb +19 -0
- data/app/views/admin/sites/_fields.html.erb +14 -1
- data/app/views/admin/translations/_form.html.erb +31 -0
- data/app/views/admin/users/_attributes_table.html.arb +13 -0
- data/app/views/components/pageflow/admin/add_membership_button.rb +13 -12
- data/app/views/components/pageflow/admin/custom_scopes_renderer.rb +1 -0
- data/app/views/components/pageflow/admin/embed_code_field.rb +1 -0
- data/app/views/components/pageflow/admin/embedded_index_table.rb +9 -10
- data/app/views/components/pageflow/admin/entries_tab.rb +1 -0
- data/app/views/components/pageflow/admin/entry_publication_state_indicator.rb +6 -5
- data/app/views/components/pageflow/admin/entry_templates_tab.rb +2 -1
- data/app/views/components/pageflow/admin/entry_translations_tab.rb +102 -0
- data/app/views/components/pageflow/admin/entry_user_badge_list.rb +1 -0
- data/app/views/components/pageflow/admin/extensible_attributes_table.rb +9 -8
- data/app/views/components/pageflow/admin/features_tab.rb +1 -0
- data/app/views/components/pageflow/admin/grouped_folder_list.rb +1 -0
- data/app/views/components/pageflow/admin/icon_link_to.rb +1 -0
- data/app/views/components/pageflow/admin/members_tab.rb +4 -3
- data/app/views/components/pageflow/admin/membership_role_with_tooltip.rb +3 -2
- data/app/views/components/pageflow/admin/revisions_tab.rb +14 -10
- data/app/views/components/pageflow/admin/sites_tab.rb +1 -0
- data/app/views/components/pageflow/admin/tabs_view.rb +1 -0
- data/app/views/components/pageflow/admin/timestamp.rb +6 -5
- data/app/views/components/pageflow/admin/user_account_badge_list.rb +2 -3
- data/app/views/components/pageflow/admin/user_accounts_tab.rb +1 -0
- data/app/views/components/pageflow/admin/user_entries_tab.rb +1 -0
- data/app/views/components/pageflow/admin/users_tab.rb +1 -0
- data/app/views/pageflow/audio_files/_audio_file.json.jbuilder +0 -1
- data/app/views/pageflow/editor/config/_seeds.json.jbuilder +5 -3
- data/app/views/pageflow/editor/entries/_entry.json.jbuilder +2 -1
- data/app/views/pageflow/editor/entries/index.json.jbuilder +1 -1
- data/app/views/pageflow/editor/entries/seed.json.erb +1 -0
- data/app/views/pageflow/editor/entries/show.json.jbuilder +1 -1
- data/app/views/pageflow/editor/entry_publications/check.json.jbuilder +2 -2
- data/app/views/pageflow/editor/file_folders/_file_folder.json.jbuilder +5 -0
- data/app/views/pageflow/editor/file_folders/create.json.jbuilder +1 -0
- data/app/views/pageflow/editor/file_folders/index.json.jbuilder +3 -0
- data/app/views/pageflow/editor/files/_file.json.jbuilder +7 -14
- data/app/views/pageflow/editor/files/create.json.jbuilder +1 -1
- data/app/views/pageflow/editor/files/index.json.jbuilder +1 -1
- data/app/views/pageflow/editor/image_files/_image_file.json.jbuilder +3 -0
- data/app/views/pageflow/editor/sites/_site.json.jbuilder +1 -0
- data/app/views/pageflow/editor/video_files/_video_file.json.jbuilder +4 -3
- data/app/views/pageflow/entries/share_menu/_bluesky_link.html.erb +8 -0
- data/app/views/pageflow/entries/share_menu/_threads_link.html.erb +8 -0
- data/app/views/pageflow/files/_file.json.jbuilder +5 -3
- data/app/views/pageflow/image_files/_image_file.json.jbuilder +0 -1
- data/app/views/pageflow/review/comment_threads/_comment_thread.json.jbuilder +20 -0
- data/app/views/pageflow/review/comment_threads/create.json.jbuilder +2 -0
- data/app/views/pageflow/review/comment_threads/index.json.jbuilder +13 -0
- data/app/views/pageflow/review/comments/_comment.json.jbuilder +13 -0
- data/app/views/pageflow/review/comments/create.json.jbuilder +1 -0
- data/app/views/pageflow/sitemaps/index.xml.builder +9 -1
- data/app/views/pageflow/social_share/_entry_meta_tags.html.erb +1 -1
- data/app/views/pageflow/social_share/_page_meta_tags.html.erb +1 -1
- data/app/views/pageflow/structured_data/_entry.json.jbuilder +2 -3
- data/app/views/pageflow/video_files/_video_file.json.jbuilder +0 -1
- data/config/initializers/active_admin_patches.rb +3 -6
- data/config/initializers/admin_resource_tabs.rb +3 -0
- data/config/initializers/paperclip.rb +1 -0
- data/config/initializers/symmetric_encryption.rb +1 -1
- data/config/initializers/zencoder.rb +24 -13
- data/config/locales/de.yml +256 -12
- data/config/locales/en.yml +253 -15
- data/config/routes.rb +29 -13
- data/config/spring.rb +2 -1
- data/db/migrate/20140418225525_setup_schema.rb +2 -2
- data/db/migrate/20190306161431_copy_file_attributes_of_failed_uploads.rb +2 -2
- data/db/migrate/20190820152900_drop_accounts_themes.rb +2 -1
- data/db/migrate/20191202145757_create_pageflow_scrolled_sections.rb +11 -11
- data/db/migrate/20191202150657_create_pageflow_scrolled_chapters.rb +1 -1
- data/db/migrate/20191202154723_create_pageflow_scrolled_content_elements.rb +9 -9
- data/db/migrate/20191219143450_add_position_to_content_elements.rb +2 -1
- data/db/migrate/20200117133200_change_revision_appearance_option_default_and_null.rb +2 -1
- data/db/migrate/20200122115400_create_pageflow_entry_templates.rb +25 -25
- data/db/migrate/20200206134400_convert_legacy_scrolled_content_element_types.rb +3 -3
- data/db/migrate/20221215120856_associate_entry_templates_with_sites.rb +2 -2
- data/db/migrate/20230120092923_create_other_files.rb +2 -1
- data/db/migrate/20230419083307_create_pageflow_entry_translation_group.rb +9 -0
- data/db/migrate/20240612110434_add_cutoff_mode_name_to_sites.rb +5 -0
- data/db/migrate/20240918084059_create_pageflow_permalink_redirects.rb +14 -0
- data/db/migrate/20250508172234_ensure_scrolled_entries_have_main_storyline.rb +14 -0
- data/db/migrate/20250617090048_add_custom404_entry_to_sites.rb +5 -0
- data/db/migrate/20250617100000_add_perma_id_counter_to_entries.rb +28 -0
- data/db/migrate/20250722174123_add_perma_id_to_chapters.rb +10 -0
- data/db/migrate/20250725080603_add_perma_id_indexes_to_revision_components.rb +13 -0
- data/db/migrate/20250726000000_add_display_name_to_file_usages.rb +5 -0
- data/db/migrate/20251015000000_add_structured_data_type_name_to_revisions.rb +5 -0
- data/db/migrate/20251029142849_convert_legacy_social_embeds_to_unified_social_embed.rb +38 -0
- data/db/migrate/20251119083347_fix_main_storyline_positions.rb +16 -0
- data/db/migrate/20251119083348_ensure_scrolled_entries_have_excursion_storyline.rb +15 -0
- data/db/migrate/20260303000000_add_layout_version_to_revisions.rb +6 -0
- data/db/migrate/20260323000000_create_comment_threads_and_comments.rb +30 -0
- data/db/migrate/20260414000000_add_subject_range_to_comment_threads.rb +5 -0
- data/db/migrate/20260415000000_add_section_perma_id_to_comment_threads.rb +5 -0
- data/db/migrate/20260727000000_add_quote_to_comments.rb +5 -0
- data/db/migrate/20260728000000_create_file_folders.rb +17 -0
- data/db/migrate/20260805000000_add_attribute_translations_to_sites.rb +5 -0
- data/db/migrate/20260806000000_add_edited_at_to_comments.rb +5 -0
- data/db/migrate/20260810000000_create_pageflow_scrolled_lottie_files.rb +24 -0
- data/db/migrate/20260817000000_create_comment_thread_reads.rb +16 -0
- data/db/migrate/20260819000000_add_unread_comments_since_at_to_users.rb +18 -0
- data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/editor.js +12599 -5398
- data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/frontend.js +1275 -4560
- data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-client.js +20204 -22
- data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-server.js +18657 -19
- data/entry_types/paged/app/controllers/pageflow_paged/entries_controller.rb +1 -0
- data/entry_types/paged/app/helpers/pageflow_paged/page_background_asset_helper.rb +1 -1
- data/entry_types/paged/app/helpers/pageflow_paged/third_party_embed_consent_helper.rb +7 -7
- data/entry_types/paged/app/views/pageflow_paged/editor/entries/_seed.json.jbuilder +1 -0
- data/entry_types/paged/app/views/pageflow_paged/entries/show.html.erb +1 -0
- data/entry_types/paged/lib/pageflow_paged/plugin.rb +4 -0
- data/entry_types/paged/lib/pageflow_paged/react/page_type.rb +1 -1
- data/entry_types/paged/lib/pageflow_paged/react/widget_type.rb +3 -3
- data/entry_types/paged/lib/pageflow_paged.rb +1 -1
- data/entry_types/scrolled/app/assets/stylesheets/pageflow_scrolled/ui.scss +2 -0
- data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/chapters_controller.rb +5 -3
- data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/content_elements_controller.rb +35 -5
- data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/sections_controller.rb +4 -5
- data/entry_types/scrolled/app/controllers/pageflow_scrolled/entries_controller.rb +23 -2
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/cache_helper.rb +3 -3
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/entry_json_seed_helper.rb +8 -5
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/seed_html_helper.rb +7 -3
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/entry_json_seed_helper.rb +72 -10
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/i18n_helper.rb +28 -13
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/packs_helper.rb +41 -12
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/react_server_side_rendering_helper.rb +23 -25
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/sprockets_helper.rb +16 -0
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/themes_helper.rb +189 -3
- data/entry_types/scrolled/app/models/pageflow_scrolled/chapter.rb +15 -14
- data/entry_types/scrolled/app/models/pageflow_scrolled/content_element.rb +61 -7
- data/entry_types/scrolled/app/models/pageflow_scrolled/lottie_file.rb +6 -0
- data/entry_types/scrolled/app/models/pageflow_scrolled/lottie_file_url_templates.rb +16 -0
- data/entry_types/scrolled/app/models/pageflow_scrolled/section.rb +12 -1
- data/entry_types/scrolled/app/models/pageflow_scrolled/storyline.rb +20 -1
- data/entry_types/scrolled/app/views/pageflow_scrolled/editor/entries/_head.html.erb +1 -0
- data/entry_types/scrolled/app/views/pageflow_scrolled/editor/sections/_section_with_content_elements.json.jbuilder +2 -2
- data/entry_types/scrolled/app/views/pageflow_scrolled/entries/show.html.erb +15 -12
- data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry.json.jbuilder +45 -17
- data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry_translations.json.jbuilder +14 -0
- data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_theme.json.jbuilder +34 -5
- data/entry_types/scrolled/app/views/pageflow_scrolled/favicons/_entry.html.erb +1 -1
- data/entry_types/scrolled/app/views/pageflow_scrolled/storylines/_storyline.json.jbuilder +7 -0
- data/entry_types/scrolled/config/configuration_schemas/pageflow-scrolled.json +414 -0
- data/entry_types/scrolled/config/locales/de.yml +1094 -78
- data/entry_types/scrolled/config/locales/en.yml +1083 -78
- data/entry_types/scrolled/config/routes.rb +18 -8
- data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/install_generator.rb +11 -11
- data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/theme/logoDarkVariantDesktop.svg +56 -0
- data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/theme/logoDarkVariantMobile.svg +22 -0
- data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/themes_plugin.rb.tt +26 -11
- data/entry_types/scrolled/lib/pageflow_scrolled/additional_packs.rb +27 -8
- data/entry_types/scrolled/lib/pageflow_scrolled/additional_seed_data.rb +2 -2
- data/entry_types/scrolled/lib/pageflow_scrolled/additional_theme_assets.rb +27 -0
- data/entry_types/scrolled/lib/pageflow_scrolled/configuration.rb +67 -10
- data/entry_types/scrolled/lib/pageflow_scrolled/configuration_schemas.rb +91 -0
- data/entry_types/scrolled/lib/pageflow_scrolled/content_element_consent_vendors.rb +1 -1
- data/entry_types/scrolled/lib/pageflow_scrolled/engine.rb +2 -1
- data/entry_types/scrolled/lib/pageflow_scrolled/entry_file_reference_locations.rb +71 -0
- data/entry_types/scrolled/lib/pageflow_scrolled/entry_structured_data_types/faq_page.rb +58 -0
- data/entry_types/scrolled/lib/pageflow_scrolled/file_reference_locations.rb +69 -0
- data/entry_types/scrolled/lib/pageflow_scrolled/plugin.rb +222 -19
- data/entry_types/scrolled/lib/pageflow_scrolled/react_widget_type.rb +2 -4
- data/entry_types/scrolled/lib/pageflow_scrolled/seeds.rb +76 -54
- data/entry_types/scrolled/lib/pageflow_scrolled/theme_options_default_scale.rb +35 -0
- data/entry_types/scrolled/lib/pageflow_scrolled.rb +14 -1
- data/entry_types/scrolled/lib/tasks/pageflow_scrolled/storybook.rake +24 -5
- data/entry_types/scrolled/package/config/configurationSchemas.js +70 -0
- data/entry_types/scrolled/package/config/rollup/configurationSchemas.js +19 -0
- data/entry_types/scrolled/package/config/webpack.js +94 -0
- data/entry_types/scrolled/package/contentElements/hotspots-frontend.css +1 -0
- data/entry_types/scrolled/package/contentElements/hotspots-frontend.js +1795 -0
- data/entry_types/scrolled/package/contentElements/inlineBeforeAfter-frontend.css +1 -0
- data/entry_types/scrolled/package/contentElements/inlineBeforeAfter-frontend.js +219 -0
- data/entry_types/scrolled/package/contentElements/lottieAnimation-frontend.css +1 -0
- data/entry_types/scrolled/package/contentElements/lottieAnimation-frontend.js +244 -0
- data/entry_types/scrolled/package/contentElements/socialEmbed-frontend.css +1 -0
- data/entry_types/scrolled/package/contentElements/socialEmbed-frontend.js +823 -0
- data/entry_types/scrolled/package/contentElements/tikTokEmbed-frontend.css +1 -0
- data/entry_types/scrolled/package/contentElements/tikTokEmbed-frontend.js +109 -0
- data/entry_types/scrolled/package/contentElements/twitterEmbed-frontend.css +1 -0
- data/entry_types/scrolled/package/contentElements/twitterEmbed-frontend.js +277 -0
- data/entry_types/scrolled/package/contentElements/videoEmbed-frontend.css +1 -0
- data/entry_types/scrolled/package/contentElements/videoEmbed-frontend.js +244 -0
- data/entry_types/scrolled/package/contentElements-editor.js +3155 -583
- data/entry_types/scrolled/package/contentElements-frontend.css +1 -1
- data/entry_types/scrolled/package/contentElements-frontend.js +3375 -1585
- data/entry_types/scrolled/package/editor.css +1 -0
- data/entry_types/scrolled/package/editor.js +8452 -2867
- data/entry_types/scrolled/package/entryState.js +1600 -0
- data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-20c600de.js +1979 -0
- data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-58189220.js +2538 -0
- data/entry_types/scrolled/package/frontend/{PhonePlatformContext-4ec6b2de.js → PhonePlatformContext-035a99fa.js} +9 -15
- data/entry_types/scrolled/package/frontend/PhonePlatformContext-9da43797.js +33 -0
- data/entry_types/scrolled/package/frontend/Placeholder.module-bf0966b4.js +5267 -0
- data/entry_types/scrolled/package/frontend/Placeholder.module-d22a9335.js +4180 -0
- data/entry_types/scrolled/package/frontend/ThemeIcon-ab834849.js +754 -0
- data/entry_types/scrolled/package/frontend/ThemeIcon-f6cb3a8f.js +773 -0
- data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-30fd1124.js +39 -0
- data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-61c25224.js +72 -0
- data/entry_types/scrolled/package/frontend/Viewer-1074fd59.js +147 -0
- data/entry_types/scrolled/package/frontend/Viewer-1482ca34.js +480 -0
- data/entry_types/scrolled/package/frontend/Viewer-70504231.js +210 -0
- data/entry_types/scrolled/package/frontend/{Viewer-1bb5597c.js → Viewer-cc8d4072.js} +100 -135
- data/entry_types/scrolled/package/frontend/Wavesurfer-656e3c36.js +302 -0
- data/entry_types/scrolled/package/frontend/Wavesurfer-b753b838.js +385 -0
- data/entry_types/scrolled/package/frontend/commenting.css +1 -0
- data/entry_types/scrolled/package/frontend/extensions-167ae143.js +1455 -0
- data/entry_types/scrolled/package/frontend/extensions-2318f92b.js +5918 -0
- data/entry_types/scrolled/package/frontend/extensions-a461fd13.js +4890 -0
- data/entry_types/scrolled/package/frontend/extensions-c98269fd.js +1982 -0
- data/entry_types/scrolled/package/frontend/i18n-493cd2a6.js +93 -0
- data/entry_types/scrolled/package/frontend/i18n-e06cac14.js +100 -0
- data/entry_types/scrolled/package/frontend/{index-11f32f10.js → index-2e5546f7.js} +39 -54
- data/entry_types/scrolled/package/frontend/index-de2b418d.js +130 -0
- data/entry_types/scrolled/package/frontend/index.css +1 -1
- data/entry_types/scrolled/package/frontend/index.js +1976 -1367
- data/entry_types/scrolled/package/frontend/inlineEditing.css +1 -0
- data/entry_types/scrolled/package/frontend/useContentElementEditorState-a084912e.js +25 -0
- data/entry_types/scrolled/package/frontend/useDelayedBoolean-a387d85b.js +24 -0
- data/entry_types/scrolled/package/frontend/usePhonePlatform-4ecb96d4.js +8 -0
- data/entry_types/scrolled/package/frontend/usePhonePlatform-ce9ff08d.js +8 -0
- data/entry_types/scrolled/package/frontend-server.js +20 -210
- data/entry_types/scrolled/package/package.json +22 -12
- data/entry_types/scrolled/package/review.css +1 -0
- data/entry_types/scrolled/package/review.js +3306 -0
- data/entry_types/scrolled/package/testHelpers.js +952 -239
- data/entry_types/scrolled/package/values/widgets.module.css +18 -0
- data/entry_types/scrolled/package/widgets/consentBar.css +1 -1
- data/entry_types/scrolled/package/widgets/consentBar.js +275 -315
- data/entry_types/scrolled/package/widgets/defaultNavigation.css +2 -2
- data/entry_types/scrolled/package/widgets/defaultNavigation.js +961 -432
- data/entry_types/scrolled/package/widgets/excursionSheet.css +1 -0
- data/entry_types/scrolled/package/widgets/excursionSheet.js +225 -0
- data/entry_types/scrolled/package/widgets/iconInlineFileRights.css +1 -1
- data/entry_types/scrolled/package/widgets/iconInlineFileRights.js +62 -32
- data/entry_types/scrolled/package/widgets/iconScrollIndicator.css +1 -0
- data/entry_types/scrolled/package/widgets/iconScrollIndicator.js +67 -0
- data/entry_types/scrolled/package/widgets/mainStorylineSheet.css +1 -0
- data/entry_types/scrolled/package/widgets/mainStorylineSheet.js +100 -0
- data/entry_types/scrolled/package/widgets/textInlineFileRights.css +1 -1
- data/entry_types/scrolled/package/widgets/textInlineFileRights.js +48 -26
- data/entry_types/scrolled/package/widgets-server.js +2 -0
- data/entry_types/scrolled/spec/factories/chapters.rb +14 -1
- data/entry_types/scrolled/spec/factories/content_elements.rb +1 -1
- data/entry_types/scrolled/spec/factories/lottie_files.rb +29 -0
- data/entry_types/scrolled/spec/fixtures/animation.lottie +0 -0
- data/entry_types/scrolled/spec/fixtures/font.woff +0 -0
- data/entry_types/scrolled/spec/fixtures/font.woff2 +0 -0
- data/lib/generators/pageflow/active_admin_initializer/active_admin_initializer_generator.rb +5 -3
- data/lib/generators/pageflow/assets/assets_generator.rb +7 -5
- data/lib/generators/pageflow/cancan/cancan_generator.rb +2 -1
- data/lib/generators/pageflow/cancan/templates/ability.rb +1 -1
- data/lib/generators/pageflow/error_pages/error_pages_generator.rb +2 -1
- data/lib/generators/pageflow/initializer/initializer_generator.rb +3 -2
- data/lib/generators/pageflow/install/install_generator.rb +2 -2
- data/lib/generators/pageflow/procfile/procfile_generator.rb +2 -1
- data/lib/generators/pageflow/resque/resque_generator.rb +4 -4
- data/lib/generators/pageflow/resque/templates/resque.rake +1 -1
- data/lib/generators/pageflow/routes/routes_generator.rb +4 -2
- data/lib/generators/pageflow/seeds/seeds_generator.rb +3 -2
- data/lib/generators/pageflow/seeds/templates/seeds.rb +6 -6
- data/lib/generators/pageflow/theme/theme_generator.rb +2 -1
- data/lib/generators/pageflow/user/user_generator.rb +2 -1
- data/lib/pageflow/ability_mixin.rb +25 -15
- data/lib/pageflow/active_admin_patches/views/attributes_table.rb +1 -0
- data/lib/pageflow/active_admin_patches/views/pages/base.rb +3 -2
- data/lib/pageflow/active_admin_patches/views/table_for.rb +1 -0
- data/lib/pageflow/additional_headers.rb +27 -0
- data/lib/pageflow/admin/attributes_table_rows.rb +3 -3
- data/lib/pageflow/admin/tabs.rb +1 -1
- data/lib/pageflow/built_in_file_type.rb +1 -1
- data/lib/pageflow/built_in_page_types_plugin.rb +1 -1
- data/lib/pageflow/built_in_widget_type.rb +2 -2
- data/lib/pageflow/built_in_widget_types_plugin.rb +2 -1
- data/lib/pageflow/configuration.rb +183 -18
- data/lib/pageflow/cutoff_modes.rb +39 -0
- data/lib/pageflow/deprecation.rb +19 -0
- data/lib/pageflow/editor_controller.rb +6 -10
- data/lib/pageflow/engine.rb +5 -6
- data/lib/pageflow/entries_controller_env_helper.rb +32 -2
- data/lib/pageflow/entry_export_import/attachment_files.rb +1 -1
- data/lib/pageflow/entry_export_import/entry_serialization.rb +9 -8
- data/lib/pageflow/entry_export_import/file_mappings.rb +1 -0
- data/lib/pageflow/entry_export_import/page_type_versions.rb +2 -2
- data/lib/pageflow/entry_export_import/revision_serialization/import.rb +11 -1
- data/lib/pageflow/entry_export_import/revision_serialization.rb +3 -0
- data/lib/pageflow/entry_export_import.rb +3 -3
- data/lib/pageflow/entry_structured_data_types.rb +43 -0
- data/lib/pageflow/entry_type_configuration.rb +1 -0
- data/lib/pageflow/entry_types.rb +2 -2
- data/lib/pageflow/feature.rb +1 -1
- data/lib/pageflow/features.rb +4 -6
- data/lib/pageflow/file_importers.rb +4 -4
- data/lib/pageflow/file_type.rb +5 -5
- data/lib/pageflow/file_types.rb +4 -4
- data/lib/pageflow/global_config_api.rb +5 -6
- data/lib/pageflow/help_entries.rb +7 -7
- data/lib/pageflow/help_entry.rb +1 -1
- data/lib/pageflow/hooks.rb +1 -1
- data/lib/pageflow/nested_revision_component.rb +9 -27
- data/lib/pageflow/news_item_api.rb +1 -1
- data/lib/pageflow/oembed_providers.rb +42 -0
- data/lib/pageflow/page_type.rb +2 -3
- data/lib/pageflow/page_types.rb +3 -3
- data/lib/pageflow/paperclip_interpolations/support.rb +1 -1
- data/lib/pageflow/paperclip_processors/vtt.rb +1 -0
- data/lib/pageflow/paperclip_processors/webp.rb +2 -2
- data/lib/pageflow/partial_editor_fragment_renderer.rb +2 -2
- data/lib/pageflow/plugin.rb +1 -2
- data/lib/pageflow/primary_domain_entry_redirect.rb +6 -0
- data/lib/pageflow/quota.rb +9 -5
- data/lib/pageflow/quotas.rb +1 -1
- data/lib/pageflow/rails_version.rb +14 -2
- data/lib/pageflow/react.rb +1 -1
- data/lib/pageflow/revision_component.rb +8 -40
- data/lib/pageflow/revision_components.rb +16 -6
- data/lib/pageflow/seeds.rb +12 -16
- data/lib/pageflow/theme.rb +1 -1
- data/lib/pageflow/theme_customizations.rb +10 -10
- data/lib/pageflow/themes.rb +44 -3
- data/lib/pageflow/user_mixin.rb +18 -6
- data/lib/pageflow/version.rb +1 -1
- data/lib/pageflow/widget_type.rb +4 -4
- data/lib/pageflow/widget_types.rb +9 -9
- data/lib/pageflow/zencoder_api.rb +32 -42
- data/lib/pageflow/zencoder_audio_output_definition.rb +14 -13
- data/lib/pageflow/zencoder_meta_data_output_definition.rb +3 -2
- data/lib/pageflow/zencoder_output_definition.rb +16 -14
- data/lib/pageflow/zencoder_video_output_definition.rb +140 -88
- data/lib/pageflow.rb +8 -2
- data/lib/tasks/pageflow_tasks.rake +1 -1
- data/package/config/eslint-rules/documented-in-toc.js +125 -0
- data/package/config/jest/index.js +0 -1
- data/package/config/webpack.js +0 -1
- data/package/config/webpack5.js +0 -1
- data/package/editor.js +4880 -2313
- data/package/frontend.js +710 -1223
- data/package/package.json +3 -1
- data/package/review.js +384 -0
- data/package/testHelpers.js +257 -243
- data/package/ui.js +1487 -813
- data/package/vendor/stubs/wysihtml5.js +2 -0
- data/spec/factories/accounts.rb +37 -22
- data/spec/factories/audio_files.rb +2 -2
- data/spec/factories/authentication_tokens.rb +4 -4
- data/spec/factories/chapters.rb +4 -4
- data/spec/factories/comment_thread_reads.rb +10 -0
- data/spec/factories/comment_threads.rb +10 -0
- data/spec/factories/comments.rb +9 -0
- data/spec/factories/draft_entries.rb +14 -6
- data/spec/factories/edit_locks.rb +1 -1
- data/spec/factories/entries.rb +28 -19
- data/spec/factories/entry_translation_groups.rb +6 -0
- data/spec/factories/file_folders.rb +8 -0
- data/spec/factories/file_usages.rb +2 -1
- data/spec/factories/folders.rb +1 -1
- data/spec/factories/image_files.rb +2 -2
- data/spec/factories/pages.rb +3 -3
- data/spec/factories/published_entries.rb +28 -8
- data/spec/factories/revisions.rb +1 -1
- data/spec/factories/sites.rb +6 -0
- data/spec/factories/test_multi_attachment_files.rb +1 -1
- data/spec/factories/text_track_files.rb +1 -1
- data/spec/factories/uploadable_files.rb +1 -1
- data/spec/factories/users.rb +6 -6
- data/spec/factories/video_files.rb +2 -2
- data/spec/factories/widgets.rb +1 -1
- data/spec/fixtures/7x15_rotated.jpg +0 -0
- data/vendor/assets/javascripts/wysihtml-toolbar.js +19288 -0
- metadata +266 -92
- data/app/assets/stylesheets/pageflow/editor/select_button.scss +0 -51
- data/config/initializers/revision_components.rb +0 -5
- data/config/locales/twitter_to_x.de.yml +0 -6
- data/config/locales/twitter_to_x.en.yml +0 -6
- data/entry_types/scrolled/app/helpers/pageflow_scrolled/generated_media_queries_helper.rb +0 -55
- data/entry_types/scrolled/config/locales/twitter_to_x.de.yml +0 -12
- data/entry_types/scrolled/config/locales/twitter_to_x.en.yml +0 -12
- data/entry_types/scrolled/config/locales/vr_image_projection.de.yml +0 -24
- data/entry_types/scrolled/config/locales/vr_image_projection.en.yml +0 -24
- data/entry_types/scrolled/package/frontend/EditableInlineText.module-ebd22921.js +0 -5497
- data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-b79159cc.js +0 -107
- data/entry_types/scrolled/package/frontend/Viewer-1ecf3375.js +0 -154
- data/entry_types/scrolled/package/frontend/Wavesurfer-7d9cf1b7.js +0 -333
- data/entry_types/scrolled/package/frontend/arrowRight-62998af9.js +0 -77
- data/entry_types/scrolled/package/frontend/components-024a9893.js +0 -2853
- data/entry_types/scrolled/package/frontend/createSuper-d0f30da3.js +0 -115
- data/entry_types/scrolled/package/frontend/i18n-ce13a8bf.js +0 -1129
- data/entry_types/scrolled/package/frontend/useContentElementEditorState-4f4c3cf6.js +0 -52
- data/lib/generators/pageflow/resque/templates/resque_enqueue_after_commit_patch.rb +0 -25
- data/package/vendor/jquery.minicolors.js +0 -1108
|
@@ -0,0 +1,1979 @@
|
|
|
1
|
+
import React, { createContext, useState, useRef, useContext, useEffect, useMemo, useCallback } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { u as useDelayedBoolean } from './useDelayedBoolean-a387d85b.js';
|
|
4
|
+
import BackboneEvents from 'backbone-events-standalone';
|
|
5
|
+
import { useTheme, useEntryStructure, useEmbedOriginUrl, useChapter, useDownloadableFile } from 'pageflow-scrolled/entryState';
|
|
6
|
+
import Measure from 'react-measure';
|
|
7
|
+
|
|
8
|
+
const OnScreenObserverRootContext = createContext();
|
|
9
|
+
const OnScreenObserverRootProvider = OnScreenObserverRootContext.Provider;
|
|
10
|
+
function useOnScreen(ref, {
|
|
11
|
+
rootMargin,
|
|
12
|
+
onIntersecting,
|
|
13
|
+
onChange,
|
|
14
|
+
skipIframeFix
|
|
15
|
+
} = {}) {
|
|
16
|
+
var _useContext;
|
|
17
|
+
const [isIntersecting, setIntersecting] = useState(false);
|
|
18
|
+
const onIntersectingRef = useRef();
|
|
19
|
+
const onChangeRef = useRef();
|
|
20
|
+
const root = (_useContext = useContext(OnScreenObserverRootContext)) === null || _useContext === void 0 ? void 0 : _useContext.current;
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
onIntersectingRef.current = onIntersecting;
|
|
23
|
+
}, [onIntersecting]);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
onChangeRef.current = onChange;
|
|
26
|
+
}, [onChange]);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
const current = ref.current;
|
|
29
|
+
const observer = createIntersectionObserver(entries => {
|
|
30
|
+
// Even when observing only a single element, multiple entries
|
|
31
|
+
// may have queued up. In Chrome this can be observed when
|
|
32
|
+
// moving an observed element in the DOM: The callback is
|
|
33
|
+
// invoked once with two entries for the same target, one
|
|
34
|
+
// claiming the element no longer intersects and one - with a
|
|
35
|
+
// later timestamp - saying that is does intersect. Assuming
|
|
36
|
+
// entries are ordered according to time, we only consider the
|
|
37
|
+
// last entry.
|
|
38
|
+
const entry = entries[entries.length - 1];
|
|
39
|
+
setIntersecting(entry.isIntersecting);
|
|
40
|
+
if (entry.isIntersecting && onIntersectingRef.current) {
|
|
41
|
+
onIntersectingRef.current();
|
|
42
|
+
}
|
|
43
|
+
if (onChangeRef.current) {
|
|
44
|
+
onChangeRef.current(entry.isIntersecting);
|
|
45
|
+
}
|
|
46
|
+
}, {
|
|
47
|
+
rootMargin,
|
|
48
|
+
root
|
|
49
|
+
}, skipIframeFix);
|
|
50
|
+
if (ref.current) {
|
|
51
|
+
observer.observe(current);
|
|
52
|
+
}
|
|
53
|
+
return () => {
|
|
54
|
+
observer.unobserve(current);
|
|
55
|
+
};
|
|
56
|
+
}, [ref, rootMargin, root, skipIframeFix]);
|
|
57
|
+
return isIntersecting;
|
|
58
|
+
}
|
|
59
|
+
function createIntersectionObserver(callback, options, skipIframeFix) {
|
|
60
|
+
if (skipIframeFix || options.root) {
|
|
61
|
+
return new IntersectionObserver(callback, options);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Positive root margins are ignored in iframes [1] (i.e. in
|
|
65
|
+
// the Pageflow editor). To make it work, the iframe document
|
|
66
|
+
// needs to be passed as `root` [2].
|
|
67
|
+
// This leads to a `TypeError`, though, in browers that do not
|
|
68
|
+
// support this yet (e.g. Chrome 80). We catch the error and
|
|
69
|
+
// skip passing the `root` option.
|
|
70
|
+
//
|
|
71
|
+
// [1] https://github.com/w3c/IntersectionObserver/issues/283
|
|
72
|
+
// [2] https://github.com/w3c/IntersectionObserver/issues/372
|
|
73
|
+
try {
|
|
74
|
+
let optionsWithIframeFix = options;
|
|
75
|
+
if (options.rootMargin && window.parent !== window) {
|
|
76
|
+
optionsWithIframeFix = {
|
|
77
|
+
...options,
|
|
78
|
+
root: window.document
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return new IntersectionObserver(callback, optionsWithIframeFix);
|
|
82
|
+
} catch (e) {
|
|
83
|
+
// Normally we would check for TypeError here. Since the polyfill
|
|
84
|
+
// throws a generic error, we retry either way and trust that the
|
|
85
|
+
// error will happen again if it is not related to the `root`
|
|
86
|
+
// option.
|
|
87
|
+
return createIntersectionObserver(callback, options, true);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const StorylineActivityContext = createContext('active');
|
|
92
|
+
const MainStorylineCoverageContext = createContext({
|
|
93
|
+
mainStorylineCovered: false,
|
|
94
|
+
setMainStorylineCovered: () => {}
|
|
95
|
+
});
|
|
96
|
+
function MainStorylineActivity({
|
|
97
|
+
activeExcursion,
|
|
98
|
+
children
|
|
99
|
+
}) {
|
|
100
|
+
const {
|
|
101
|
+
mainStorylineCovered
|
|
102
|
+
} = useContext(MainStorylineCoverageContext);
|
|
103
|
+
const mode = activeExcursion ? mainStorylineCovered ? 'covered' : 'background' : 'active';
|
|
104
|
+
return /*#__PURE__*/React.createElement(StorylineActivityContext.Provider, {
|
|
105
|
+
value: mode
|
|
106
|
+
}, children);
|
|
107
|
+
}
|
|
108
|
+
function MainStorylineCoverageProvider({
|
|
109
|
+
children
|
|
110
|
+
}) {
|
|
111
|
+
const [mainStorylineCovered, setMainStorylineCovered] = useState(false);
|
|
112
|
+
const value = useMemo(() => ({
|
|
113
|
+
mainStorylineCovered,
|
|
114
|
+
setMainStorylineCovered
|
|
115
|
+
}), [mainStorylineCovered]);
|
|
116
|
+
return /*#__PURE__*/React.createElement(MainStorylineCoverageContext.Provider, {
|
|
117
|
+
value: value
|
|
118
|
+
}, children);
|
|
119
|
+
}
|
|
120
|
+
function useStorylineActivity() {
|
|
121
|
+
return useContext(StorylineActivityContext);
|
|
122
|
+
}
|
|
123
|
+
function useMainStorylineCoverage() {
|
|
124
|
+
return useContext(MainStorylineCoverageContext);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
var styles = {"wrapper":"useScrollPositionLifecycle-module_wrapper__1a6Kr","isActiveProbe":"useScrollPositionLifecycle-module_isActiveProbe__3VKB5"};
|
|
128
|
+
|
|
129
|
+
const StaticPreviewContext = createContext(false);
|
|
130
|
+
function StaticPreview({
|
|
131
|
+
children
|
|
132
|
+
}) {
|
|
133
|
+
return /*#__PURE__*/React.createElement(StaticPreviewContext.Provider, {
|
|
134
|
+
value: true
|
|
135
|
+
}, children);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Use inside a content element component to determine whether the
|
|
140
|
+
* component is being rendered in a static preview, e.g. editor
|
|
141
|
+
* thumbnails.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* const isStaticPreview = useIsStaticPreview();
|
|
145
|
+
*/
|
|
146
|
+
function useIsStaticPreview() {
|
|
147
|
+
return useContext(StaticPreviewContext);
|
|
148
|
+
}
|
|
149
|
+
function createScrollPositionLifecycleProvider(Context) {
|
|
150
|
+
return function ScrollPositionLifecycleProvider({
|
|
151
|
+
children,
|
|
152
|
+
onActivate,
|
|
153
|
+
entersWithFadeTransition
|
|
154
|
+
}) {
|
|
155
|
+
const ref = useRef();
|
|
156
|
+
const isActiveProbeRef = useRef();
|
|
157
|
+
const isStaticPreview = useContext(StaticPreviewContext);
|
|
158
|
+
const mode = useStorylineActivity();
|
|
159
|
+
const shouldLoad = useOnScreen(ref, {
|
|
160
|
+
rootMargin: '200% 0px 200% 0px'
|
|
161
|
+
});
|
|
162
|
+
const shouldPrepare = useOnScreen(ref, {
|
|
163
|
+
rootMargin: '25% 0px 25% 0px'
|
|
164
|
+
}) && !isStaticPreview;
|
|
165
|
+
|
|
166
|
+
// Sections that enter with fade transition only become visible
|
|
167
|
+
// once they reach the center of the viewport. We want to reflect
|
|
168
|
+
// that in `isVisible`/`onVisible` to prevent background videos
|
|
169
|
+
// from starting too soon. Since fade section might still exit
|
|
170
|
+
// with a scroll transition, we want to keep `isVisible` true
|
|
171
|
+
// until the section has completely left the viewport. We do not
|
|
172
|
+
// care about when exactly a background video pauses.
|
|
173
|
+
//
|
|
174
|
+
// Note that with fade transitions sections actually stay visible
|
|
175
|
+
// a bit longer while they are still fading out. This is handled
|
|
176
|
+
// by `isVisibleWithDelay` below.
|
|
177
|
+
const shouldBeVisible = useOnScreen(ref, {
|
|
178
|
+
rootMargin: entersWithFadeTransition ? '0px 0px -50% 0px' : undefined
|
|
179
|
+
}) && !isStaticPreview;
|
|
180
|
+
const shouldBeActive = useOnScreen(isActiveProbeRef, {
|
|
181
|
+
rootMargin: '-50% 0px -50% 0px',
|
|
182
|
+
onIntersecting: onActivate
|
|
183
|
+
}) && !isStaticPreview;
|
|
184
|
+
|
|
185
|
+
// useDelayedBoolean causes an extra render once the delay has
|
|
186
|
+
// elapsed. When entersWithFadeTransition is false,
|
|
187
|
+
// isVisibleWithDelay is never used, though. Since hooks can not
|
|
188
|
+
// be wrapped in conditionals, we ensure that the value passed to
|
|
189
|
+
// useDelayedBoolean is always false if entersWithFadeTransition
|
|
190
|
+
// is false. This prevents the extra render.
|
|
191
|
+
const isVisibleWithDelay = useDelayedBoolean(shouldBeVisible && entersWithFadeTransition, {
|
|
192
|
+
fromTrueToFalse: 1000
|
|
193
|
+
});
|
|
194
|
+
const isVisible = entersWithFadeTransition ? isVisibleWithDelay : shouldBeVisible;
|
|
195
|
+
// Elements in covered storylines are not visible.
|
|
196
|
+
// Elements in background storylines are visible (but not active).
|
|
197
|
+
// Elements in active storylines are both visible and active.
|
|
198
|
+
const isVisibleFinal = isVisible && mode !== 'covered';
|
|
199
|
+
const inForeground = mode === 'active';
|
|
200
|
+
|
|
201
|
+
// We want to make sure that `onActivate` is never called before
|
|
202
|
+
// `onVisible`, no matter in which order the intersection
|
|
203
|
+
// observers above fire.
|
|
204
|
+
const isActive = isVisibleFinal && shouldBeActive && inForeground;
|
|
205
|
+
const value = useMemo(() => ({
|
|
206
|
+
shouldLoad,
|
|
207
|
+
shouldPrepare,
|
|
208
|
+
isVisible: isVisibleFinal,
|
|
209
|
+
isActive,
|
|
210
|
+
inForeground
|
|
211
|
+
}), [shouldLoad, shouldPrepare, isVisibleFinal, isActive, inForeground]);
|
|
212
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
213
|
+
ref: ref,
|
|
214
|
+
className: classNames(styles.wrapper)
|
|
215
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
216
|
+
ref: isActiveProbeRef,
|
|
217
|
+
className: styles.isActiveProbe
|
|
218
|
+
}), /*#__PURE__*/React.createElement(Context.Provider, {
|
|
219
|
+
value: value
|
|
220
|
+
}, children));
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function createScrollPositionLifecycleHook(Context) {
|
|
224
|
+
return function useScrollPositionLifecycle({
|
|
225
|
+
onActivate,
|
|
226
|
+
onDeactivate,
|
|
227
|
+
onVisible,
|
|
228
|
+
onInvisible,
|
|
229
|
+
onEnterBackground,
|
|
230
|
+
onEnterForeground
|
|
231
|
+
} = {}) {
|
|
232
|
+
const result = useContext(Context);
|
|
233
|
+
const wasActive = useRef();
|
|
234
|
+
const wasVisible = useRef();
|
|
235
|
+
const wasForeground = useRef();
|
|
236
|
+
const {
|
|
237
|
+
isActive,
|
|
238
|
+
isVisible,
|
|
239
|
+
inForeground
|
|
240
|
+
} = result || {};
|
|
241
|
+
useEffect(() => {
|
|
242
|
+
if (!wasVisible.current && isVisible && onVisible) {
|
|
243
|
+
onVisible();
|
|
244
|
+
}
|
|
245
|
+
if (!wasActive.current && isActive && onActivate) {
|
|
246
|
+
onActivate();
|
|
247
|
+
} else if (wasActive.current && !isActive && onDeactivate) {
|
|
248
|
+
onDeactivate();
|
|
249
|
+
}
|
|
250
|
+
if (wasForeground.current && !inForeground && onEnterBackground) {
|
|
251
|
+
onEnterBackground();
|
|
252
|
+
} else if (!wasForeground.current && inForeground && onEnterForeground) {
|
|
253
|
+
onEnterForeground();
|
|
254
|
+
}
|
|
255
|
+
if (wasVisible.current && !isVisible && onInvisible) {
|
|
256
|
+
onInvisible();
|
|
257
|
+
}
|
|
258
|
+
wasActive.current = isActive;
|
|
259
|
+
wasVisible.current = isVisible;
|
|
260
|
+
wasForeground.current = inForeground;
|
|
261
|
+
});
|
|
262
|
+
return result;
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function extensible(name, Component) {
|
|
267
|
+
return function ExtensibleComponent(props) {
|
|
268
|
+
const isStaticPreview = useIsStaticPreview();
|
|
269
|
+
const extensions = useExtensions();
|
|
270
|
+
if (isStaticPreview) {
|
|
271
|
+
return /*#__PURE__*/React.createElement(Component, props);
|
|
272
|
+
}
|
|
273
|
+
const Alternative = extensions.alternatives[name];
|
|
274
|
+
if (Alternative) {
|
|
275
|
+
return /*#__PURE__*/React.createElement(Alternative, props);
|
|
276
|
+
}
|
|
277
|
+
const Decorator = extensions.decorators[name];
|
|
278
|
+
if (Decorator) {
|
|
279
|
+
return /*#__PURE__*/React.createElement(Decorator, props, /*#__PURE__*/React.createElement(Component, props));
|
|
280
|
+
}
|
|
281
|
+
return /*#__PURE__*/React.createElement(Component, props);
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function provideExtensions({
|
|
285
|
+
decorators: d,
|
|
286
|
+
alternatives: a
|
|
287
|
+
} = {}) {
|
|
288
|
+
decorators = d || {};
|
|
289
|
+
alternatives = a || {};
|
|
290
|
+
notifyListeners();
|
|
291
|
+
}
|
|
292
|
+
function ExtensionsProvider({
|
|
293
|
+
children
|
|
294
|
+
}) {
|
|
295
|
+
const [version, setVersion] = useState(0);
|
|
296
|
+
useEffect(() => subscribe(() => setVersion(v => v + 1)), []);
|
|
297
|
+
return /*#__PURE__*/React.createElement(ExtensionsContext.Provider, {
|
|
298
|
+
value: version
|
|
299
|
+
}, children);
|
|
300
|
+
}
|
|
301
|
+
let decorators = {};
|
|
302
|
+
let alternatives = {};
|
|
303
|
+
let listeners = [];
|
|
304
|
+
const ExtensionsContext = createContext(0);
|
|
305
|
+
function useExtensions() {
|
|
306
|
+
useContext(ExtensionsContext);
|
|
307
|
+
return {
|
|
308
|
+
decorators,
|
|
309
|
+
alternatives
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
function subscribe(listener) {
|
|
313
|
+
listeners = [...listeners, listener];
|
|
314
|
+
return () => {
|
|
315
|
+
listeners = listeners.filter(l => l !== listener);
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
function notifyListeners() {
|
|
319
|
+
listeners.forEach(l => l());
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Register new types of content elements.
|
|
324
|
+
* @name frontend_contentElementTypes
|
|
325
|
+
*/
|
|
326
|
+
class ContentElementTypeRegistry {
|
|
327
|
+
constructor() {
|
|
328
|
+
this.types = {};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Register a new type of content element.
|
|
333
|
+
*
|
|
334
|
+
* @param {string} typeName - Name of the content element type.
|
|
335
|
+
* @param {Object} options
|
|
336
|
+
* @param {React.Component} options.component
|
|
337
|
+
* @param {boolean} [options.supportsWrappingAroundFloats] -
|
|
338
|
+
* In sections with centered layout, content elements can be
|
|
339
|
+
* floated to the left or right. By default all content
|
|
340
|
+
* elements are cleared to position them below floating
|
|
341
|
+
* elements. If a content element renders mainly text that
|
|
342
|
+
* can wrap around floating elements, clearing can be
|
|
343
|
+
* disabled via this option.
|
|
344
|
+
* @memberof frontend_contentElementTypes
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
*
|
|
348
|
+
* // frontend.js
|
|
349
|
+
*
|
|
350
|
+
* import {frontend} from 'pageflow-scrolled/frontend';
|
|
351
|
+
* import {InlineImage} from './InlineImage';
|
|
352
|
+
*
|
|
353
|
+
* frontend.contentElementTypes.register('inlineImage', {
|
|
354
|
+
* component: InlineImage
|
|
355
|
+
* });
|
|
356
|
+
*/
|
|
357
|
+
register(typeName, options) {
|
|
358
|
+
this.types[typeName] = options;
|
|
359
|
+
}
|
|
360
|
+
getComponent(typeName) {
|
|
361
|
+
return this.types[typeName] && this.types[typeName].component;
|
|
362
|
+
}
|
|
363
|
+
getOptions(typeName) {
|
|
364
|
+
return this.types[typeName];
|
|
365
|
+
}
|
|
366
|
+
consentVendors({
|
|
367
|
+
contentElements,
|
|
368
|
+
t
|
|
369
|
+
}) {
|
|
370
|
+
const vendorsByName = {};
|
|
371
|
+
contentElements.forEach(contentElement => {
|
|
372
|
+
const type = this.types[contentElement.typeName];
|
|
373
|
+
const consentVendors = typeof type.consentVendors === 'function' ? type.consentVendors({
|
|
374
|
+
configuration: contentElement.configuration,
|
|
375
|
+
t
|
|
376
|
+
}) : type.consentVendors || [];
|
|
377
|
+
consentVendors.forEach(vendor => {
|
|
378
|
+
vendorsByName[vendor.name] = vendor;
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
return Object.values(vendorsByName);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Register new types of widgets.
|
|
387
|
+
* @name frontend_widgetTypes
|
|
388
|
+
*/
|
|
389
|
+
class WidgetTypeRegistry {
|
|
390
|
+
constructor() {
|
|
391
|
+
this.types = {};
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Register a new type of widget.
|
|
396
|
+
*
|
|
397
|
+
* @param {string} typeName - Name of the content element type.
|
|
398
|
+
* @param {Object} options
|
|
399
|
+
* @param {React.Component} options.component
|
|
400
|
+
* @memberof frontend_widgetTypes
|
|
401
|
+
*
|
|
402
|
+
* @example
|
|
403
|
+
*
|
|
404
|
+
* // frontend.js
|
|
405
|
+
*
|
|
406
|
+
* import {frontend} from 'pageflow-scrolled/frontend';
|
|
407
|
+
* import {CustomNavigationBar} from './CustomNavigationBar';
|
|
408
|
+
*
|
|
409
|
+
* frontend.contentElementTypes.register('customNavigationBar', {
|
|
410
|
+
* component: CustomNavigationBar
|
|
411
|
+
* });
|
|
412
|
+
*/
|
|
413
|
+
register(typeName, options) {
|
|
414
|
+
if (!options.component) {
|
|
415
|
+
throw new Error(`Missing required component option for widget type '${typeName}'.`);
|
|
416
|
+
}
|
|
417
|
+
this.types[typeName] = options;
|
|
418
|
+
}
|
|
419
|
+
getComponent(typeName) {
|
|
420
|
+
if (!this.types[typeName]) {
|
|
421
|
+
throw new Error(`Unknown widget type '${typeName}'. Consider calling frontent.widgetTypes.register.`);
|
|
422
|
+
}
|
|
423
|
+
return this.types[typeName].component;
|
|
424
|
+
}
|
|
425
|
+
getPresenceProvider(typeName) {
|
|
426
|
+
var _this$types$typeName;
|
|
427
|
+
return (_this$types$typeName = this.types[typeName]) === null || _this$types$typeName === void 0 ? void 0 : _this$types$typeName.presenceProvider;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const api = {
|
|
432
|
+
contentElementTypes: new ContentElementTypeRegistry(),
|
|
433
|
+
widgetTypes: new WidgetTypeRegistry(),
|
|
434
|
+
/**
|
|
435
|
+
* Custom error boundary component to wrap content elements.
|
|
436
|
+
* Allows integration with error monitoring tools. The component receives
|
|
437
|
+
* typeName (string), configuration (object), fallback (function returning
|
|
438
|
+
* default UI), and children (content element).
|
|
439
|
+
*
|
|
440
|
+
* @name frontend_contentElementErrorBoundary
|
|
441
|
+
* @type {React.Component}
|
|
442
|
+
*/
|
|
443
|
+
contentElementErrorBoundary: undefined
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const ContentElementAttributesContext = createContext({});
|
|
447
|
+
function ContentElementAttributesProvider({
|
|
448
|
+
id,
|
|
449
|
+
permaId,
|
|
450
|
+
width,
|
|
451
|
+
position,
|
|
452
|
+
inlineComments,
|
|
453
|
+
children
|
|
454
|
+
}) {
|
|
455
|
+
const attributes = useMemo(() => ({
|
|
456
|
+
contentElementId: id,
|
|
457
|
+
contentElementPermaId: permaId,
|
|
458
|
+
width,
|
|
459
|
+
position,
|
|
460
|
+
inlineComments
|
|
461
|
+
}), [id, permaId, width, position, inlineComments]);
|
|
462
|
+
return /*#__PURE__*/React.createElement(ContentElementAttributesContext.Provider, {
|
|
463
|
+
value: attributes
|
|
464
|
+
}, children);
|
|
465
|
+
}
|
|
466
|
+
function useContentElementAttributes() {
|
|
467
|
+
return useContext(ContentElementAttributesContext);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const ContentElementLifecycleContext = createContext();
|
|
471
|
+
const LifecycleProvider = createScrollPositionLifecycleProvider(ContentElementLifecycleContext);
|
|
472
|
+
const useLifecycle = createScrollPositionLifecycleHook(ContentElementLifecycleContext);
|
|
473
|
+
function ContentElementLifecycleProvider({
|
|
474
|
+
type,
|
|
475
|
+
children,
|
|
476
|
+
override
|
|
477
|
+
}) {
|
|
478
|
+
const {
|
|
479
|
+
lifecycle
|
|
480
|
+
} = api.contentElementTypes.getOptions(type);
|
|
481
|
+
if (override) {
|
|
482
|
+
return /*#__PURE__*/React.createElement(ContentElementLifecycleContext.Provider, {
|
|
483
|
+
value: override,
|
|
484
|
+
children: children
|
|
485
|
+
});
|
|
486
|
+
} else if (lifecycle) {
|
|
487
|
+
return /*#__PURE__*/React.createElement(LifecycleProvider, null, children);
|
|
488
|
+
} else {
|
|
489
|
+
return children;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Returns an object containing information about the scroll position
|
|
495
|
+
* related lifecylce of the content element. Requires the `lifecycle`
|
|
496
|
+
* option to be set to true in the `frontend.contentElements.register`
|
|
497
|
+
* call for the content element's type.
|
|
498
|
+
*
|
|
499
|
+
* * `shouldLoad` is true if the content element should start lazy
|
|
500
|
+
* load. Becomes true before `shouldPrepare`.
|
|
501
|
+
*
|
|
502
|
+
* * `shouldPrepare` is true if the content element is about to enter
|
|
503
|
+
* the viewport.
|
|
504
|
+
*
|
|
505
|
+
* * `isActive` is true if the content element is completely in the
|
|
506
|
+
* viewport.
|
|
507
|
+
*
|
|
508
|
+
* @param {Function} onActivate -
|
|
509
|
+
* Invoked when content element has entered the viewport.
|
|
510
|
+
*
|
|
511
|
+
* @param {Function} onDeactivate -
|
|
512
|
+
* Invoked when content element has left the viewport.
|
|
513
|
+
*
|
|
514
|
+
* @example
|
|
515
|
+
*
|
|
516
|
+
* const {isActive, shouldPrepare} = useContentElementLifecycle();
|
|
517
|
+
*/
|
|
518
|
+
function useContentElementLifecycle(options) {
|
|
519
|
+
const result = useLifecycle(options);
|
|
520
|
+
if (!result) {
|
|
521
|
+
throw new Error('useContentElementLifecycle is only available in ' + 'content elements for which `lifecycle: true` has ' + 'been passed to frontend.contentElements.register');
|
|
522
|
+
}
|
|
523
|
+
return result;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Offsets of the subject's top edge relative to the viewport's top
|
|
527
|
+
// edge at the milestones of a content element's view timeline.
|
|
528
|
+
//
|
|
529
|
+
// Elements that are pinned in the viewport for part of their scroll
|
|
530
|
+
// space are measured along a taller subject: They move with the
|
|
531
|
+
// subject's top edge until they are pinned and continue with the
|
|
532
|
+
// subject's bottom edge once they have been.
|
|
533
|
+
const milestones = {
|
|
534
|
+
firstVisible: ({
|
|
535
|
+
viewportHeight
|
|
536
|
+
}) => viewportHeight,
|
|
537
|
+
firstContained: ({
|
|
538
|
+
viewportHeight,
|
|
539
|
+
elementHeight
|
|
540
|
+
}) => viewportHeight - elementHeight,
|
|
541
|
+
reachesCenter: ({
|
|
542
|
+
viewportHeight
|
|
543
|
+
}) => viewportHeight / 2,
|
|
544
|
+
leavesCenter: ({
|
|
545
|
+
subjectHeight,
|
|
546
|
+
viewportHeight
|
|
547
|
+
}) => viewportHeight / 2 - subjectHeight,
|
|
548
|
+
lastContained: ({
|
|
549
|
+
subjectHeight,
|
|
550
|
+
elementHeight
|
|
551
|
+
}) => elementHeight - subjectHeight,
|
|
552
|
+
lastVisible: ({
|
|
553
|
+
subjectHeight
|
|
554
|
+
}) => -subjectHeight,
|
|
555
|
+
// The pinned position is only known while the element actually is
|
|
556
|
+
// pinned. That is exactly when progress along the pinned range is
|
|
557
|
+
// between 0 and 1, though: Before, the element's top edge coincides
|
|
558
|
+
// with the subject's, after, its bottom edge does. Both edges of the
|
|
559
|
+
// range therefore come out equally far off in those phases, which
|
|
560
|
+
// makes progress clamp to 0 respectively 1.
|
|
561
|
+
reachesPinnedPosition: ({
|
|
562
|
+
elementTop
|
|
563
|
+
}) => elementTop,
|
|
564
|
+
leavesPinnedPosition: ({
|
|
565
|
+
elementTop,
|
|
566
|
+
subjectHeight,
|
|
567
|
+
elementHeight
|
|
568
|
+
}) => elementTop - subjectHeight + elementHeight
|
|
569
|
+
};
|
|
570
|
+
const ranges = {
|
|
571
|
+
// Mirror the named ranges of CSS scroll driven animations.
|
|
572
|
+
cover: ['firstVisible', 'lastVisible'],
|
|
573
|
+
contain: ['firstContained', 'lastContained'],
|
|
574
|
+
entry: ['firstVisible', 'firstContained'],
|
|
575
|
+
exit: ['lastContained', 'lastVisible'],
|
|
576
|
+
// Same part of the page during which content elements become active
|
|
577
|
+
// and autoplayed videos play.
|
|
578
|
+
center: ['reachesCenter', 'leavesCenter'],
|
|
579
|
+
// Only elements that components like TwoColumn or
|
|
580
|
+
// ContentElementScrollSpace pin in the viewport have a pinned phase.
|
|
581
|
+
pinned: ['reachesPinnedPosition', 'leavesPinnedPosition']
|
|
582
|
+
};
|
|
583
|
+
const rangeAliases = {
|
|
584
|
+
// Elements that are pinned in the viewport hold the reader's
|
|
585
|
+
// attention while they stay in place. Elements that are not pinned
|
|
586
|
+
// at all do so while they pass the center of the viewport.
|
|
587
|
+
inFocus: hasPinnedPhase => hasPinnedPhase ? 'pinned' : 'center'
|
|
588
|
+
};
|
|
589
|
+
function getViewTimelineProgress({
|
|
590
|
+
range,
|
|
591
|
+
subjectRect,
|
|
592
|
+
elementRect,
|
|
593
|
+
viewportHeight
|
|
594
|
+
}) {
|
|
595
|
+
const hasPinnedPhase = subjectRect.height > elementRect.height;
|
|
596
|
+
const alias = rangeAliases[range];
|
|
597
|
+
const milestoneNames = ranges[alias ? alias(hasPinnedPhase) : range];
|
|
598
|
+
if (!milestoneNames) {
|
|
599
|
+
const supportedRanges = [...Object.keys(ranges), ...Object.keys(rangeAliases)];
|
|
600
|
+
throw new Error(`Unknown view timeline range '${range}'. ` + `Supported ranges: ${supportedRanges.join(', ')}.`);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Without enough content next to it, a pinned element never reaches
|
|
604
|
+
// its pinned position and keeps moving with the page. Its own rect
|
|
605
|
+
// then is the subject covering the same range of the page.
|
|
606
|
+
const subject = hasPinnedPhase ? subjectRect : elementRect;
|
|
607
|
+
const [start, end] = orderEdges(milestoneNames.map(name => milestones[name]({
|
|
608
|
+
subjectHeight: subject.height,
|
|
609
|
+
elementTop: elementRect.top,
|
|
610
|
+
elementHeight: elementRect.height,
|
|
611
|
+
viewportHeight
|
|
612
|
+
})));
|
|
613
|
+
if (start === end) {
|
|
614
|
+
return subject.top <= start ? 1 : 0;
|
|
615
|
+
}
|
|
616
|
+
return clamp((start - subject.top) / (start - end));
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Milestones come out in reverse order for elements taller than the
|
|
620
|
+
// viewport: Such elements cover the viewport instead of being
|
|
621
|
+
// contained in it, so they stop being contained before they start
|
|
622
|
+
// being contained.
|
|
623
|
+
function orderEdges([start, end]) {
|
|
624
|
+
return start < end ? [end, start] : [start, end];
|
|
625
|
+
}
|
|
626
|
+
function clamp(value) {
|
|
627
|
+
return Math.min(Math.max(value, 0), 1);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const ContentElementViewTimelineContext = createContext();
|
|
631
|
+
|
|
632
|
+
// Lets components that pin content elements in the viewport for part
|
|
633
|
+
// of their scroll space pass a function returning the pinned element
|
|
634
|
+
// and the subject that keeps moving with the page. Pinned content
|
|
635
|
+
// elements would otherwise stop making progress along their view
|
|
636
|
+
// timeline for exactly the part of the page that the extra scroll
|
|
637
|
+
// space was added for.
|
|
638
|
+
//
|
|
639
|
+
// Passing a function instead of refs lets components resolve elements
|
|
640
|
+
// they do not render themselves: Sticky boxes in TwoColumn walk up the
|
|
641
|
+
// DOM to find their group.
|
|
642
|
+
const ViewTimelinePinContext = createContext();
|
|
643
|
+
function ViewTimelinePinProvider({
|
|
644
|
+
getPinnedElements,
|
|
645
|
+
children
|
|
646
|
+
}) {
|
|
647
|
+
return /*#__PURE__*/React.createElement(ViewTimelinePinContext.Provider, {
|
|
648
|
+
value: getPinnedElements,
|
|
649
|
+
children: children
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
function ContentElementViewTimelineProvider({
|
|
653
|
+
type,
|
|
654
|
+
children
|
|
655
|
+
}) {
|
|
656
|
+
const {
|
|
657
|
+
viewTimeline
|
|
658
|
+
} = api.contentElementTypes.getOptions(type);
|
|
659
|
+
if (viewTimeline) {
|
|
660
|
+
return /*#__PURE__*/React.createElement(ViewTimelineProvider, null, children);
|
|
661
|
+
} else {
|
|
662
|
+
return children;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
function ViewTimelineProvider({
|
|
666
|
+
children
|
|
667
|
+
}) {
|
|
668
|
+
const getPinnedElements = useContext(ViewTimelinePinContext);
|
|
669
|
+
const ownElementRef = useRef();
|
|
670
|
+
const subscriptionsRef = useRef(new Set());
|
|
671
|
+
|
|
672
|
+
// Content element types can support view timelines without always
|
|
673
|
+
// observing scroll position. Only listen while there are
|
|
674
|
+
// subscriptions to prevent each of them from adding a handler.
|
|
675
|
+
const [hasSubscriptions, setHasSubscriptions] = useState(false);
|
|
676
|
+
const getElements = useCallback(() => getPinnedElements ? getPinnedElements() : {
|
|
677
|
+
subject: ownElementRef.current,
|
|
678
|
+
element: ownElementRef.current
|
|
679
|
+
}, [getPinnedElements]);
|
|
680
|
+
const viewTimeline = useMemo(() => ({
|
|
681
|
+
subscribe(range, callback) {
|
|
682
|
+
const subscription = {
|
|
683
|
+
range,
|
|
684
|
+
callback
|
|
685
|
+
};
|
|
686
|
+
subscriptionsRef.current.add(subscription);
|
|
687
|
+
setHasSubscriptions(true);
|
|
688
|
+
update(getElements(), [subscription]);
|
|
689
|
+
return () => {
|
|
690
|
+
subscriptionsRef.current.delete(subscription);
|
|
691
|
+
setHasSubscriptions(subscriptionsRef.current.size > 0);
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
}), [getElements]);
|
|
695
|
+
useEffect(() => {
|
|
696
|
+
if (!hasSubscriptions) {
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
const subscriptions = subscriptionsRef.current;
|
|
700
|
+
let animationFrame;
|
|
701
|
+
function handle() {
|
|
702
|
+
if (animationFrame) {
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
animationFrame = requestAnimationFrame(() => {
|
|
706
|
+
animationFrame = null;
|
|
707
|
+
update(getElements(), subscriptions);
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
window.addEventListener('scroll', handle);
|
|
711
|
+
window.addEventListener('resize', handle);
|
|
712
|
+
return () => {
|
|
713
|
+
cancelAnimationFrame(animationFrame);
|
|
714
|
+
window.removeEventListener('scroll', handle);
|
|
715
|
+
window.removeEventListener('resize', handle);
|
|
716
|
+
};
|
|
717
|
+
}, [getElements, hasSubscriptions]);
|
|
718
|
+
const content = /*#__PURE__*/React.createElement(ContentElementViewTimelineContext.Provider, {
|
|
719
|
+
value: viewTimeline
|
|
720
|
+
}, children);
|
|
721
|
+
if (getPinnedElements) {
|
|
722
|
+
return content;
|
|
723
|
+
}
|
|
724
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
725
|
+
ref: ownElementRef
|
|
726
|
+
}, content);
|
|
727
|
+
}
|
|
728
|
+
function update({
|
|
729
|
+
subject,
|
|
730
|
+
element
|
|
731
|
+
}, subscriptions) {
|
|
732
|
+
const subjectRect = subject.getBoundingClientRect();
|
|
733
|
+
const elementRect = element === subject ? subjectRect : element.getBoundingClientRect();
|
|
734
|
+
const viewportHeight = window.innerHeight;
|
|
735
|
+
subscriptions.forEach(subscription => {
|
|
736
|
+
const progress = getViewTimelineProgress({
|
|
737
|
+
range: subscription.range,
|
|
738
|
+
subjectRect,
|
|
739
|
+
elementRect,
|
|
740
|
+
viewportHeight
|
|
741
|
+
});
|
|
742
|
+
if (progress !== subscription.lastProgress) {
|
|
743
|
+
subscription.lastProgress = progress;
|
|
744
|
+
subscription.callback(progress);
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Invokes a callback with the progress of the content element along a
|
|
751
|
+
* range of its view timeline. Mirrors the concepts of CSS scroll
|
|
752
|
+
* driven animations: The content element acts as the subject of a
|
|
753
|
+
* view timeline of the page's scroll container. Requires the
|
|
754
|
+
* `viewTimeline` option to be set to true in the
|
|
755
|
+
* `frontend.contentElementTypes.register` call for the content
|
|
756
|
+
* element's type.
|
|
757
|
+
*
|
|
758
|
+
* Progress is passed to a callback instead of being returned to
|
|
759
|
+
* prevent rerendering the content element on every scroll frame.
|
|
760
|
+
*
|
|
761
|
+
* @param {Object} options
|
|
762
|
+
*
|
|
763
|
+
* @param {string} [options.range='cover'] -
|
|
764
|
+
* Which part of the content element's view timeline to measure:
|
|
765
|
+
*
|
|
766
|
+
* * `cover`: From the moment the content element starts entering
|
|
767
|
+
* the viewport until it has completely left it.
|
|
768
|
+
*
|
|
769
|
+
* * `contain`: While the content element is completely inside the
|
|
770
|
+
* viewport. For content elements taller than the viewport, while
|
|
771
|
+
* the content element completely covers the viewport.
|
|
772
|
+
*
|
|
773
|
+
* * `entry`: While the content element is entering the viewport.
|
|
774
|
+
*
|
|
775
|
+
* * `exit`: While the content element is leaving the viewport.
|
|
776
|
+
*
|
|
777
|
+
* * `center`: While the content element intersects the vertical
|
|
778
|
+
* center of the viewport, i.e. from its top edge passing the
|
|
779
|
+
* center until its bottom edge does.
|
|
780
|
+
*
|
|
781
|
+
* * `pinned`: While the content element stays pinned in the
|
|
782
|
+
* viewport, i.e. from the moment it reaches the position it is
|
|
783
|
+
* pinned at until it starts moving with the page again. Progress
|
|
784
|
+
* stays 1 for content elements that are not pinned at all.
|
|
785
|
+
*
|
|
786
|
+
* * `inFocus`: While the content element holds the reader's
|
|
787
|
+
* attention: `pinned` for content elements that are pinned in the
|
|
788
|
+
* viewport, `center` for all others.
|
|
789
|
+
*
|
|
790
|
+
* @param {Function} [options.onProgress] -
|
|
791
|
+
* Invoked with a number between 0 and 1 whenever progress along the
|
|
792
|
+
* range changes. Pass a falsy value to not observe scroll position
|
|
793
|
+
* at all.
|
|
794
|
+
*
|
|
795
|
+
* @example
|
|
796
|
+
*
|
|
797
|
+
* useContentElementViewTimelineProgress({
|
|
798
|
+
* range: 'cover',
|
|
799
|
+
* onProgress: progress => player.seekTo(progress)
|
|
800
|
+
* });
|
|
801
|
+
*/
|
|
802
|
+
function useContentElementViewTimelineProgress({
|
|
803
|
+
range = 'cover',
|
|
804
|
+
onProgress
|
|
805
|
+
} = {}) {
|
|
806
|
+
const viewTimeline = useContext(ContentElementViewTimelineContext);
|
|
807
|
+
const onProgressRef = useRef();
|
|
808
|
+
onProgressRef.current = onProgress;
|
|
809
|
+
const enabled = !!onProgress;
|
|
810
|
+
useEffect(() => {
|
|
811
|
+
if (viewTimeline && enabled) {
|
|
812
|
+
return viewTimeline.subscribe(range, progress => onProgressRef.current(progress));
|
|
813
|
+
}
|
|
814
|
+
}, [viewTimeline, range, enabled]);
|
|
815
|
+
if (!viewTimeline) {
|
|
816
|
+
throw new Error('useContentElementViewTimelineProgress is only available in ' + 'content elements for which `viewTimeline: true` has ' + 'been passed to frontend.contentElementTypes.register');
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
const widths = {
|
|
821
|
+
xxs: -3,
|
|
822
|
+
xs: -2,
|
|
823
|
+
sm: -1,
|
|
824
|
+
md: 0,
|
|
825
|
+
lg: 1,
|
|
826
|
+
xl: 2,
|
|
827
|
+
full: 3
|
|
828
|
+
};
|
|
829
|
+
function widthName(width) {
|
|
830
|
+
return Object.keys(widths)[(width || 0) + 3];
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
const TrimDefaultMarginTopContext = createContext(false);
|
|
834
|
+
const TrimDefaultMarginTop = TrimDefaultMarginTopContext.Provider;
|
|
835
|
+
function useTrimDefaultMarginTop() {
|
|
836
|
+
return useContext(TrimDefaultMarginTopContext);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
var styles$1 = {"wrapper":"ContentElementMargin-module_wrapper__20kIk","noTopMargin":"ContentElementMargin-module_noTopMargin__mB7t3"};
|
|
840
|
+
|
|
841
|
+
function ContentElementMargin({
|
|
842
|
+
width,
|
|
843
|
+
first,
|
|
844
|
+
defaultMarginTop,
|
|
845
|
+
top,
|
|
846
|
+
bottom,
|
|
847
|
+
previousBottom,
|
|
848
|
+
children
|
|
849
|
+
}) {
|
|
850
|
+
const trimDefaultMarginTop = useTrimDefaultMarginTop();
|
|
851
|
+
if (width === widths.full) {
|
|
852
|
+
return children;
|
|
853
|
+
}
|
|
854
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
855
|
+
className: classNames(styles$1.wrapper, {
|
|
856
|
+
[styles$1.noTopMargin]: trimDefaultMarginTop && first && !top
|
|
857
|
+
}),
|
|
858
|
+
style: {
|
|
859
|
+
'--margin-top': scaleProperty(top) || defaultMarginTop,
|
|
860
|
+
'--margin-bottom': scaleProperty(bottom),
|
|
861
|
+
'--prev-margin-bottom': scaleProperty(previousBottom)
|
|
862
|
+
}
|
|
863
|
+
}, children);
|
|
864
|
+
}
|
|
865
|
+
function scaleProperty(value) {
|
|
866
|
+
return value && `var(--theme-content-element-margin-${value})`;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
var styles$2 = {"missing":"ContentElement-module_missing__2_1j9"};
|
|
870
|
+
|
|
871
|
+
function ContentElementErrorBoundary({
|
|
872
|
+
typeName,
|
|
873
|
+
configuration,
|
|
874
|
+
children
|
|
875
|
+
}) {
|
|
876
|
+
const ErrorBoundary = api.contentElementErrorBoundary || DefaultErrorBoundary;
|
|
877
|
+
const fallback = () => /*#__PURE__*/React.createElement(DefaultFallback, {
|
|
878
|
+
type: typeName
|
|
879
|
+
});
|
|
880
|
+
return /*#__PURE__*/React.createElement(ErrorBoundary, {
|
|
881
|
+
typeName: typeName,
|
|
882
|
+
configuration: configuration,
|
|
883
|
+
fallback: fallback
|
|
884
|
+
}, children);
|
|
885
|
+
}
|
|
886
|
+
class DefaultErrorBoundary extends React.Component {
|
|
887
|
+
constructor(props) {
|
|
888
|
+
super(props);
|
|
889
|
+
this.state = {
|
|
890
|
+
hasError: false
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
static getDerivedStateFromError(error) {
|
|
894
|
+
return {
|
|
895
|
+
hasError: true
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
render() {
|
|
899
|
+
if (this.state.hasError) {
|
|
900
|
+
return this.props.fallback();
|
|
901
|
+
}
|
|
902
|
+
return this.props.children;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
function DefaultFallback({
|
|
906
|
+
type
|
|
907
|
+
}) {
|
|
908
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
909
|
+
className: styles$2.missing
|
|
910
|
+
}, "Error rendering element of type \"", type, "\"");
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
const ContentElement = React.memo(extensible('ContentElement', function ContentElement(props) {
|
|
914
|
+
const Component = api.contentElementTypes.getComponent(props.type);
|
|
915
|
+
const {
|
|
916
|
+
defaultMarginTop,
|
|
917
|
+
inlineComments
|
|
918
|
+
} = api.contentElementTypes.getOptions(props.type) || {};
|
|
919
|
+
if (Component) {
|
|
920
|
+
return /*#__PURE__*/React.createElement(ContentElementAttributesProvider, {
|
|
921
|
+
id: props.id,
|
|
922
|
+
permaId: props.permaId,
|
|
923
|
+
width: props.width,
|
|
924
|
+
position: props.position,
|
|
925
|
+
inlineComments: inlineComments
|
|
926
|
+
}, /*#__PURE__*/React.createElement(ContentElementLifecycleProvider, {
|
|
927
|
+
type: props.type,
|
|
928
|
+
override: props.lifecycleOverride
|
|
929
|
+
}, /*#__PURE__*/React.createElement(ContentElementMargin, {
|
|
930
|
+
width: props.width,
|
|
931
|
+
first: props.first,
|
|
932
|
+
defaultMarginTop: defaultMarginTop,
|
|
933
|
+
top: props.itemProps.marginTop,
|
|
934
|
+
bottom: props.marginBottom,
|
|
935
|
+
previousBottom: props.previousMarginBottom
|
|
936
|
+
}, /*#__PURE__*/React.createElement(ContentElementViewTimelineProvider, {
|
|
937
|
+
type: props.type
|
|
938
|
+
}, /*#__PURE__*/React.createElement(ContentElementErrorBoundary, {
|
|
939
|
+
typeName: props.type,
|
|
940
|
+
configuration: props.itemProps
|
|
941
|
+
}, /*#__PURE__*/React.createElement(Component, {
|
|
942
|
+
sectionProps: props.sectionProps,
|
|
943
|
+
customMargin: props.customMargin,
|
|
944
|
+
configuration: props.itemProps,
|
|
945
|
+
contentElementWidth: props.width,
|
|
946
|
+
contentElementId: props.id
|
|
947
|
+
}))))));
|
|
948
|
+
} else {
|
|
949
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
950
|
+
className: styles$2.missing
|
|
951
|
+
}, "Element of unknown type \"", props.type, "\"");
|
|
952
|
+
}
|
|
953
|
+
}), arePropsEqual);
|
|
954
|
+
function arePropsEqual(prevProps, nextProps) {
|
|
955
|
+
return prevProps.id === nextProps.id && prevProps.permaId === nextProps.permaId && prevProps.type === nextProps.type && prevProps.first === nextProps.first && prevProps.position === nextProps.position && prevProps.width === nextProps.width && prevProps.itemProps === nextProps.itemProps && prevProps.marginBottom === nextProps.marginBottom && prevProps.previousMarginBottom === nextProps.previousMarginBottom && prevProps.customMargin === nextProps.customMargin && prevProps.sectionProps === nextProps.sectionProps && prevProps.lifecycleOverride === nextProps.lifecycleOverride;
|
|
956
|
+
}
|
|
957
|
+
ContentElement.defaultProps = {
|
|
958
|
+
itemProps: {}
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
var styles$3 = {"wrapper":"ContentElementScrollSpace-module_wrapper__2ZBwZ","inner":"ContentElementScrollSpace-module_inner__1FBgh"};
|
|
962
|
+
|
|
963
|
+
function ContentElementScrollSpace({
|
|
964
|
+
children
|
|
965
|
+
}) {
|
|
966
|
+
const ref = useRef();
|
|
967
|
+
const innerRef = useRef();
|
|
968
|
+
const getPinnedElements = useCallback(() => ({
|
|
969
|
+
subject: ref.current,
|
|
970
|
+
element: innerRef.current
|
|
971
|
+
}), []);
|
|
972
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
973
|
+
className: styles$3.wrapper,
|
|
974
|
+
ref: ref
|
|
975
|
+
}, /*#__PURE__*/React.createElement(ViewTimelinePinProvider, {
|
|
976
|
+
getPinnedElements: getPinnedElements
|
|
977
|
+
}, /*#__PURE__*/React.createElement(Measure, {
|
|
978
|
+
bounds: true,
|
|
979
|
+
innerRef: innerRef
|
|
980
|
+
}, ({
|
|
981
|
+
measureRef,
|
|
982
|
+
contentRect
|
|
983
|
+
}) => /*#__PURE__*/React.createElement("div", {
|
|
984
|
+
ref: measureRef,
|
|
985
|
+
className: styles$3.inner,
|
|
986
|
+
style: {
|
|
987
|
+
'--height': contentRect.bounds.height / 2
|
|
988
|
+
}
|
|
989
|
+
}, children))));
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
function ContentElements(props) {
|
|
993
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, props.items.map((item, index) => props.children(item, renderScrollSpace(item, /*#__PURE__*/React.createElement(ContentElement, {
|
|
994
|
+
key: item.id,
|
|
995
|
+
id: item.id,
|
|
996
|
+
permaId: item.permaId,
|
|
997
|
+
type: item.type,
|
|
998
|
+
first: index === 0,
|
|
999
|
+
position: item.position,
|
|
1000
|
+
width: item.width,
|
|
1001
|
+
itemProps: item.props,
|
|
1002
|
+
marginBottom: item.marginBottom,
|
|
1003
|
+
previousMarginBottom: item.previousMarginBottom,
|
|
1004
|
+
customMargin: props.customMargin,
|
|
1005
|
+
sectionProps: props.sectionProps
|
|
1006
|
+
})), index)));
|
|
1007
|
+
}
|
|
1008
|
+
ContentElements.defaultProps = {
|
|
1009
|
+
children: (item, child) => child
|
|
1010
|
+
};
|
|
1011
|
+
function renderScrollSpace(item, children) {
|
|
1012
|
+
if (!item.standAlone) {
|
|
1013
|
+
return children;
|
|
1014
|
+
}
|
|
1015
|
+
return /*#__PURE__*/React.createElement(ContentElementScrollSpace, {
|
|
1016
|
+
key: item.id,
|
|
1017
|
+
children: children
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
function useMediaQuery(query, {
|
|
1022
|
+
active
|
|
1023
|
+
} = {
|
|
1024
|
+
active: true
|
|
1025
|
+
}) {
|
|
1026
|
+
const [doesMatch, setDoesMatch] = useState(false);
|
|
1027
|
+
useEffect(() => {
|
|
1028
|
+
if (!active) {
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
const onUpdateMatch = ({
|
|
1032
|
+
matches
|
|
1033
|
+
}) => {
|
|
1034
|
+
setDoesMatch(matches);
|
|
1035
|
+
};
|
|
1036
|
+
const matcher = window.matchMedia(query);
|
|
1037
|
+
matcher.addEventListener('change', onUpdateMatch);
|
|
1038
|
+
onUpdateMatch(matcher);
|
|
1039
|
+
return () => {
|
|
1040
|
+
matcher.removeEventListener('change', onUpdateMatch);
|
|
1041
|
+
};
|
|
1042
|
+
}, [query, setDoesMatch, active]);
|
|
1043
|
+
return active && doesMatch;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
var styles$4 = {"root":"TwoColumn-module_root__37EqL","group":"TwoColumn-module_group__3Hg2y","group-full":"TwoColumn-module_group-full__2OT4o","box":"TwoColumn-module_box__1Nils","inline":"TwoColumn-module_inline__1fPfM","width-lg":"TwoColumn-module_width-lg__2MD35","width-xl":"TwoColumn-module_width-xl__3Bxet","width-full":"TwoColumn-module_width-full__1QWYO","constrainContentWidth":"TwoColumn-module_constrainContentWidth__1cxa-","group-lg":"TwoColumn-module_group-lg__xgaPx","restrict-xxs":"TwoColumn-module_restrict-xxs__6il-H","restrict-xs":"TwoColumn-module_restrict-xs__AOezq","restrict-sm":"TwoColumn-module_restrict-sm__2rKty","align-left":"TwoColumn-module_align-left__QSe2G","align-right":"TwoColumn-module_align-right__3Dn4i","customMargin":"TwoColumn-module_customMargin__o0uxH","right":"TwoColumn-module_right__Fr52a","side":"TwoColumn-module_side__2xx0s","sticky":"TwoColumn-module_sticky__4LCDO TwoColumn-module_side__2xx0s"};
|
|
1047
|
+
|
|
1048
|
+
function TwoColumn(props) {
|
|
1049
|
+
const shouldInline = useShouldInlineSticky();
|
|
1050
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1051
|
+
className: classNames(styles$4.root, styles$4[props.align], {
|
|
1052
|
+
[styles$4.constrainContentWidth]: props.constrainContentWidth
|
|
1053
|
+
})
|
|
1054
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1055
|
+
className: classNames(styles$4.group),
|
|
1056
|
+
key: props.align
|
|
1057
|
+
}, /*#__PURE__*/React.createElement("div", Object.assign({
|
|
1058
|
+
className: classNames(styles$4.box, styles$4.inline),
|
|
1059
|
+
ref: props.contentAreaRef
|
|
1060
|
+
}, TwoColumn.contentAreaProbeProps))), renderItems(props, shouldInline), renderPlaceholder(props.placeholder));
|
|
1061
|
+
}
|
|
1062
|
+
TwoColumn.defaultProps = {
|
|
1063
|
+
align: 'left'
|
|
1064
|
+
};
|
|
1065
|
+
function useShouldInlineSticky() {
|
|
1066
|
+
var _theme$options$proper;
|
|
1067
|
+
const theme = useTheme();
|
|
1068
|
+
const root = ((_theme$options$proper = theme.options.properties) === null || _theme$options$proper === void 0 ? void 0 : _theme$options$proper.root) || {};
|
|
1069
|
+
const shouldInline = {
|
|
1070
|
+
[widths.md]: useMediaQuery(`(max-width: ${root.twoColumnStickyBreakpoint || '950px'})`),
|
|
1071
|
+
[widths.lg]: useMediaQuery(`(max-width: ${root.twoColumnStickyLgBreakpoint || '1200px'})`),
|
|
1072
|
+
[widths.xl]: useMediaQuery(`(max-width: ${root.twoColumnStickyXlBreakpoint || '1400px'})`)
|
|
1073
|
+
};
|
|
1074
|
+
return function (width) {
|
|
1075
|
+
return width <= widths.md ? shouldInline[widths.md] : shouldInline[width];
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// Used in tests to render markers around groups
|
|
1080
|
+
TwoColumn.GroupComponent = 'div';
|
|
1081
|
+
|
|
1082
|
+
// Used to set data-testids on probe element
|
|
1083
|
+
TwoColumn.contentAreaProbeProps = {};
|
|
1084
|
+
function renderItems(props, shouldInline) {
|
|
1085
|
+
const groups = groupItemsByPosition(props.items, shouldInline, props.isContentPadded);
|
|
1086
|
+
return groups.map((group, groupIndex) => /*#__PURE__*/React.createElement(TwoColumn.GroupComponent, {
|
|
1087
|
+
key: groupIndex,
|
|
1088
|
+
className: classNames(styles$4.group, styles$4[`group-${widthName(group.width)}`])
|
|
1089
|
+
}, group.boxes.map((box, boxIndex) => renderItemGroup(props, box, boxIndex))));
|
|
1090
|
+
}
|
|
1091
|
+
function renderItemGroup(props, box, key) {
|
|
1092
|
+
if (box.items.length) {
|
|
1093
|
+
return /*#__PURE__*/React.createElement(Box, {
|
|
1094
|
+
key: key,
|
|
1095
|
+
box: box
|
|
1096
|
+
}, props.children( /*#__PURE__*/React.createElement(ContentElements, {
|
|
1097
|
+
sectionProps: props.sectionProps,
|
|
1098
|
+
customMargin: box.customMargin,
|
|
1099
|
+
items: box.items
|
|
1100
|
+
}, (item, child) => restrictWidth(item.effectiveWidth, item.effectiveAlignment, child)), {
|
|
1101
|
+
position: box.position,
|
|
1102
|
+
width: box.width,
|
|
1103
|
+
customMargin: box.customMargin,
|
|
1104
|
+
lastMarginBottom: box.items[box.items.length - 1].marginBottom,
|
|
1105
|
+
openStart: box.openStart,
|
|
1106
|
+
openEnd: box.openEnd,
|
|
1107
|
+
atSectionStart: box.atSectionStart,
|
|
1108
|
+
atSectionEnd: box.atSectionEnd
|
|
1109
|
+
}));
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
function Box({
|
|
1113
|
+
box,
|
|
1114
|
+
children
|
|
1115
|
+
}) {
|
|
1116
|
+
const ref = useRef();
|
|
1117
|
+
useStickyBoxHeight(ref, box.position === 'sticky');
|
|
1118
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1119
|
+
ref: ref,
|
|
1120
|
+
className: classNames(styles$4.box, styles$4[box.position], styles$4[`width-${widthName(box.width)}`], {
|
|
1121
|
+
[styles$4.customMargin]: box.customMargin
|
|
1122
|
+
})
|
|
1123
|
+
}, box.position === 'sticky' ? /*#__PURE__*/React.createElement(ViewTimelinePin, {
|
|
1124
|
+
boxRef: ref,
|
|
1125
|
+
children: children
|
|
1126
|
+
}) : children);
|
|
1127
|
+
}
|
|
1128
|
+
function useStickyBoxHeight(ref, sticky) {
|
|
1129
|
+
useEffect(() => {
|
|
1130
|
+
if (!sticky || typeof ResizeObserver === 'undefined') {
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
const box = ref.current;
|
|
1134
|
+
const observer = new ResizeObserver(entries => box.style.setProperty('--sticky-box-height', `${entries[entries.length - 1].contentRect.height}px`));
|
|
1135
|
+
observer.observe(box);
|
|
1136
|
+
return () => {
|
|
1137
|
+
observer.disconnect();
|
|
1138
|
+
box.style.removeProperty('--sticky-box-height');
|
|
1139
|
+
};
|
|
1140
|
+
}, [ref, sticky]);
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
// Sticky boxes stay pinned while the rest of their group scrolls past.
|
|
1144
|
+
// The group therefore is the subject that drives view timelines of
|
|
1145
|
+
// content elements inside the box.
|
|
1146
|
+
function ViewTimelinePin({
|
|
1147
|
+
boxRef,
|
|
1148
|
+
children
|
|
1149
|
+
}) {
|
|
1150
|
+
const getPinnedElements = useCallback(() => ({
|
|
1151
|
+
subject: boxRef.current.closest(`.${styles$4.group}`),
|
|
1152
|
+
element: boxRef.current
|
|
1153
|
+
}), [boxRef]);
|
|
1154
|
+
return /*#__PURE__*/React.createElement(ViewTimelinePinProvider, {
|
|
1155
|
+
getPinnedElements: getPinnedElements,
|
|
1156
|
+
children: children
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
function restrictWidth(width, alignment, children) {
|
|
1160
|
+
if (width >= 0) {
|
|
1161
|
+
return children;
|
|
1162
|
+
}
|
|
1163
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1164
|
+
className: classNames(styles$4[`restrict-${widthName(width)}`], styles$4[`align-${alignment}`])
|
|
1165
|
+
}, children);
|
|
1166
|
+
}
|
|
1167
|
+
function groupItemsByPosition(items, shouldInline, isContentPadded) {
|
|
1168
|
+
const groups = [];
|
|
1169
|
+
let currentGroup, currentBox;
|
|
1170
|
+
let firstInlineBox = null;
|
|
1171
|
+
let previousInlineBox = null;
|
|
1172
|
+
let previousInlineItem = null;
|
|
1173
|
+
let previousMarginBottom = null;
|
|
1174
|
+
items.reduce((previousPosition, item, index) => {
|
|
1175
|
+
var _item$props;
|
|
1176
|
+
let {
|
|
1177
|
+
customMargin: elementSupportsCustomMargin
|
|
1178
|
+
} = api.contentElementTypes.getOptions(item.type) || {};
|
|
1179
|
+
if (typeof elementSupportsCustomMargin === 'function') {
|
|
1180
|
+
elementSupportsCustomMargin = elementSupportsCustomMargin({
|
|
1181
|
+
configuration: item.props
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
let width = item.width || 0;
|
|
1185
|
+
const position = onTheSide(item.position) && !shouldInline(width) ? item.position : 'inline';
|
|
1186
|
+
const customMargin = !!elementSupportsCustomMargin && width < widths.full;
|
|
1187
|
+
const alignment = item.position === 'inline' && width < 0 ? item.alignment : null;
|
|
1188
|
+
if (onTheSide(item.position) && position === 'inline' && width > widths.md) {
|
|
1189
|
+
width -= 1;
|
|
1190
|
+
}
|
|
1191
|
+
const boxWidth = position === 'inline' ? Math.max(width, widths.md) : width;
|
|
1192
|
+
if (!currentGroup || previousPosition !== position || onTheSide(position) && currentBox.customMargin !== customMargin || currentBox.width !== boxWidth) {
|
|
1193
|
+
currentBox = null;
|
|
1194
|
+
if (!(onTheSide(previousPosition) && position === 'inline' && width <= widths.md)) {
|
|
1195
|
+
currentGroup = {
|
|
1196
|
+
width: onTheSide(position) ? widths.md : boxWidth,
|
|
1197
|
+
boxes: []
|
|
1198
|
+
};
|
|
1199
|
+
groups.push(currentGroup);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
if (!currentBox || currentBox.customMargin !== customMargin) {
|
|
1203
|
+
currentBox = {
|
|
1204
|
+
customMargin,
|
|
1205
|
+
position,
|
|
1206
|
+
width: boxWidth,
|
|
1207
|
+
items: []
|
|
1208
|
+
};
|
|
1209
|
+
if (position === 'inline') {
|
|
1210
|
+
if (!continueInlineBoxes(previousInlineBox, currentBox)) {
|
|
1211
|
+
previousMarginBottom = null;
|
|
1212
|
+
}
|
|
1213
|
+
markFirstInlineBoxAsAtSectionStart(currentBox);
|
|
1214
|
+
}
|
|
1215
|
+
currentGroup.boxes.push(currentBox);
|
|
1216
|
+
}
|
|
1217
|
+
item = {
|
|
1218
|
+
...item,
|
|
1219
|
+
effectiveWidth: width,
|
|
1220
|
+
effectiveAlignment: alignment,
|
|
1221
|
+
marginBottom: (_item$props = item.props) === null || _item$props === void 0 ? void 0 : _item$props.marginBottom
|
|
1222
|
+
};
|
|
1223
|
+
currentBox.items.push(item);
|
|
1224
|
+
if (position === 'inline') {
|
|
1225
|
+
assignPreviousMarginBottom(previousInlineItem, item);
|
|
1226
|
+
previousInlineItem = item;
|
|
1227
|
+
previousInlineBox = currentBox;
|
|
1228
|
+
}
|
|
1229
|
+
return position;
|
|
1230
|
+
}, null);
|
|
1231
|
+
if (currentBox) {
|
|
1232
|
+
currentBox.atSectionEnd = true;
|
|
1233
|
+
}
|
|
1234
|
+
return groups;
|
|
1235
|
+
function markFirstInlineBoxAsAtSectionStart(inlineBox) {
|
|
1236
|
+
if (!firstInlineBox) {
|
|
1237
|
+
inlineBox.atSectionStart = !isContentPadded;
|
|
1238
|
+
firstInlineBox = currentBox;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
function assignPreviousMarginBottom(previousInlineItem, item) {
|
|
1242
|
+
if (previousMarginBottom) {
|
|
1243
|
+
item.previousMarginBottom = previousMarginBottom;
|
|
1244
|
+
previousInlineItem.marginBottom = null;
|
|
1245
|
+
previousMarginBottom = null;
|
|
1246
|
+
}
|
|
1247
|
+
if (item.marginBottom) {
|
|
1248
|
+
previousMarginBottom = item.marginBottom;
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
function continueInlineBoxes(previousInlineBox, currentBox) {
|
|
1253
|
+
if (previousInlineBox && isContinuable(previousInlineBox) && isContinuable(currentBox)) {
|
|
1254
|
+
previousInlineBox.openEnd = true;
|
|
1255
|
+
currentBox.openStart = true;
|
|
1256
|
+
return true;
|
|
1257
|
+
}
|
|
1258
|
+
return false;
|
|
1259
|
+
}
|
|
1260
|
+
function isContinuable(box) {
|
|
1261
|
+
return box.width <= widths.md && !box.customMargin;
|
|
1262
|
+
}
|
|
1263
|
+
function onTheSide(position) {
|
|
1264
|
+
return position === 'side' || position === 'sticky';
|
|
1265
|
+
}
|
|
1266
|
+
function renderPlaceholder(placeholder) {
|
|
1267
|
+
if (!placeholder) {
|
|
1268
|
+
return null;
|
|
1269
|
+
}
|
|
1270
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1271
|
+
className: classNames(styles$4.group)
|
|
1272
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1273
|
+
className: classNames(styles$4.box, styles$4.inline)
|
|
1274
|
+
}, placeholder));
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
var styles$5 = {"outer":"Center-module_outer__3Rr0H","customMargin":"Center-module_customMargin__1es3t","outer-full":"Center-module_outer-full__3dknO","box":"Center-module_box__3pZbI","box-lg":"Center-module_box-lg__3t3-e","box-xl":"Center-module_box-xl__1zdnc","box-full":"Center-module_box-full__1YKgB","constrainContentWidth":"Center-module_constrainContentWidth__3BThN","outer-lg":"Center-module_outer-lg__fEKHC","clear":"Center-module_clear__jJEap","selfClear-left":"Center-module_selfClear-left__39q7E","selfClear-right":"Center-module_selfClear-right__1T_fm","selfClear-both":"Center-module_selfClear-both__2WDEC","inner-xxs":"Center-module_inner-xxs__1oroz","inner-xs":"Center-module_inner-xs__3FRT8","inner-sm":"Center-module_inner-sm__-oQ0E","align-left":"Center-module_align-left__1rYBX","align-right":"Center-module_align-right__1uglq","inner-left":"Center-module_inner-left__2z9Ea","inner-right":"Center-module_inner-right__KBkVt","sideBySide":"Center-module_sideBySide__-YsP0","inner-md":"Center-module_inner-md__3dLC3","inner-lg":"Center-module_inner-lg__2GQbs","inner-xl":"Center-module_inner-xl__3dOME"};
|
|
1278
|
+
|
|
1279
|
+
const floatedPositions = ['left', 'right'];
|
|
1280
|
+
function Center(props) {
|
|
1281
|
+
const groups = groupItems(props.items);
|
|
1282
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1283
|
+
className: classNames(styles$5.root, {
|
|
1284
|
+
[styles$5.constrainContentWidth]: props.constrainContentWidth
|
|
1285
|
+
})
|
|
1286
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1287
|
+
ref: props.contentAreaRef
|
|
1288
|
+
}), groups.map((group, groupIndex) => {
|
|
1289
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1290
|
+
key: groupIndex,
|
|
1291
|
+
className: classNames(styles$5.outer, styles$5[`outer-${widthName(group.width)}`], {
|
|
1292
|
+
[styles$5.customMargin]: group.customMargin
|
|
1293
|
+
})
|
|
1294
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1295
|
+
className: classNames(styles$5.box, styles$5[`box-${widthName(group.width)}`])
|
|
1296
|
+
}, props.children( /*#__PURE__*/React.createElement(ContentElements, {
|
|
1297
|
+
sectionProps: props.sectionProps,
|
|
1298
|
+
items: group.items,
|
|
1299
|
+
customMargin: group.customMargin
|
|
1300
|
+
}, (item, child, itemIndex) => {
|
|
1301
|
+
const alignment = getWidth(item) < 0 && item.position === 'inline' ? item.alignment : null;
|
|
1302
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1303
|
+
key: item.id,
|
|
1304
|
+
className: classNames(styles$5[`selfClear-${selfClear(group.items, itemIndex)}`])
|
|
1305
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1306
|
+
className: classNames(styles$5[`inner-${item.position}`], styles$5[`inner-${widthName(item.width)}`], styles$5[`align-${alignment}`], {
|
|
1307
|
+
[styles$5.clear]: clearItem(group.items, itemIndex)
|
|
1308
|
+
}, {
|
|
1309
|
+
[styles$5[`sideBySide`]]: sideBySideFloat(group.items, itemIndex)
|
|
1310
|
+
})
|
|
1311
|
+
}, child));
|
|
1312
|
+
}), boxProps(groups, groupIndex, props.isContentPadded))));
|
|
1313
|
+
}), renderPlaceholder$1(props.placeholder));
|
|
1314
|
+
}
|
|
1315
|
+
function groupItems(items) {
|
|
1316
|
+
const groups = [];
|
|
1317
|
+
let currentGroup;
|
|
1318
|
+
items.forEach(item => {
|
|
1319
|
+
var _item$props;
|
|
1320
|
+
const width = isFloated(item) ? widths.md : groupWidth(item);
|
|
1321
|
+
const customMargin = hasCustomMargin(item);
|
|
1322
|
+
if (!currentGroup || currentGroup.width !== width || currentGroup.customMargin !== customMargin) {
|
|
1323
|
+
currentGroup = {
|
|
1324
|
+
items: [],
|
|
1325
|
+
position: 'inline',
|
|
1326
|
+
width,
|
|
1327
|
+
customMargin
|
|
1328
|
+
};
|
|
1329
|
+
groups.push(currentGroup);
|
|
1330
|
+
}
|
|
1331
|
+
currentGroup.items.push({
|
|
1332
|
+
...item,
|
|
1333
|
+
marginBottom: (_item$props = item.props) === null || _item$props === void 0 ? void 0 : _item$props.marginBottom
|
|
1334
|
+
});
|
|
1335
|
+
});
|
|
1336
|
+
return groups;
|
|
1337
|
+
}
|
|
1338
|
+
function boxProps(groups, index, isContentPadded) {
|
|
1339
|
+
const group = groups[index];
|
|
1340
|
+
const lastItem = group.items[group.items.length - 1];
|
|
1341
|
+
return {
|
|
1342
|
+
position: group.position,
|
|
1343
|
+
width: group.width,
|
|
1344
|
+
customMargin: group.customMargin,
|
|
1345
|
+
lastMarginBottom: lastItem.marginBottom,
|
|
1346
|
+
atSectionStart: index === 0 && !isContentPadded,
|
|
1347
|
+
atSectionEnd: index === groups.length - 1
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
function selfClear(items, index) {
|
|
1351
|
+
const item = items[index];
|
|
1352
|
+
const next = items[index + 1];
|
|
1353
|
+
if (supportsWrappingAroundFloats(item) || isFloated(item) && (!next || clearItem(items, index + 1))) {
|
|
1354
|
+
return 'both';
|
|
1355
|
+
} else if (isFloated(item)) {
|
|
1356
|
+
return item.position === 'left' ? 'right' : 'left';
|
|
1357
|
+
}
|
|
1358
|
+
return 'none';
|
|
1359
|
+
}
|
|
1360
|
+
function clearItem(items, index) {
|
|
1361
|
+
return supportsWrappingAroundFloats(items[index]) ? followsSideBySideElements(items, index) : !isFloatedFollowingOppositeFloated(items, index);
|
|
1362
|
+
}
|
|
1363
|
+
function followsSideBySideElements(items, index) {
|
|
1364
|
+
return index > 1 && (items[index - 1].position === 'left' && items[index - 2].position === 'right' || items[index - 1].position === 'right' && items[index - 2].position === 'left');
|
|
1365
|
+
}
|
|
1366
|
+
function sideBySideFloat(items, index) {
|
|
1367
|
+
return isFloatedFollowingOppositeFloated(items, index) || isFloatedFollowedByOppositeFloated(items, index);
|
|
1368
|
+
}
|
|
1369
|
+
function isFloatedFollowingOppositeFloated(items, index) {
|
|
1370
|
+
return index > 0 && isFloated(items[index]) && isFloated(items[index - 1]) && items[index].position !== items[index - 1].position;
|
|
1371
|
+
}
|
|
1372
|
+
function isFloatedFollowedByOppositeFloated(items, index) {
|
|
1373
|
+
return index < items.length - 1 && isFloated(items[index]) && isFloated(items[index + 1]) && items[index].position !== items[index + 1].position;
|
|
1374
|
+
}
|
|
1375
|
+
function isFloated(item) {
|
|
1376
|
+
return floatedPositions.includes(item.position);
|
|
1377
|
+
}
|
|
1378
|
+
function supportsWrappingAroundFloats(item) {
|
|
1379
|
+
const {
|
|
1380
|
+
supportsWrappingAroundFloats
|
|
1381
|
+
} = api.contentElementTypes.getOptions(item.type);
|
|
1382
|
+
return supportsWrappingAroundFloats;
|
|
1383
|
+
}
|
|
1384
|
+
function hasCustomMargin(item) {
|
|
1385
|
+
const position = item.position;
|
|
1386
|
+
let {
|
|
1387
|
+
customMargin: elementSupportsCustomMargin
|
|
1388
|
+
} = api.contentElementTypes.getOptions(item.type) || {};
|
|
1389
|
+
if (typeof elementSupportsCustomMargin === 'function') {
|
|
1390
|
+
elementSupportsCustomMargin = elementSupportsCustomMargin({
|
|
1391
|
+
configuration: item.props
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
return !!(elementSupportsCustomMargin && position === 'inline' && getWidth(item) < widths.full);
|
|
1395
|
+
}
|
|
1396
|
+
function groupWidth(item) {
|
|
1397
|
+
return Math.max(getWidth(item), widths.md);
|
|
1398
|
+
}
|
|
1399
|
+
function getWidth(item) {
|
|
1400
|
+
return item.width || widths.md;
|
|
1401
|
+
}
|
|
1402
|
+
function renderPlaceholder$1(placeholder) {
|
|
1403
|
+
if (!placeholder) {
|
|
1404
|
+
return null;
|
|
1405
|
+
}
|
|
1406
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1407
|
+
className: classNames(styles$5.outer)
|
|
1408
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1409
|
+
className: classNames(styles$5.item)
|
|
1410
|
+
}, placeholder));
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
const Layout = React.memo(extensible('LayoutWithPlaceholder', LayoutWithoutInlineEditing), (prevProps, nextProps) => prevProps.sectionId === nextProps.sectionId && prevProps.items === nextProps.items && prevProps.appearance === nextProps.appearance && prevProps.contentAreaRef === nextProps.contentAreaRef && prevProps.sectionProps === nextProps.sectionProps && prevProps.isContentPadded === nextProps.isContentPadded && prevProps.constrainContentWidth === nextProps.constrainContentWidth);
|
|
1414
|
+
function LayoutWithoutInlineEditing(props) {
|
|
1415
|
+
if (props.sectionProps.layout === 'center' || props.sectionProps.layout === 'centerRagged') {
|
|
1416
|
+
return /*#__PURE__*/React.createElement(Center, props);
|
|
1417
|
+
} else if (props.sectionProps.layout === 'right') {
|
|
1418
|
+
return /*#__PURE__*/React.createElement(TwoColumn, Object.assign({
|
|
1419
|
+
align: "right"
|
|
1420
|
+
}, props));
|
|
1421
|
+
} else {
|
|
1422
|
+
return /*#__PURE__*/React.createElement(TwoColumn, props);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
Layout.defaultProps = {
|
|
1426
|
+
layout: 'left'
|
|
1427
|
+
};
|
|
1428
|
+
|
|
1429
|
+
function scrollToElement(element, {
|
|
1430
|
+
align,
|
|
1431
|
+
ifNeeded,
|
|
1432
|
+
behavior
|
|
1433
|
+
} = {}) {
|
|
1434
|
+
const rect = element.getBoundingClientRect();
|
|
1435
|
+
if (ifNeeded && isInViewport(align, rect)) {
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
window.scrollTo({
|
|
1439
|
+
top: rect.top + window.scrollY + getAlignOffset(align, rect),
|
|
1440
|
+
behavior: behavior || 'smooth'
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
function getAlignOffset(align, rect) {
|
|
1444
|
+
if (align === 'start') {
|
|
1445
|
+
return 0;
|
|
1446
|
+
} else if (align === 'center') {
|
|
1447
|
+
return (rect.height - window.innerHeight) / 2;
|
|
1448
|
+
} else if (align === 'nearEnd') {
|
|
1449
|
+
return rect.height - window.innerHeight * 0.75;
|
|
1450
|
+
} else {
|
|
1451
|
+
return -window.innerHeight * 0.25;
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
function isInViewport(align, rect) {
|
|
1455
|
+
if (align === 'nearEnd') {
|
|
1456
|
+
const bottom = rect.top + rect.height;
|
|
1457
|
+
return bottom > 0 && bottom <= window.innerHeight;
|
|
1458
|
+
} else {
|
|
1459
|
+
return rect.top >= 0 && rect.top < window.innerHeight;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
const ScrollTargetEmitterContext = createContext();
|
|
1464
|
+
function ScrollTargetEmitterProvider({
|
|
1465
|
+
children
|
|
1466
|
+
}) {
|
|
1467
|
+
const emitter = useMemo(() => Object.assign({}, BackboneEvents), []);
|
|
1468
|
+
return /*#__PURE__*/React.createElement(ScrollTargetEmitterContext.Provider, {
|
|
1469
|
+
value: emitter
|
|
1470
|
+
}, children);
|
|
1471
|
+
}
|
|
1472
|
+
function useScrollToTarget() {
|
|
1473
|
+
const emitter = useContext(ScrollTargetEmitterContext);
|
|
1474
|
+
return useCallback(({
|
|
1475
|
+
id,
|
|
1476
|
+
align,
|
|
1477
|
+
ifNeeded,
|
|
1478
|
+
behavior
|
|
1479
|
+
}) => {
|
|
1480
|
+
emitter.trigger(id, {
|
|
1481
|
+
align,
|
|
1482
|
+
ifNeeded,
|
|
1483
|
+
behavior
|
|
1484
|
+
});
|
|
1485
|
+
}, [emitter]);
|
|
1486
|
+
}
|
|
1487
|
+
function useScrollTarget(id) {
|
|
1488
|
+
const ref = useRef();
|
|
1489
|
+
const emitter = useContext(ScrollTargetEmitterContext);
|
|
1490
|
+
useEffect(() => {
|
|
1491
|
+
const handler = options => {
|
|
1492
|
+
if (ref.current) {
|
|
1493
|
+
scrollToElement(ref.current, options);
|
|
1494
|
+
}
|
|
1495
|
+
};
|
|
1496
|
+
emitter.on(id, handler);
|
|
1497
|
+
return () => emitter.off(id, handler);
|
|
1498
|
+
}, [id, emitter]);
|
|
1499
|
+
return ref;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
const DarkBackgroundContext = createContext(true);
|
|
1503
|
+
function BackgroundColorProvider({
|
|
1504
|
+
dark,
|
|
1505
|
+
invert,
|
|
1506
|
+
children
|
|
1507
|
+
}) {
|
|
1508
|
+
const previousValue = useDarkBackground();
|
|
1509
|
+
return /*#__PURE__*/React.createElement(DarkBackgroundContext.Provider, {
|
|
1510
|
+
value: getValue({
|
|
1511
|
+
dark,
|
|
1512
|
+
invert,
|
|
1513
|
+
previousValue
|
|
1514
|
+
})
|
|
1515
|
+
}, children);
|
|
1516
|
+
}
|
|
1517
|
+
function getValue({
|
|
1518
|
+
dark,
|
|
1519
|
+
invert,
|
|
1520
|
+
previousValue
|
|
1521
|
+
}) {
|
|
1522
|
+
if (dark !== undefined) {
|
|
1523
|
+
return dark;
|
|
1524
|
+
} else if (invert === true) {
|
|
1525
|
+
return !previousValue;
|
|
1526
|
+
} else {
|
|
1527
|
+
return previousValue;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
/**
|
|
1532
|
+
* Use to invert elements depending on whether they are rendered on a
|
|
1533
|
+
* dark or light background to ensure correct display in inverted
|
|
1534
|
+
* sections or in sections with card appearance.
|
|
1535
|
+
*
|
|
1536
|
+
* @return {boolean}
|
|
1537
|
+
*/
|
|
1538
|
+
function useDarkBackground() {
|
|
1539
|
+
return useContext(DarkBackgroundContext);
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
const ActiveExcursionContext = createContext({
|
|
1543
|
+
activeExcursion: undefined,
|
|
1544
|
+
activateExcursionOfSection: () => {},
|
|
1545
|
+
returnFromExcursion: () => {}
|
|
1546
|
+
});
|
|
1547
|
+
function ActiveExcursionProvider({
|
|
1548
|
+
children
|
|
1549
|
+
}) {
|
|
1550
|
+
const entryStructure = useEntryStructure();
|
|
1551
|
+
const scrollToTarget = useScrollToTarget();
|
|
1552
|
+
const [activeExcursionId, setActiveExcursionId] = useState();
|
|
1553
|
+
const [scrollTarget, setScrollTarget] = useState();
|
|
1554
|
+
const returnUrlRef = useRef(null);
|
|
1555
|
+
useEffect(() => {
|
|
1556
|
+
function handleHashChange(event) {
|
|
1557
|
+
const hash = window.__ACTIVE_EXCURSION__ ||
|
|
1558
|
+
// Used in Storybook
|
|
1559
|
+
window.location.hash.slice(1);
|
|
1560
|
+
const {
|
|
1561
|
+
excursion,
|
|
1562
|
+
sectionId
|
|
1563
|
+
} = findScrollTargetByHash(hash);
|
|
1564
|
+
if (excursion && (event === null || event === void 0 ? void 0 : event.oldURL) && !returnUrlRef.current) {
|
|
1565
|
+
returnUrlRef.current = event.oldURL;
|
|
1566
|
+
}
|
|
1567
|
+
setActiveExcursionId(prevExcursionId => {
|
|
1568
|
+
const excursionChanged = (excursion === null || excursion === void 0 ? void 0 : excursion.id) !== prevExcursionId;
|
|
1569
|
+
if (excursionChanged && sectionId) {
|
|
1570
|
+
setScrollTarget(sectionId);
|
|
1571
|
+
}
|
|
1572
|
+
return excursion === null || excursion === void 0 ? void 0 : excursion.id;
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
function findScrollTargetByHash(hash) {
|
|
1576
|
+
if (hash.startsWith('section-')) {
|
|
1577
|
+
const permaId = parseInt(hash.replace('section-', ''), 10);
|
|
1578
|
+
for (const chapter of entryStructure.excursions) {
|
|
1579
|
+
const section = chapter.sections.find(s => s.permaId === permaId);
|
|
1580
|
+
if (section) {
|
|
1581
|
+
var _chapter$sections$;
|
|
1582
|
+
const isFirstSection = section.id === ((_chapter$sections$ = chapter.sections[0]) === null || _chapter$sections$ === void 0 ? void 0 : _chapter$sections$.id);
|
|
1583
|
+
return {
|
|
1584
|
+
excursion: chapter,
|
|
1585
|
+
sectionId: isFirstSection ? null : section.id
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
for (const chapter of entryStructure.main) {
|
|
1590
|
+
const section = chapter.sections.find(s => s.permaId === permaId);
|
|
1591
|
+
if (section) {
|
|
1592
|
+
return {
|
|
1593
|
+
excursion: null,
|
|
1594
|
+
sectionId: section.id
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
return {
|
|
1599
|
+
excursion: null,
|
|
1600
|
+
sectionId: null
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
const excursion = entryStructure.excursions.find(chapter => chapter.chapterSlug === hash);
|
|
1604
|
+
if (excursion) {
|
|
1605
|
+
return {
|
|
1606
|
+
excursion,
|
|
1607
|
+
sectionId: null
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
const mainChapter = entryStructure.main.find(chapter => chapter.chapterSlug === hash);
|
|
1611
|
+
if (mainChapter) {
|
|
1612
|
+
var _mainChapter$sections;
|
|
1613
|
+
return {
|
|
1614
|
+
excursion: null,
|
|
1615
|
+
sectionId: (_mainChapter$sections = mainChapter.sections[0]) === null || _mainChapter$sections === void 0 ? void 0 : _mainChapter$sections.id
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
return {
|
|
1619
|
+
excursion: null,
|
|
1620
|
+
sectionId: null
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1623
|
+
handleHashChange();
|
|
1624
|
+
window.addEventListener('hashchange', handleHashChange);
|
|
1625
|
+
return () => window.removeEventListener('hashchange', handleHashChange);
|
|
1626
|
+
}, [entryStructure]);
|
|
1627
|
+
useEffect(() => {
|
|
1628
|
+
if (!scrollTarget) {
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1631
|
+
setTimeout(() => {
|
|
1632
|
+
scrollToTarget({
|
|
1633
|
+
id: scrollTarget
|
|
1634
|
+
});
|
|
1635
|
+
}, 500);
|
|
1636
|
+
setScrollTarget(null);
|
|
1637
|
+
}, [scrollTarget, scrollToTarget]);
|
|
1638
|
+
const activateExcursionOfSection = useCallback(({
|
|
1639
|
+
id
|
|
1640
|
+
}) => {
|
|
1641
|
+
const excursion = entryStructure.excursions.find(chapter => chapter.sections.find(section => section.id === id));
|
|
1642
|
+
if (excursion) {
|
|
1643
|
+
returnUrlRef.current = returnUrlRef.current || window.location.href;
|
|
1644
|
+
window.history.replaceState(null, null, '#' + excursion.chapterSlug);
|
|
1645
|
+
}
|
|
1646
|
+
setActiveExcursionId(excursion === null || excursion === void 0 ? void 0 : excursion.id);
|
|
1647
|
+
}, [entryStructure]);
|
|
1648
|
+
const returnFromExcursion = useCallback(() => {
|
|
1649
|
+
setActiveExcursionId(undefined);
|
|
1650
|
+
if (returnUrlRef.current) {
|
|
1651
|
+
window.history.replaceState(null, null, returnUrlRef.current);
|
|
1652
|
+
returnUrlRef.current = null;
|
|
1653
|
+
}
|
|
1654
|
+
}, []);
|
|
1655
|
+
const activeExcursion = useMemo(() => {
|
|
1656
|
+
return entryStructure.excursions.find(excursion => excursion.id === activeExcursionId);
|
|
1657
|
+
}, [entryStructure, activeExcursionId]);
|
|
1658
|
+
const value = useMemo(() => ({
|
|
1659
|
+
activeExcursion,
|
|
1660
|
+
activateExcursionOfSection,
|
|
1661
|
+
returnFromExcursion
|
|
1662
|
+
}), [activeExcursion, activateExcursionOfSection, returnFromExcursion]);
|
|
1663
|
+
return /*#__PURE__*/React.createElement(ActiveExcursionContext.Provider, {
|
|
1664
|
+
value: value
|
|
1665
|
+
}, children);
|
|
1666
|
+
}
|
|
1667
|
+
function useActiveExcursion() {
|
|
1668
|
+
return useContext(ActiveExcursionContext);
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
function camelize(snakeCase) {
|
|
1672
|
+
return snakeCase.replace(/[_-][a-z]/g, function (match) {
|
|
1673
|
+
return match[1].toUpperCase();
|
|
1674
|
+
});
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
/**
|
|
1678
|
+
* Resolve a palette color to a CSS custom property.
|
|
1679
|
+
*
|
|
1680
|
+
* @example
|
|
1681
|
+
* <div style={{backgroundColor: paletteColor(configuration.backgroundColor)}}>
|
|
1682
|
+
*/
|
|
1683
|
+
function paletteColor(value) {
|
|
1684
|
+
if (!value) {
|
|
1685
|
+
return undefined;
|
|
1686
|
+
}
|
|
1687
|
+
if (value[0] === '#') {
|
|
1688
|
+
return value;
|
|
1689
|
+
}
|
|
1690
|
+
return `var(--theme-palette-color-${value})`;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
var textStyles = {"text-2xs":"16px","text-xs":"18px","text-s":"20px","text-base":"22px","text-md":"33px","text-l":"40px","text-2l":"50px","text-xl":"66px","text-2xl":"88px","text-3xl":"110px","text-4xl":"200px","text-5xl":"350px","heading-lg":"Text-module_heading-lg__FKxzu typography-headingLg typography-heading","heading-md":"Text-module_heading-md__1q5Ss typography-headingMd typography-heading","heading-sm":"Text-module_heading-sm__2awaz typography-headingSm typography-heading","heading-xs":"Text-module_heading-xs__21nHy typography-headingXs typography-heading","headingTagline-lg":"Text-module_headingTagline-lg__1O2TQ typography-headingTaglineLg typography-headingTagline","headingTagline-md":"Text-module_headingTagline-md__2hrVS typography-headingTaglineMd typography-headingTagline","headingTagline-sm":"Text-module_headingTagline-sm__1Fw2J typography-headingTaglineSm typography-headingTagline","headingSubtitle-lg":"Text-module_headingSubtitle-lg__15kj0 typography-headingSubtitleLg typography-headingSubtitle","headingSubtitle-md":"Text-module_headingSubtitle-md__2_qtz typography-headingSubtitleMd typography-headingSubtitle","headingSubtitle-sm":"Text-module_headingSubtitle-sm__MOc_6 typography-headingSubtitleSm typography-headingSubtitle","body":"Text-module_body__4oWD- typography-body","caption":"Text-module_caption__3_6Au typography-caption","question":"Text-module_question__ByVAq typography-question","questionAnswer":"Text-module_questionAnswer__2Hdmc typography-questionAnswer","quoteText-lg":"Text-module_quoteText-lg__3ZnZi typography-quoteText typography-quoteTextLg","quoteText-md":"Text-module_quoteText-md__2eooO typography-quoteText typography-quoteTextMd","quoteText-sm":"Text-module_quoteText-sm__5nKex typography-quoteText typography-quoteTextSm","quoteText-xs":"Text-module_quoteText-xs__2p5on typography-quoteText typography-quoteTextXs","quoteAttribution":"Text-module_quoteAttribution__VBqLw typography-quoteAttribution","quoteAttribution-lg":"Text-module_quoteAttribution-lg__23pl- Text-module_quoteAttribution__VBqLw typography-quoteAttribution typography-quoteAttributionLg","quoteAttribution-md":"Text-module_quoteAttribution-md__2BnBN Text-module_quoteAttribution__VBqLw typography-quoteAttribution typography-quoteAttributionMd","quoteAttribution-sm":"Text-module_quoteAttribution-sm__17vbI Text-module_quoteAttribution__VBqLw typography-quoteAttribution typography-quoteAttributionSm","quoteAttribution-xs":"Text-module_quoteAttribution-xs__3v3BW Text-module_quoteAttribution__VBqLw typography-quoteAttribution typography-quoteAttributionXs","counterNumber":"Text-module_counterNumber__DkyhC typography-counterNumber","counterUnit":"Text-module_counterUnit__1N5v7 typography-counterUnit","counterDescription":"Text-module_counterDescription__34NjQ typography-counterDescription","hotspotsTooltipTitle":"Text-module_hotspotsTooltipTitle__2KROf typography-hotspotTooltipTitle","hotspotsTooltipDescription":"Text-module_hotspotsTooltipDescription__2l9v5 typography-hotspotTooltipDescription","hotspotsTooltipLink":"Text-module_hotspotsTooltipLink__2F2aj typography-hotspotTooltipLink typography-linkButton","teaserTitle-lg":"Text-module_teaserTitle-lg__K3MFG typography-externalLinkTitle typography-externalLinkTitleLg","teaserTitle-md":"Text-module_teaserTitle-md__tBZIn typography-externalLinkTitle typography-externalLinkTitleMd","teaserTitle-sm":"Text-module_teaserTitle-sm__ZXQCr typography-externalLinkTitle typography-externalLinkTitleSm","teaserTagline-lg":"Text-module_teaserTagline-lg__8JW7Z typography-externalLinkTagline typography-externalLinkTaglineLg","teaserTagline-md":"Text-module_teaserTagline-md__45HLL typography-externalLinkTagline typography-externalLinkTaglineMd","teaserTagline-sm":"Text-module_teaserTagline-sm__1vzOr typography-externalLinkTagline typography-externalLinkTaglineSm","teaserDescription-lg":"Text-module_teaserDescription-lg__1ysIZ typography-externalLinkDescription","teaserDescription-md":"Text-module_teaserDescription-md__1JtRs typography-externalLinkDescription","teaserDescription-sm":"Text-module_teaserDescription-sm__2VRao typography-externalLinkDescription","teaserLink":"Text-module_teaserLink__XEWHU typography-teaserLink typography-linkButton","infoTableLabel":"Text-module_infoTableLabel__w9kbv typography-infoTableLabel","infoTableValue":"Text-module_infoTableValue__2elOY typography-infoTableValue"};
|
|
1694
|
+
|
|
1695
|
+
/**
|
|
1696
|
+
* Render some text using the default typography scale.
|
|
1697
|
+
*
|
|
1698
|
+
* @param {Object} props
|
|
1699
|
+
* @param {string} props.scaleCategory -
|
|
1700
|
+
* One of the styles `'heading-lg'`, `'heading-md'`, `'heading-sm'`,`'heading-xs'`,
|
|
1701
|
+
* `'headingTagline-lg'`, `'headingTagline-md'`, `'headingTagline-sm'`,
|
|
1702
|
+
* `'headingSubtitle-lg'`, `'headingSubtitle-md'`, `'headingSubtitle-sm'`,
|
|
1703
|
+
* `'body'`, `'caption'`, `'question'`, `'questionAnswer'`,
|
|
1704
|
+
* `'quoteText-lg'`, `'quoteText-md'`, `'quoteText-sm'`, `'quoteText-xs'`, `'quoteAttribution'`,
|
|
1705
|
+
* `'counterNumber'`, `'counterUnit'`, `'counterDescription'`,
|
|
1706
|
+
* `'infoTableLabel'`, `'infoTableValue`'.
|
|
1707
|
+
* `'hotspotsTooltipTitle'`, `'hotspotsTooltipDescription`', `'hotspotsTooltipLink`',
|
|
1708
|
+
* `'teaserTitle-lg'`, `'teaserTitle-md'`, `'teaserTitle-sm'`,
|
|
1709
|
+
* `'teaserTagline-lg'`, `'teaserTagline-md'`, `'teaserTagline-sm'`,
|
|
1710
|
+
* `'teaserDescription-lg'`, `'teaserDescription-md'`, `'teaserDescription-sm'`,
|
|
1711
|
+
* `'teaserLink'`.
|
|
1712
|
+
* @param {string} [props.typographyVariant] - Suffix for variant class name.
|
|
1713
|
+
* @param {string} [props.typographySize] - Suffix for size class name.
|
|
1714
|
+
* @param {string} [props.inline] - Render a span instread of a div.
|
|
1715
|
+
* @param {string} props.children - Nodes to render with specified typography.
|
|
1716
|
+
*/
|
|
1717
|
+
function Text({
|
|
1718
|
+
inline,
|
|
1719
|
+
scaleCategory,
|
|
1720
|
+
typographyVariant,
|
|
1721
|
+
typographySize,
|
|
1722
|
+
children
|
|
1723
|
+
}) {
|
|
1724
|
+
const variantClassName = typographyVariant && `typography-${scaleCategory.split('-')[0]}-${typographyVariant}`;
|
|
1725
|
+
const sizeClassName = typographySize && `typography-${scaleCategory}-${typographySize}`;
|
|
1726
|
+
return React.createElement(inline ? 'span' : 'div', {
|
|
1727
|
+
className: classNames(textStyles[scaleCategory], variantClassName, sizeClassName)
|
|
1728
|
+
}, children);
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
function Link({
|
|
1732
|
+
attributes,
|
|
1733
|
+
children,
|
|
1734
|
+
href,
|
|
1735
|
+
openInNewTab
|
|
1736
|
+
}) {
|
|
1737
|
+
const embedOriginUrl = useEmbedOriginUrl();
|
|
1738
|
+
if (href === null || href === void 0 ? void 0 : href.chapter) {
|
|
1739
|
+
return /*#__PURE__*/React.createElement(ChapterLink, {
|
|
1740
|
+
attributes: attributes,
|
|
1741
|
+
chapterPermaId: href.chapter
|
|
1742
|
+
}, children);
|
|
1743
|
+
} else if (href === null || href === void 0 ? void 0 : href.section) {
|
|
1744
|
+
return /*#__PURE__*/React.createElement("a", Object.assign({}, attributes, {
|
|
1745
|
+
href: `#section-${href.section}`
|
|
1746
|
+
}), children);
|
|
1747
|
+
}
|
|
1748
|
+
if (href === null || href === void 0 ? void 0 : href.file) {
|
|
1749
|
+
return /*#__PURE__*/React.createElement(FileLink, {
|
|
1750
|
+
attributes: attributes,
|
|
1751
|
+
fileOptions: href.file
|
|
1752
|
+
}, children);
|
|
1753
|
+
} else {
|
|
1754
|
+
const targetAttributes = getTargetAttributes({
|
|
1755
|
+
href,
|
|
1756
|
+
openInNewTab,
|
|
1757
|
+
embedOriginUrl
|
|
1758
|
+
});
|
|
1759
|
+
return /*#__PURE__*/React.createElement("a", Object.assign({}, attributes, targetAttributes, {
|
|
1760
|
+
href: href
|
|
1761
|
+
}), children);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
function ChapterLink({
|
|
1765
|
+
attributes,
|
|
1766
|
+
children,
|
|
1767
|
+
chapterPermaId
|
|
1768
|
+
}) {
|
|
1769
|
+
const chapter = useChapter({
|
|
1770
|
+
permaId: chapterPermaId
|
|
1771
|
+
});
|
|
1772
|
+
return /*#__PURE__*/React.createElement("a", Object.assign({}, attributes, {
|
|
1773
|
+
href: `#${(chapter === null || chapter === void 0 ? void 0 : chapter.chapterSlug) || ''}`
|
|
1774
|
+
}), children);
|
|
1775
|
+
}
|
|
1776
|
+
function FileLink({
|
|
1777
|
+
attributes,
|
|
1778
|
+
children,
|
|
1779
|
+
fileOptions
|
|
1780
|
+
}) {
|
|
1781
|
+
const file = useDownloadableFile(fileOptions);
|
|
1782
|
+
return /*#__PURE__*/React.createElement("a", Object.assign({}, attributes, {
|
|
1783
|
+
target: "_blank",
|
|
1784
|
+
rel: "noopener noreferrer",
|
|
1785
|
+
href: file === null || file === void 0 ? void 0 : file.urls.download
|
|
1786
|
+
}), children);
|
|
1787
|
+
}
|
|
1788
|
+
function getTargetAttributes({
|
|
1789
|
+
href,
|
|
1790
|
+
openInNewTab,
|
|
1791
|
+
embedOriginUrl
|
|
1792
|
+
}) {
|
|
1793
|
+
if (openInNewTab) {
|
|
1794
|
+
return {
|
|
1795
|
+
target: '_blank',
|
|
1796
|
+
rel: 'noopener noreferrer'
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
if (embedOriginUrl && typeof href === 'string' && !href.startsWith('#') && !href.startsWith(embedOriginUrl)) {
|
|
1800
|
+
return {
|
|
1801
|
+
target: '_top'
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1804
|
+
return {};
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
var textStyles$1 = {"darkContentTextColor":"var(--theme-dark-content-text-color, #222)","lightContentTextColor":"var(--theme-light-content-text-color, #fff)","contentLinkColor":"var(--content-link-color)","root":"EditableText-module_root__2v1tU","justify":"EditableText-module_justify__1pNdv","light":"EditableText-module_light__2c29h","dark":"EditableText-module_dark__2ym90","link":"EditableText-module_link__3vDbl typography-contentLink","bold":"EditableText-module_bold__tGw26","sub":"EditableText-module_sub__pNBwN","sup":"EditableText-module_sup__RJJlq"};
|
|
1808
|
+
|
|
1809
|
+
const defaultValue = [{
|
|
1810
|
+
type: 'paragraph',
|
|
1811
|
+
children: [{
|
|
1812
|
+
text: ''
|
|
1813
|
+
}]
|
|
1814
|
+
}];
|
|
1815
|
+
function PlainEditableText({
|
|
1816
|
+
value,
|
|
1817
|
+
className,
|
|
1818
|
+
scaleCategory = 'body',
|
|
1819
|
+
typographyVariant,
|
|
1820
|
+
typographySize
|
|
1821
|
+
}) {
|
|
1822
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1823
|
+
className: classNames(textStyles$1.root, className)
|
|
1824
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
1825
|
+
scaleCategory: scaleCategory,
|
|
1826
|
+
typographyVariant: typographyVariant,
|
|
1827
|
+
typographySize: typographySize
|
|
1828
|
+
}, render(value || defaultValue)));
|
|
1829
|
+
}
|
|
1830
|
+
const EditableText = extensible('EditableText', PlainEditableText);
|
|
1831
|
+
function render(children) {
|
|
1832
|
+
return children.map((element, index) => {
|
|
1833
|
+
if (element.type && element.children) {
|
|
1834
|
+
return renderElement({
|
|
1835
|
+
attributes: {
|
|
1836
|
+
key: index
|
|
1837
|
+
},
|
|
1838
|
+
element,
|
|
1839
|
+
children: render(element.children)
|
|
1840
|
+
});
|
|
1841
|
+
} else {
|
|
1842
|
+
return renderLeaf({
|
|
1843
|
+
attributes: {
|
|
1844
|
+
key: index
|
|
1845
|
+
},
|
|
1846
|
+
leaf: element,
|
|
1847
|
+
children: children.length === 1 && element.text.trim() === '' ? '\uFEFF' : element.text
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1850
|
+
});
|
|
1851
|
+
}
|
|
1852
|
+
function renderElement({
|
|
1853
|
+
attributes,
|
|
1854
|
+
children,
|
|
1855
|
+
element
|
|
1856
|
+
}) {
|
|
1857
|
+
const variantClassName = element.variant && ['typography-textBlock', camelize(element.type), element.variant].join('-');
|
|
1858
|
+
const sizeClassName = element.size && ['typography-textBlock', camelize(element.type), element.size].join('-');
|
|
1859
|
+
const className = classNames(variantClassName, sizeClassName, {
|
|
1860
|
+
[textStyles$1.justify]: element.textAlign === 'justify'
|
|
1861
|
+
});
|
|
1862
|
+
const inlineStyles = {
|
|
1863
|
+
...(element.color && {
|
|
1864
|
+
color: paletteColor(element.color)
|
|
1865
|
+
})
|
|
1866
|
+
};
|
|
1867
|
+
switch (element.type) {
|
|
1868
|
+
case 'block-quote':
|
|
1869
|
+
return /*#__PURE__*/React.createElement("blockquote", Object.assign({}, attributes, {
|
|
1870
|
+
className: className,
|
|
1871
|
+
style: inlineStyles
|
|
1872
|
+
}), children);
|
|
1873
|
+
case 'bulleted-list':
|
|
1874
|
+
return /*#__PURE__*/React.createElement("ul", Object.assign({}, attributes, {
|
|
1875
|
+
className: className,
|
|
1876
|
+
style: inlineStyles
|
|
1877
|
+
}), children);
|
|
1878
|
+
case 'numbered-list':
|
|
1879
|
+
return /*#__PURE__*/React.createElement("ol", Object.assign({}, attributes, {
|
|
1880
|
+
className: className,
|
|
1881
|
+
style: inlineStyles
|
|
1882
|
+
}), children);
|
|
1883
|
+
case 'list-item':
|
|
1884
|
+
return /*#__PURE__*/React.createElement("li", attributes, children);
|
|
1885
|
+
case 'heading':
|
|
1886
|
+
const {
|
|
1887
|
+
key,
|
|
1888
|
+
...otherAttributes
|
|
1889
|
+
} = attributes;
|
|
1890
|
+
return /*#__PURE__*/React.createElement(Heading, {
|
|
1891
|
+
key: key,
|
|
1892
|
+
attributes: otherAttributes,
|
|
1893
|
+
className: className,
|
|
1894
|
+
inlineStyles: inlineStyles
|
|
1895
|
+
}, children);
|
|
1896
|
+
case 'link':
|
|
1897
|
+
return renderLink({
|
|
1898
|
+
attributes,
|
|
1899
|
+
children,
|
|
1900
|
+
element
|
|
1901
|
+
});
|
|
1902
|
+
default:
|
|
1903
|
+
return /*#__PURE__*/React.createElement("p", Object.assign({}, attributes, {
|
|
1904
|
+
className: className,
|
|
1905
|
+
style: inlineStyles
|
|
1906
|
+
}), children);
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
function Heading({
|
|
1910
|
+
attributes,
|
|
1911
|
+
className,
|
|
1912
|
+
inlineStyles,
|
|
1913
|
+
children
|
|
1914
|
+
}) {
|
|
1915
|
+
const darkBackground = useDarkBackground();
|
|
1916
|
+
return /*#__PURE__*/React.createElement("h2", Object.assign({}, attributes, {
|
|
1917
|
+
className: classNames(className, darkBackground ? textStyles$1.light : textStyles$1.dark, 'scope-headings', textStyles['heading-xs']),
|
|
1918
|
+
style: inlineStyles
|
|
1919
|
+
}), children);
|
|
1920
|
+
}
|
|
1921
|
+
function renderLink({
|
|
1922
|
+
attributes,
|
|
1923
|
+
children,
|
|
1924
|
+
element
|
|
1925
|
+
}) {
|
|
1926
|
+
const {
|
|
1927
|
+
key,
|
|
1928
|
+
...otherAttributes
|
|
1929
|
+
} = attributes;
|
|
1930
|
+
return /*#__PURE__*/React.createElement(Link, {
|
|
1931
|
+
key: key,
|
|
1932
|
+
attributes: {
|
|
1933
|
+
...otherAttributes,
|
|
1934
|
+
className: textStyles$1.link
|
|
1935
|
+
},
|
|
1936
|
+
href: element.href,
|
|
1937
|
+
openInNewTab: element.openInNewTab,
|
|
1938
|
+
children: children
|
|
1939
|
+
});
|
|
1940
|
+
}
|
|
1941
|
+
function renderLeaf({
|
|
1942
|
+
attributes,
|
|
1943
|
+
children,
|
|
1944
|
+
leaf
|
|
1945
|
+
}) {
|
|
1946
|
+
if (leaf.bold) {
|
|
1947
|
+
children = /*#__PURE__*/React.createElement("strong", {
|
|
1948
|
+
className: textStyles$1.bold
|
|
1949
|
+
}, children);
|
|
1950
|
+
}
|
|
1951
|
+
if (leaf.italic) {
|
|
1952
|
+
children = /*#__PURE__*/React.createElement("em", null, children);
|
|
1953
|
+
}
|
|
1954
|
+
if (leaf.underline) {
|
|
1955
|
+
children = /*#__PURE__*/React.createElement("u", null, children);
|
|
1956
|
+
}
|
|
1957
|
+
if (leaf.strikethrough) {
|
|
1958
|
+
children = /*#__PURE__*/React.createElement("s", null, children);
|
|
1959
|
+
}
|
|
1960
|
+
if (leaf.sub) {
|
|
1961
|
+
children = /*#__PURE__*/React.createElement("sub", {
|
|
1962
|
+
className: textStyles$1.sub
|
|
1963
|
+
}, children);
|
|
1964
|
+
}
|
|
1965
|
+
if (leaf.sup) {
|
|
1966
|
+
children = /*#__PURE__*/React.createElement("sup", {
|
|
1967
|
+
className: textStyles$1.sup
|
|
1968
|
+
}, children);
|
|
1969
|
+
}
|
|
1970
|
+
return /*#__PURE__*/React.createElement("span", attributes, children);
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
const FloatingPortalRootContext = createContext();
|
|
1974
|
+
function useFloatingPortalRoot() {
|
|
1975
|
+
return useContext(FloatingPortalRootContext);
|
|
1976
|
+
}
|
|
1977
|
+
const FloatingPortalRootProvider = FloatingPortalRootContext.Provider;
|
|
1978
|
+
|
|
1979
|
+
export { useScrollTarget as A, BackgroundColorProvider as B, ContentElementAttributesProvider as C, Layout as D, EditableText as E, FloatingPortalRootProvider as F, ExtensionsProvider as G, ActiveExcursionProvider as H, StaticPreview as I, camelize as J, renderLink as K, Link as L, MainStorylineCoverageProvider as M, renderLeaf as N, OnScreenObserverRootProvider as O, scrollToElement as P, LayoutWithoutInlineEditing as Q, renderElement as R, ScrollTargetEmitterProvider as S, Text as T, PlainEditableText as U, textStyles$1 as V, useActiveExcursion as a, MainStorylineActivity as b, useMainStorylineCoverage as c, api as d, extensible as e, useDarkBackground as f, useContentElementAttributes as g, useContentElementLifecycle as h, ContentElementLifecycleContext as i, useContentElementViewTimelineProgress as j, ContentElementViewTimelineContext as k, getViewTimelineProgress as l, useIsStaticPreview as m, useStorylineActivity as n, useFloatingPortalRoot as o, provideExtensions as p, paletteColor as q, widthName as r, useOnScreen as s, createScrollPositionLifecycleProvider as t, useScrollToTarget as u, createScrollPositionLifecycleHook as v, widths as w, useMediaQuery as x, TrimDefaultMarginTop as y, ContentElement as z };
|