@qwickapps/react-framework 1.3.2 → 1.3.3

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.
Files changed (33) hide show
  1. package/README.md +106 -0
  2. package/dist/components/AccessibilityProvider.d.ts +64 -0
  3. package/dist/components/AccessibilityProvider.d.ts.map +1 -0
  4. package/dist/components/Breadcrumbs.d.ts +39 -0
  5. package/dist/components/Breadcrumbs.d.ts.map +1 -0
  6. package/dist/components/ErrorBoundary.d.ts +39 -0
  7. package/dist/components/ErrorBoundary.d.ts.map +1 -0
  8. package/dist/components/QwickApp.d.ts.map +1 -1
  9. package/dist/components/index.d.ts +3 -0
  10. package/dist/components/index.d.ts.map +1 -1
  11. package/dist/index.bundled.css +12 -0
  12. package/dist/index.esm.js +795 -12
  13. package/dist/index.js +800 -10
  14. package/package.json +1 -1
  15. package/src/components/AccessibilityProvider.tsx +466 -0
  16. package/src/components/Breadcrumbs.tsx +223 -0
  17. package/src/components/ErrorBoundary.tsx +216 -0
  18. package/src/components/QwickApp.tsx +17 -11
  19. package/src/components/__tests__/AccessibilityProvider.test.tsx +330 -0
  20. package/src/components/__tests__/Breadcrumbs.test.tsx +268 -0
  21. package/src/components/__tests__/ErrorBoundary.test.tsx +163 -0
  22. package/src/components/index.ts +3 -0
  23. package/src/stories/AccessibilityProvider.stories.tsx +284 -0
  24. package/src/stories/Breadcrumbs.stories.tsx +304 -0
  25. package/src/stories/ErrorBoundary.stories.tsx +159 -0
  26. package/dist/schemas/Builders.d.ts +0 -7
  27. package/dist/schemas/Builders.d.ts.map +0 -1
  28. package/dist/schemas/types.d.ts +0 -7
  29. package/dist/schemas/types.d.ts.map +0 -1
  30. package/dist/types/DataBinding.d.ts +0 -7
  31. package/dist/types/DataBinding.d.ts.map +0 -1
  32. package/dist/types/DataProvider.d.ts +0 -7
  33. package/dist/types/DataProvider.d.ts.map +0 -1
package/dist/index.esm.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
2
2
  import { CircularProgress, Button as Button$1, Paper, Typography, useTheme as useTheme$1, Box, Container as Container$8, Stack, Tooltip, IconButton, Snackbar, Alert, useMediaQuery, Avatar, Chip, Menu, MenuItem, Grid, Divider, Link, ListItemIcon, ListItemText, Card, CardContent, ButtonGroup, Collapse, TextField as TextField$1, FormControl, InputLabel, Select, FormHelperText } from '@mui/material';
3
- import React, { useMemo, useContext, useState, useEffect, createContext, useRef, useCallback, cloneElement } from 'react';
3
+ import React, { useMemo, useContext, useState, useEffect, createContext, useRef, useCallback, Component, useReducer, cloneElement } from 'react';
4
4
  import { MustacheTemplateProvider, MemoryCacheProvider, CachedDataProvider, Field, Editor, FieldType, Schema, Model, DataType } from '@qwickapps/schema';
5
5
  import { IsOptional, IsString, IsIn, IsUrl, IsArray, ValidateNested, IsNumber, Min, Max, IsBoolean, IsNotEmpty, IsObject } from 'class-validator';
6
6
  import { Type } from 'class-transformer';
@@ -20579,6 +20579,605 @@ const Scaffold = ({
20579
20579
 
20580
20580
  loggers.menu;
20581
20581
 
20582
+ /**
20583
+ * Generic ErrorBoundary component for catching and handling React errors
20584
+ *
20585
+ * Features:
20586
+ * - Catches JavaScript errors anywhere in child component tree
20587
+ * - Displays fallback UI with retry functionality
20588
+ * - Shows error details in development mode
20589
+ * - Customizable error handling and fallback UI
20590
+ * - Automatic error logging
20591
+ */
20592
+ class ErrorBoundary extends Component {
20593
+ constructor(props) {
20594
+ super(props);
20595
+ this.handleRetry = () => {
20596
+ this.setState({
20597
+ hasError: false,
20598
+ error: null,
20599
+ errorInfo: null
20600
+ });
20601
+ };
20602
+ this.handleRefresh = () => {
20603
+ if (typeof window !== 'undefined') {
20604
+ window.location.reload();
20605
+ }
20606
+ };
20607
+ this.state = {
20608
+ hasError: false,
20609
+ error: null,
20610
+ errorInfo: null
20611
+ };
20612
+ }
20613
+ static getDerivedStateFromError(error) {
20614
+ // Update state so the next render will show the fallback UI
20615
+ return {
20616
+ hasError: true,
20617
+ error,
20618
+ errorInfo: null
20619
+ };
20620
+ }
20621
+ componentDidCatch(error, errorInfo) {
20622
+ // Log error details
20623
+ this.setState({
20624
+ error,
20625
+ errorInfo
20626
+ });
20627
+ // Log to console for debugging
20628
+ console.error('ErrorBoundary caught an error:', error, errorInfo);
20629
+ // Custom error handler
20630
+ if (this.props.onError) {
20631
+ this.props.onError(error, errorInfo);
20632
+ }
20633
+ // Send error to logging service if available
20634
+ if (typeof window !== 'undefined') {
20635
+ // @ts-ignore - Global error logging service
20636
+ if (window.qwickapps?.logError) {
20637
+ window.qwickapps.logError(error, errorInfo);
20638
+ }
20639
+ }
20640
+ }
20641
+ render() {
20642
+ if (this.state.hasError) {
20643
+ // Custom fallback UI
20644
+ if (this.props.fallback) {
20645
+ return this.props.fallback;
20646
+ }
20647
+ // Default error UI
20648
+ return jsxs("div", {
20649
+ className: "error-boundary",
20650
+ role: "alert",
20651
+ style: {
20652
+ padding: '2rem',
20653
+ textAlign: 'center',
20654
+ backgroundColor: '#fef2f2',
20655
+ border: '1px solid #fecaca',
20656
+ borderRadius: '8px',
20657
+ margin: '1rem',
20658
+ color: '#991b1b'
20659
+ },
20660
+ children: [jsxs("div", {
20661
+ style: {
20662
+ marginBottom: '1.5rem'
20663
+ },
20664
+ children: [jsx("h2", {
20665
+ style: {
20666
+ fontSize: '1.5rem',
20667
+ fontWeight: 'bold',
20668
+ marginBottom: '0.5rem',
20669
+ color: '#991b1b'
20670
+ },
20671
+ children: "Something went wrong"
20672
+ }), jsx("p", {
20673
+ style: {
20674
+ color: '#7f1d1d',
20675
+ marginBottom: '1rem'
20676
+ },
20677
+ children: "An unexpected error occurred in the application. Please try again or refresh the page."
20678
+ })]
20679
+ }), jsxs("div", {
20680
+ style: {
20681
+ display: 'flex',
20682
+ gap: '0.75rem',
20683
+ justifyContent: 'center',
20684
+ marginBottom: '1rem'
20685
+ },
20686
+ children: [jsx(Button, {
20687
+ variant: "contained",
20688
+ onClick: this.handleRetry,
20689
+ style: {
20690
+ backgroundColor: '#dc2626',
20691
+ color: 'white'
20692
+ },
20693
+ children: "Try Again"
20694
+ }), jsx(Button, {
20695
+ variant: "outlined",
20696
+ onClick: this.handleRefresh,
20697
+ style: {
20698
+ borderColor: '#dc2626',
20699
+ color: '#dc2626'
20700
+ },
20701
+ children: "Refresh Page"
20702
+ })]
20703
+ }), this.state.error && jsxs("details", {
20704
+ style: {
20705
+ textAlign: 'left',
20706
+ marginTop: '1rem',
20707
+ padding: '1rem',
20708
+ backgroundColor: '#f9fafb',
20709
+ border: '1px solid #d1d5db',
20710
+ borderRadius: '6px'
20711
+ },
20712
+ children: [jsx("summary", {
20713
+ style: {
20714
+ cursor: 'pointer',
20715
+ fontWeight: 'bold',
20716
+ marginBottom: '0.5rem',
20717
+ color: '#374151'
20718
+ },
20719
+ children: "Error Details (Development Mode)"
20720
+ }), jsxs("pre", {
20721
+ style: {
20722
+ fontSize: '0.75rem',
20723
+ color: '#374151',
20724
+ whiteSpace: 'pre-wrap',
20725
+ overflow: 'auto'
20726
+ },
20727
+ children: [this.state.error.toString(), this.state.errorInfo?.componentStack && jsxs(Fragment, {
20728
+ children: [jsx("br", {}), jsx("br", {}), "Component Stack:", this.state.errorInfo.componentStack]
20729
+ })]
20730
+ })]
20731
+ })]
20732
+ });
20733
+ }
20734
+ return this.props.children;
20735
+ }
20736
+ }
20737
+ /**
20738
+ * Higher-order component that wraps a component with ErrorBoundary
20739
+ */
20740
+ function withErrorBoundary(WrappedComponent, errorBoundaryProps) {
20741
+ const WithErrorBoundaryComponent = props => jsx(ErrorBoundary, {
20742
+ ...errorBoundaryProps,
20743
+ children: jsx(WrappedComponent, {
20744
+ ...props
20745
+ })
20746
+ });
20747
+ WithErrorBoundaryComponent.displayName = `withErrorBoundary(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;
20748
+ return WithErrorBoundaryComponent;
20749
+ }
20750
+
20751
+ const AccessibilityContext = /*#__PURE__*/createContext(null);
20752
+ // Reducer
20753
+ const accessibilityReducer = (state, action) => {
20754
+ switch (action.type) {
20755
+ case 'SET_HIGH_CONTRAST':
20756
+ return {
20757
+ ...state,
20758
+ highContrast: action.payload
20759
+ };
20760
+ case 'SET_REDUCED_MOTION':
20761
+ return {
20762
+ ...state,
20763
+ reducedMotion: action.payload
20764
+ };
20765
+ case 'SET_LARGE_TEXT':
20766
+ return {
20767
+ ...state,
20768
+ largeText: action.payload
20769
+ };
20770
+ case 'SET_FOCUS_VISIBLE':
20771
+ return {
20772
+ ...state,
20773
+ focusVisible: action.payload
20774
+ };
20775
+ case 'SET_KEYBOARD_USER':
20776
+ return {
20777
+ ...state,
20778
+ isKeyboardUser: action.payload
20779
+ };
20780
+ case 'ADD_ISSUE':
20781
+ return {
20782
+ ...state,
20783
+ issues: [...state.issues, action.payload]
20784
+ };
20785
+ case 'CLEAR_ISSUES':
20786
+ return {
20787
+ ...state,
20788
+ issues: []
20789
+ };
20790
+ case 'SET_ANNOUNCEMENT':
20791
+ return {
20792
+ ...state,
20793
+ lastAnnouncement: action.payload
20794
+ };
20795
+ default:
20796
+ return state;
20797
+ }
20798
+ };
20799
+ // Initial state
20800
+ const initialState = {
20801
+ highContrast: false,
20802
+ reducedMotion: false,
20803
+ largeText: false,
20804
+ focusVisible: true,
20805
+ isKeyboardUser: false,
20806
+ issues: [],
20807
+ lastAnnouncement: null,
20808
+ preferences: {}
20809
+ };
20810
+ // ARIA Live Manager
20811
+ class AriaLiveManager {
20812
+ constructor() {
20813
+ this.politeRegion = null;
20814
+ this.assertiveRegion = null;
20815
+ this.createLiveRegions();
20816
+ }
20817
+ createLiveRegions() {
20818
+ if (typeof document === 'undefined') return;
20819
+ // Polite announcements
20820
+ this.politeRegion = document.createElement('div');
20821
+ this.politeRegion.setAttribute('aria-live', 'polite');
20822
+ this.politeRegion.setAttribute('aria-atomic', 'true');
20823
+ this.politeRegion.setAttribute('id', 'qwickapps-aria-live-polite');
20824
+ this.politeRegion.style.cssText = `
20825
+ position: absolute !important;
20826
+ left: -10000px !important;
20827
+ width: 1px !important;
20828
+ height: 1px !important;
20829
+ overflow: hidden !important;
20830
+ `;
20831
+ document.body.appendChild(this.politeRegion);
20832
+ // Assertive announcements
20833
+ this.assertiveRegion = document.createElement('div');
20834
+ this.assertiveRegion.setAttribute('aria-live', 'assertive');
20835
+ this.assertiveRegion.setAttribute('aria-atomic', 'true');
20836
+ this.assertiveRegion.setAttribute('id', 'qwickapps-aria-live-assertive');
20837
+ this.assertiveRegion.style.cssText = `
20838
+ position: absolute !important;
20839
+ left: -10000px !important;
20840
+ width: 1px !important;
20841
+ height: 1px !important;
20842
+ overflow: hidden !important;
20843
+ `;
20844
+ document.body.appendChild(this.assertiveRegion);
20845
+ }
20846
+ announce(message, level = 'polite') {
20847
+ if (level === 'assertive') {
20848
+ this.announceAssertive(message);
20849
+ } else {
20850
+ this.announcePolite(message);
20851
+ }
20852
+ }
20853
+ announcePolite(message) {
20854
+ if (!this.politeRegion) return;
20855
+ this.politeRegion.textContent = '';
20856
+ // Small delay ensures screen readers detect the change
20857
+ setTimeout(() => {
20858
+ if (this.politeRegion) {
20859
+ this.politeRegion.textContent = message;
20860
+ }
20861
+ }, 100);
20862
+ }
20863
+ announceAssertive(message) {
20864
+ if (!this.assertiveRegion) return;
20865
+ this.assertiveRegion.textContent = '';
20866
+ // Small delay ensures screen readers detect the change
20867
+ setTimeout(() => {
20868
+ if (this.assertiveRegion) {
20869
+ this.assertiveRegion.textContent = message;
20870
+ }
20871
+ }, 100);
20872
+ }
20873
+ }
20874
+ const ariaLiveManager = new AriaLiveManager();
20875
+ /**
20876
+ * Accessibility Provider Component
20877
+ * Provides comprehensive accessibility context and utilities
20878
+ *
20879
+ * Features:
20880
+ * - System preference detection (high contrast, reduced motion)
20881
+ * - Keyboard navigation detection
20882
+ * - ARIA live announcements
20883
+ * - Focus management
20884
+ * - Accessibility auditing
20885
+ * - Settings persistence
20886
+ */
20887
+ const AccessibilityProvider = ({
20888
+ children,
20889
+ enableAudit = "development" === 'development'
20890
+ }) => {
20891
+ const [state, dispatch] = useReducer(accessibilityReducer, initialState);
20892
+ useEffect(() => {
20893
+ // Detect user preferences from system
20894
+ detectUserPreferences();
20895
+ // Set up keyboard detection
20896
+ const keyboardCleanup = setupKeyboardDetection();
20897
+ // Initialize focus management
20898
+ initializeFocusManagement();
20899
+ // Run initial accessibility audit
20900
+ if (enableAudit) {
20901
+ runAccessibilityAudit();
20902
+ }
20903
+ // Cleanup
20904
+ return () => {
20905
+ if (keyboardCleanup) keyboardCleanup();
20906
+ };
20907
+ }, [enableAudit]);
20908
+ const detectUserPreferences = () => {
20909
+ if (typeof window === 'undefined') return;
20910
+ // High contrast mode
20911
+ if (window.matchMedia && window.matchMedia('(prefers-contrast: high)').matches) {
20912
+ dispatch({
20913
+ type: 'SET_HIGH_CONTRAST',
20914
+ payload: true
20915
+ });
20916
+ }
20917
+ // Reduced motion
20918
+ if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
20919
+ dispatch({
20920
+ type: 'SET_REDUCED_MOTION',
20921
+ payload: true
20922
+ });
20923
+ }
20924
+ // Listen for changes
20925
+ if (window.matchMedia) {
20926
+ const contrastMedia = window.matchMedia('(prefers-contrast: high)');
20927
+ const motionMedia = window.matchMedia('(prefers-reduced-motion: reduce)');
20928
+ const contrastHandler = e => {
20929
+ dispatch({
20930
+ type: 'SET_HIGH_CONTRAST',
20931
+ payload: e.matches
20932
+ });
20933
+ };
20934
+ const motionHandler = e => {
20935
+ dispatch({
20936
+ type: 'SET_REDUCED_MOTION',
20937
+ payload: e.matches
20938
+ });
20939
+ };
20940
+ contrastMedia.addEventListener('change', contrastHandler);
20941
+ motionMedia.addEventListener('change', motionHandler);
20942
+ // Return cleanup function
20943
+ return () => {
20944
+ contrastMedia.removeEventListener('change', contrastHandler);
20945
+ motionMedia.removeEventListener('change', motionHandler);
20946
+ };
20947
+ }
20948
+ };
20949
+ const setupKeyboardDetection = () => {
20950
+ if (typeof document === 'undefined') return;
20951
+ let keyboardUser = false;
20952
+ const handleKeyDown = e => {
20953
+ if (e.key === 'Tab') {
20954
+ keyboardUser = true;
20955
+ dispatch({
20956
+ type: 'SET_KEYBOARD_USER',
20957
+ payload: true
20958
+ });
20959
+ document.body.classList.add('keyboard-user');
20960
+ }
20961
+ };
20962
+ const handleMouseDown = () => {
20963
+ if (keyboardUser) {
20964
+ keyboardUser = false;
20965
+ dispatch({
20966
+ type: 'SET_KEYBOARD_USER',
20967
+ payload: false
20968
+ });
20969
+ document.body.classList.remove('keyboard-user');
20970
+ }
20971
+ };
20972
+ document.addEventListener('keydown', handleKeyDown);
20973
+ document.addEventListener('mousedown', handleMouseDown);
20974
+ return () => {
20975
+ document.removeEventListener('keydown', handleKeyDown);
20976
+ document.removeEventListener('mousedown', handleMouseDown);
20977
+ };
20978
+ };
20979
+ const initializeFocusManagement = () => {
20980
+ if (typeof document === 'undefined') return;
20981
+ // Enhanced focus indicators for keyboard users
20982
+ const style = document.createElement('style');
20983
+ style.textContent = `
20984
+ .keyboard-user *:focus {
20985
+ outline: 3px solid #005cee !important;
20986
+ outline-offset: 2px !important;
20987
+ }
20988
+
20989
+ .high-contrast *:focus {
20990
+ outline: 3px solid #ffffff !important;
20991
+ outline-offset: 2px !important;
20992
+ box-shadow: 0 0 0 1px #000000 !important;
20993
+ }
20994
+
20995
+ .reduced-motion * {
20996
+ animation-duration: 0.01ms !important;
20997
+ animation-iteration-count: 1 !important;
20998
+ transition-duration: 0.01ms !important;
20999
+ }
21000
+
21001
+ .large-text {
21002
+ font-size: 1.2em !important;
21003
+ }
21004
+ `;
21005
+ document.head.appendChild(style);
21006
+ };
21007
+ const runAccessibilityAudit = () => {
21008
+ if (typeof document === 'undefined') return;
21009
+ setTimeout(() => {
21010
+ const issues = [];
21011
+ // Check for images without alt text
21012
+ const images = document.querySelectorAll('img:not([alt])');
21013
+ images.forEach(img => {
21014
+ issues.push({
21015
+ type: 'missing-alt-text',
21016
+ message: 'Image missing alt attribute',
21017
+ level: 'error',
21018
+ element: img
21019
+ });
21020
+ });
21021
+ // Check for buttons without accessible names
21022
+ const buttons = document.querySelectorAll('button:not([aria-label]):not([title])');
21023
+ buttons.forEach(button => {
21024
+ if (!button.textContent?.trim()) {
21025
+ issues.push({
21026
+ type: 'unnamed-button',
21027
+ message: 'Button missing accessible name',
21028
+ level: 'error',
21029
+ element: button
21030
+ });
21031
+ }
21032
+ });
21033
+ // Check for form inputs without labels
21034
+ const inputs = document.querySelectorAll('input:not([aria-label]):not([title])');
21035
+ inputs.forEach(input => {
21036
+ const id = input.getAttribute('id');
21037
+ if (id) {
21038
+ const label = document.querySelector(`label[for="${id}"]`);
21039
+ if (!label) {
21040
+ issues.push({
21041
+ type: 'unlabeled-input',
21042
+ message: 'Form input missing label',
21043
+ level: 'error',
21044
+ element: input
21045
+ });
21046
+ }
21047
+ } else {
21048
+ issues.push({
21049
+ type: 'unlabeled-input',
21050
+ message: 'Form input missing label',
21051
+ level: 'error',
21052
+ element: input
21053
+ });
21054
+ }
21055
+ });
21056
+ dispatch({
21057
+ type: 'CLEAR_ISSUES'
21058
+ });
21059
+ issues.forEach(issue => {
21060
+ dispatch({
21061
+ type: 'ADD_ISSUE',
21062
+ payload: issue
21063
+ });
21064
+ });
21065
+ if (issues.length > 0) {
21066
+ console.group('🔍 Accessibility Issues Found');
21067
+ issues.forEach(issue => {
21068
+ const logMethod = issue.level === 'error' ? console.error : console.warn;
21069
+ logMethod(`${issue.type}: ${issue.message}`);
21070
+ });
21071
+ console.groupEnd();
21072
+ }
21073
+ }, 1000);
21074
+ };
21075
+ // Context value
21076
+ const contextValue = {
21077
+ ...state,
21078
+ // Actions
21079
+ setHighContrast: enabled => dispatch({
21080
+ type: 'SET_HIGH_CONTRAST',
21081
+ payload: enabled
21082
+ }),
21083
+ setReducedMotion: enabled => dispatch({
21084
+ type: 'SET_REDUCED_MOTION',
21085
+ payload: enabled
21086
+ }),
21087
+ setLargeText: enabled => dispatch({
21088
+ type: 'SET_LARGE_TEXT',
21089
+ payload: enabled
21090
+ }),
21091
+ setFocusVisible: enabled => dispatch({
21092
+ type: 'SET_FOCUS_VISIBLE',
21093
+ payload: enabled
21094
+ }),
21095
+ // Utilities
21096
+ announce: (message, level = 'polite') => {
21097
+ ariaLiveManager.announce(message, level);
21098
+ dispatch({
21099
+ type: 'SET_ANNOUNCEMENT',
21100
+ payload: {
21101
+ message,
21102
+ level,
21103
+ timestamp: Date.now()
21104
+ }
21105
+ });
21106
+ },
21107
+ announcePolite: message => {
21108
+ ariaLiveManager.announcePolite(message);
21109
+ dispatch({
21110
+ type: 'SET_ANNOUNCEMENT',
21111
+ payload: {
21112
+ message,
21113
+ level: 'polite',
21114
+ timestamp: Date.now()
21115
+ }
21116
+ });
21117
+ },
21118
+ announceAssertive: message => {
21119
+ ariaLiveManager.announceAssertive(message);
21120
+ dispatch({
21121
+ type: 'SET_ANNOUNCEMENT',
21122
+ payload: {
21123
+ message,
21124
+ level: 'assertive',
21125
+ timestamp: Date.now()
21126
+ }
21127
+ });
21128
+ },
21129
+ addIssue: issue => dispatch({
21130
+ type: 'ADD_ISSUE',
21131
+ payload: issue
21132
+ }),
21133
+ clearIssues: () => dispatch({
21134
+ type: 'CLEAR_ISSUES'
21135
+ }),
21136
+ // Audit function
21137
+ runAudit: runAccessibilityAudit
21138
+ };
21139
+ // Apply CSS classes based on preferences
21140
+ useEffect(() => {
21141
+ if (typeof document === 'undefined') return;
21142
+ const {
21143
+ highContrast,
21144
+ reducedMotion,
21145
+ largeText
21146
+ } = state;
21147
+ document.body.classList.toggle('high-contrast', highContrast);
21148
+ document.body.classList.toggle('reduced-motion', reducedMotion);
21149
+ document.body.classList.toggle('large-text', largeText);
21150
+ }, [state.highContrast, state.reducedMotion, state.largeText]);
21151
+ return jsx(AccessibilityContext.Provider, {
21152
+ value: contextValue,
21153
+ children: children
21154
+ });
21155
+ };
21156
+ /**
21157
+ * Hook to access accessibility context
21158
+ */
21159
+ const useAccessibility = () => {
21160
+ const context = useContext(AccessibilityContext);
21161
+ if (!context) {
21162
+ throw new Error('useAccessibility must be used within an AccessibilityProvider');
21163
+ }
21164
+ return context;
21165
+ };
21166
+ /**
21167
+ * Higher-Order Component for accessibility enhancements
21168
+ */
21169
+ const withAccessibility = WrappedComponent => {
21170
+ const AccessibilityEnhancedComponent = props => {
21171
+ const accessibility = useAccessibility();
21172
+ return jsx(WrappedComponent, {
21173
+ ...props,
21174
+ accessibility: accessibility
21175
+ });
21176
+ };
21177
+ AccessibilityEnhancedComponent.displayName = `withAccessibility(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;
21178
+ return AccessibilityEnhancedComponent;
21179
+ };
21180
+
20582
21181
  const QwickApp = ({
20583
21182
  children,
20584
21183
  className,
@@ -20640,16 +21239,20 @@ const QwickApp = ({
20640
21239
  dataSource: dataSource,
20641
21240
  children: content
20642
21241
  }) : content;
20643
- const appContent = jsx("div", {
20644
- className: `qwick-app ${className || ''}`,
20645
- style: style,
20646
- children: jsx(ThemeProvider, {
20647
- appId: appId,
20648
- defaultTheme: defaultTheme,
20649
- defaultPalette: defaultPalette,
20650
- children: jsx(QwickAppContext.Provider, {
20651
- value: contextValue,
20652
- children: wrappedContent
21242
+ const appContent = jsx(ErrorBoundary, {
21243
+ children: jsx(AccessibilityProvider, {
21244
+ children: jsx("div", {
21245
+ className: `qwick-app ${className || ''}`,
21246
+ style: style,
21247
+ children: jsx(ThemeProvider, {
21248
+ appId: appId,
21249
+ defaultTheme: defaultTheme,
21250
+ defaultPalette: defaultPalette,
21251
+ children: jsx(QwickAppContext.Provider, {
21252
+ value: contextValue,
21253
+ children: wrappedContent
21254
+ })
21255
+ })
20653
21256
  })
20654
21257
  })
20655
21258
  });
@@ -20757,6 +21360,186 @@ const setCSSVariable = (property, value) => {
20757
21360
  document.documentElement.style.setProperty(property, value);
20758
21361
  };
20759
21362
 
21363
+ /**
21364
+ * Generic Breadcrumbs component for navigation hierarchy
21365
+ *
21366
+ * Features:
21367
+ * - Accessible navigation with proper ARIA labels
21368
+ * - Customizable separators and icons
21369
+ * - Responsive design with item truncation
21370
+ * - Support for custom navigation handlers
21371
+ * - Keyboard navigation support
21372
+ * - Screen reader friendly
21373
+ */
21374
+ const Breadcrumbs = ({
21375
+ items,
21376
+ separator = '/',
21377
+ className = '',
21378
+ onNavigate,
21379
+ maxItems,
21380
+ showRoot = true
21381
+ }) => {
21382
+ // Filter and prepare items
21383
+ let displayItems = showRoot ? items : items.slice(1);
21384
+ // Handle max items with ellipsis
21385
+ if (maxItems && displayItems.length > maxItems) {
21386
+ const firstItems = displayItems.slice(0, 1);
21387
+ const lastItems = displayItems.slice(-Math.max(1, maxItems - 2));
21388
+ displayItems = [...firstItems, {
21389
+ label: '...',
21390
+ href: undefined,
21391
+ current: false
21392
+ }, ...lastItems];
21393
+ }
21394
+ const handleItemClick = (e, item, index) => {
21395
+ if (onNavigate) {
21396
+ e.preventDefault();
21397
+ onNavigate(item, index);
21398
+ }
21399
+ };
21400
+ const handleKeyDown = (e, item, index) => {
21401
+ if (e.key === 'Enter' || e.key === ' ') {
21402
+ e.preventDefault();
21403
+ if (onNavigate) {
21404
+ onNavigate(item, index);
21405
+ } else if (item.href) {
21406
+ window.location.href = item.href;
21407
+ }
21408
+ }
21409
+ };
21410
+ if (displayItems.length <= 1) {
21411
+ return null;
21412
+ }
21413
+ return jsx("nav", {
21414
+ className: `breadcrumbs ${className}`,
21415
+ role: "navigation",
21416
+ "aria-label": "Breadcrumb navigation",
21417
+ style: {
21418
+ display: 'flex',
21419
+ alignItems: 'center',
21420
+ fontSize: '14px',
21421
+ color: '#6b7280',
21422
+ ...defaultStyles.nav
21423
+ },
21424
+ children: jsx("ol", {
21425
+ style: {
21426
+ display: 'flex',
21427
+ alignItems: 'center',
21428
+ listStyle: 'none',
21429
+ margin: 0,
21430
+ padding: 0,
21431
+ gap: '8px'
21432
+ },
21433
+ children: displayItems.map((item, index) => {
21434
+ const isLast = index === displayItems.length - 1;
21435
+ const isClickable = (item.href || onNavigate) && !item.current && item.label !== '...';
21436
+ return jsxs("li", {
21437
+ style: {
21438
+ display: 'flex',
21439
+ alignItems: 'center'
21440
+ },
21441
+ children: [isClickable ? jsxs("a", {
21442
+ href: item.href,
21443
+ onClick: e => handleItemClick(e, item, index),
21444
+ onKeyDown: e => handleKeyDown(e, item, index),
21445
+ style: {
21446
+ ...defaultStyles.link,
21447
+ color: isLast ? '#374151' : '#6b7280',
21448
+ textDecoration: 'none',
21449
+ display: 'flex',
21450
+ alignItems: 'center',
21451
+ gap: '4px'
21452
+ },
21453
+ tabIndex: 0,
21454
+ "aria-current": item.current ? 'page' : undefined,
21455
+ children: [item.icon && jsx("span", {
21456
+ style: {
21457
+ display: 'flex',
21458
+ alignItems: 'center'
21459
+ },
21460
+ "aria-hidden": "true",
21461
+ children: item.icon
21462
+ }), jsx("span", {
21463
+ children: item.label
21464
+ })]
21465
+ }) : jsxs("span", {
21466
+ style: {
21467
+ ...defaultStyles.current,
21468
+ color: isLast ? '#111827' : '#6b7280',
21469
+ fontWeight: isLast ? 600 : 400,
21470
+ display: 'flex',
21471
+ alignItems: 'center',
21472
+ gap: '4px'
21473
+ },
21474
+ "aria-current": item.current ? 'page' : undefined,
21475
+ children: [item.icon && jsx("span", {
21476
+ style: {
21477
+ display: 'flex',
21478
+ alignItems: 'center'
21479
+ },
21480
+ "aria-hidden": "true",
21481
+ children: item.icon
21482
+ }), jsx("span", {
21483
+ children: item.label
21484
+ })]
21485
+ }), !isLast && jsx("span", {
21486
+ style: {
21487
+ display: 'flex',
21488
+ alignItems: 'center',
21489
+ marginLeft: '8px',
21490
+ color: '#d1d5db',
21491
+ fontSize: '12px'
21492
+ },
21493
+ "aria-hidden": "true",
21494
+ children: separator
21495
+ })]
21496
+ }, `${item.label}-${index}`);
21497
+ })
21498
+ })
21499
+ });
21500
+ };
21501
+ // Default styles
21502
+ const defaultStyles = {
21503
+ nav: {
21504
+ padding: '8px 0'
21505
+ },
21506
+ link: {
21507
+ transition: 'color 0.2s ease',
21508
+ cursor: 'pointer',
21509
+ borderRadius: '4px',
21510
+ padding: '4px',
21511
+ margin: '-4px'
21512
+ },
21513
+ current: {
21514
+ padding: '4px'
21515
+ }
21516
+ };
21517
+ /**
21518
+ * Hook for managing breadcrumb state
21519
+ */
21520
+ const useBreadcrumbs = () => {
21521
+ const [breadcrumbs, setBreadcrumbs] = React.useState([]);
21522
+ const addBreadcrumb = React.useCallback(item => {
21523
+ setBreadcrumbs(prev => [...prev, item]);
21524
+ }, []);
21525
+ const removeBreadcrumb = React.useCallback(index => {
21526
+ setBreadcrumbs(prev => prev.filter((_, i) => i !== index));
21527
+ }, []);
21528
+ const setBreadcrumbsCurrent = React.useCallback(items => {
21529
+ setBreadcrumbs(items);
21530
+ }, []);
21531
+ const clearBreadcrumbs = React.useCallback(() => {
21532
+ setBreadcrumbs([]);
21533
+ }, []);
21534
+ return {
21535
+ breadcrumbs,
21536
+ addBreadcrumb,
21537
+ removeBreadcrumb,
21538
+ setBreadcrumbs: setBreadcrumbsCurrent,
21539
+ clearBreadcrumbs
21540
+ };
21541
+ };
21542
+
20760
21543
  const QwickAppsLogo = props => {
20761
21544
  const {
20762
21545
  styleProps,
@@ -24223,4 +25006,4 @@ var ActionType;
24223
25006
  ActionType["CANCEL"] = "cancel";
24224
25007
  })(ActionType || (ActionType = {}));
24225
25008
 
24226
- export { AVAILABLE_PALETTES, ActionModel, ActionType, AllPalettes, Article, ArticleModel, BasePage, Button, CardListGrid, CardListGridModel, ChoiceInputField, ChoiceInputFieldModel, Code, CodeModel, Content, ContentModel, CoverImageHeader, CoverImageHeaderModel, DataProvider, DataProxy, DimensionsProvider, FeatureCard, FeatureCardActionModel, FeatureCardModel, FeatureGrid, FeatureGridModel, FeatureItemModel, Footer, FooterItemModel, FooterModel, FooterSectionModel, FormBlock, FormBlockModel, FormMethod, FormPage, GridCell, GridLayout, HeaderActionModel, HeroBlock, HeroBlockModel, Html, HtmlInputField, Logo, Markdown, MetadataItemModel, Page, PageBannerHeader, PageBannerHeaderModel, PaletteAutumn, PaletteCosmic, PaletteDefault, PaletteOcean, PaletteProvider, PaletteSpring, PaletteSwitcher, PaletteSwitcherModel, PaletteWinter, ProductCard, ProductCardActionModel, ProductCardModel, ProductModel, QWICKAPP_COMPONENT, QwickApp, QwickAppsLogo, SafeSpan, SafeSpanModel, Section, SectionModel, SelectInputField, T, TextField, TextInputField, TextInputFieldModel, ThemeProvider, ThemeSwitcher, ThemeSwitcherModel, applyCustomPalette, clearUserPalettePreference, clearUserThemePreference, createLogger, createPaletteFromCurrentTheme, deleteCustomPalette, exportPalette, getCSSVariable, getComputedTheme, getCurrentPalette, getCurrentTheme, getCustomPalettes, getPaletteConfig, getPaletteName, getSystemTheme, getThemePerformanceStats, importPalette, initializePalette, initializeTheme, loadUserPalettePreference, loadUserThemePreference, logThemePerformanceStats, loggers, resetThemePerformanceStats, resolveDimension, resolveDimensions, resolveSpacing, resolveSpacingProps, saveCustomPalette, savePalettePreference, saveThemePreference, saveUserPalettePreference, saveUserThemePreference, setCSSVariable, setPalette, setTheme, t, useBaseProps, useData, useDataBinding, useDataContext, useDataProvider, useDimensions, usePalette, useQwickApp, useResolveTemplate, useSafeLocation, useSafeNavigate, useTemplate, useTheme };
25009
+ export { AVAILABLE_PALETTES, AccessibilityProvider, ActionModel, ActionType, AllPalettes, Article, ArticleModel, BasePage, Breadcrumbs, Button, CardListGrid, CardListGridModel, ChoiceInputField, ChoiceInputFieldModel, Code, CodeModel, Content, ContentModel, CoverImageHeader, CoverImageHeaderModel, DataProvider, DataProxy, DimensionsProvider, ErrorBoundary, FeatureCard, FeatureCardActionModel, FeatureCardModel, FeatureGrid, FeatureGridModel, FeatureItemModel, Footer, FooterItemModel, FooterModel, FooterSectionModel, FormBlock, FormBlockModel, FormMethod, FormPage, GridCell, GridLayout, HeaderActionModel, HeroBlock, HeroBlockModel, Html, HtmlInputField, Logo, Markdown, MetadataItemModel, Page, PageBannerHeader, PageBannerHeaderModel, PaletteAutumn, PaletteCosmic, PaletteDefault, PaletteOcean, PaletteProvider, PaletteSpring, PaletteSwitcher, PaletteSwitcherModel, PaletteWinter, ProductCard, ProductCardActionModel, ProductCardModel, ProductModel, QWICKAPP_COMPONENT, QwickApp, QwickAppsLogo, SafeSpan, SafeSpanModel, Section, SectionModel, SelectInputField, T, TextField, TextInputField, TextInputFieldModel, ThemeProvider, ThemeSwitcher, ThemeSwitcherModel, applyCustomPalette, clearUserPalettePreference, clearUserThemePreference, createLogger, createPaletteFromCurrentTheme, deleteCustomPalette, exportPalette, getCSSVariable, getComputedTheme, getCurrentPalette, getCurrentTheme, getCustomPalettes, getPaletteConfig, getPaletteName, getSystemTheme, getThemePerformanceStats, importPalette, initializePalette, initializeTheme, loadUserPalettePreference, loadUserThemePreference, logThemePerformanceStats, loggers, resetThemePerformanceStats, resolveDimension, resolveDimensions, resolveSpacing, resolveSpacingProps, saveCustomPalette, savePalettePreference, saveThemePreference, saveUserPalettePreference, saveUserThemePreference, setCSSVariable, setPalette, setTheme, t, useAccessibility, useBaseProps, useBreadcrumbs, useData, useDataBinding, useDataContext, useDataProvider, useDimensions, usePalette, useQwickApp, useResolveTemplate, useSafeLocation, useSafeNavigate, useTemplate, useTheme, withAccessibility, withErrorBoundary };